import logging
import logging.handlers as handlers
import traceback
from typing import Any

from flask import Flask, g, has_request_context
from .formatter import JsonFormatter, ContextLoggerAdapter


class FlaskLogger:
    """Structured JSON logger with PII redaction, designed for Flask apps."""

    _logger = None
    _formatter = JsonFormatter("%(timestamp)s %(level)s %(name)s %(message)s")

    def init(
        self,
        app: Flask,
        level: int = logging.INFO,
        log_file: str | None = None,       # no default path assumed
        backup_count: int = 15,
    ):
        self._logger = app.logger
        self._logger.handlers[:] = []
        logging.getLogger("werkzeug").handlers[:] = []

        stream_handler = logging.StreamHandler()
        stream_handler.setFormatter(self._formatter)
        stream_handler.setLevel(level)
        self._logger.addHandler(stream_handler)

        if log_file:
            file_handler = handlers.TimedRotatingFileHandler(
                log_file, when="midnight", backupCount=backup_count
            )
            file_handler.setFormatter(self._formatter)
            file_handler.setLevel(level)
            self._logger.addHandler(file_handler)

        logging.getLogger().setLevel(level)

    def info(self, msg: str, event: str | None = None, **extra):
        self._log(logging.INFO, msg, event=event, **extra)

    def warning(self, msg: str, event: str | None = None, **extra):
        self._log(logging.WARNING, msg, event=event, **extra)

    def error(self, msg: str, event: str | None = None, **extra):
        self._log(logging.ERROR, msg, event=event, **extra)

    def debug(self, msg: str, event: str | None = None, **extra):
        self._log(logging.DEBUG, msg, event=event, **extra)

    def exception(self, exc: Exception, message: str = "Unhandled exception", event: str | None = None, **extra):
        self._log(
            logging.ERROR,
            message,
            event=event,
            error={
                "type": exc.__class__.__name__,
                "message": str(exc),
                "stack": traceback.format_exc().splitlines(),
            },
            **extra,
        )

    @staticmethod
    def put_context(key: str, value: Any):
        """Attach a key/value to the current request's log context."""
        ctx = g.get("logger_context", {})
        ctx[key] = value
        g.logger_context = ctx

    def _log(self, level: int, message: str, **extra):
        adapter = logging.LoggerAdapter(self._logger, self._context_with(extra))
        adapter.log(level, message)

    def _context_with(self, extra: dict[str, Any]) -> dict[str, Any]:
        ctx = {}
        if has_request_context():
            ctx.update(g.get("logger_context", {}))
        ctx.update(extra)
        return ctx


logger = FlaskLogger()