from __future__ import annotations

import logging
import os
import sys
from loguru import logger
from typing import Any, Dict

_CONFIGURED = False


def setup_logging(
    level: str | None = None,
    json: bool | None = None,
    log_file: str | None = None,
    rotation: str | None = None,
    retention: str | None = None,
) -> None:
    """Configure Loguru sinks and levels.

    Priority: function args > environment variables > defaults.
    Env vars: LOG_LEVEL, LOG_FORMAT=json|plain, LOG_FILE, LOG_ROTATION, LOG_RETENTION
    """
    global _CONFIGURED
    if _CONFIGURED:
        return

    # Remove default handler
    logger.remove()

    lvl = (level or os.getenv("LOG_LEVEL", "INFO")).upper()
    use_json = (json if json is not None else os.getenv("LOG_FORMAT", "").lower() == "json")

    if use_json:
        logger.add(sys.stdout, level=lvl, serialize=True, backtrace=False, diagnose=False)
    else:
        fmt = (
            "<green>{time:YYYY-MM-DD HH:mm:ss.SSS}</green> | "
            "<level>{level: <8}</level> | "
            "<cyan>{name}</cyan>:<cyan>{function}</cyan>:<cyan>{line}</cyan> - "
            "<level>{message}</level>"
        )
        logger.add(sys.stdout, level=lvl, format=fmt, backtrace=False, diagnose=False)

    file = log_file or os.getenv("LOG_FILE")
    if file:
        rot = rotation or os.getenv("LOG_ROTATION", "10 MB")
        ret = retention or os.getenv("LOG_RETENTION", "7 days")
        logger.add(file, level=lvl, rotation=rot, retention=ret, serialize=use_json)

    _CONFIGURED = True


class InterceptHandler(logging.Handler):
    def emit(self, record: logging.LogRecord) -> None:
        try:
            level = logger.level(record.levelname).name
        except Exception:
            level = record.levelno

        # Find caller depth outside of logging module
        frame, depth = logging.currentframe(), 2
        while frame and frame.f_code.co_filename == logging.__file__:
            frame = frame.f_back
            depth += 1

        logger.opt(depth=depth, exception=record.exc_info).log(level, record.getMessage())


def patch_standard_logging() -> None:
    """Redirect stdlib logging (incl. uvicorn/fastapi) to loguru."""
    logging.root.handlers = [InterceptHandler()]
    logging.root.setLevel(logging.getLevelName(os.getenv("LOG_LEVEL", "INFO").upper()))
    for name in ("uvicorn", "uvicorn.error", "uvicorn.access", "fastapi", "asyncio"):
        log = logging.getLogger(name)
        log.handlers = []
        log.propagate = True


__all__ = ["logger", "setup_logging", "patch_standard_logging"]

SENSITIVE_KEYS = {"password", "secret", "token", "access_token", "refresh_token", "client_secret"}

def _mask_value(v: Any) -> Any:
    if v is None:
        return v
    if isinstance(v, (int, float)):
        return v
    return "***"  # generic mask

def mask_sensitive(data: Dict[str, Any]) -> Dict[str, Any]:
    masked: Dict[str, Any] = {}
    for k, v in data.items():
        if k.lower() in SENSITIVE_KEYS:
            masked[k] = _mask_value(v)
        elif isinstance(v, dict):
            masked[k] = mask_sensitive(v)
        else:
            masked[k] = v
    return masked

def sensitive_debug_enabled() -> bool:
    # 需同时满足 LOG_LEVEL=DEBUG (或传入级别) 与 LOG_SENSITIVE_DEBUG=1
    lvl = os.getenv("LOG_LEVEL", "INFO").upper()
    flag = os.getenv("LOG_SENSITIVE_DEBUG", "0") in ("1", "true", "True")
    return flag and lvl == "DEBUG"

def log_step(level: str, message: str, **extra: Any) -> None:
    # 统一入口，避免散落调用；extra 放入 logger.bind
    if level == "error":
        logger.error(message, extra=extra)
    elif level == "warning":
        logger.warning(message, extra=extra)
    elif level == "debug":
        logger.debug(message, extra=extra)
    else:
        logger.info(message, extra=extra)

def log_login_step(index: int, name: str, method: str, url: str, status_code: int, duration_ms: int) -> None:
    level = "info" if status_code < 400 else ("warning" if status_code < 500 else "error")
    log_step(level, "login.step", step_index=index, step_name=name, method=method, url=url, status_code=status_code, duration_ms=duration_ms)

def log_login_final(keys: list[str], expires_at: str | None) -> None:
    log_step("info", "login.final", keys=keys, expires_at=expires_at)

def log_login_failure(error: str) -> None:
    log_step("error", "login.failure", error=error)

def log_sensitive_payload(payload: Dict[str, Any]) -> None:
    if sensitive_debug_enabled():
        logger.debug("login.payload", extra={"payload": payload})
    else:
        # 在非敏感调试模式下输出脱敏版本用于排障（仍需 DEBUG 等级）
        if logger.level("DEBUG").no >= logger.level("DEBUG").no:
            logger.debug("login.payload.masked", extra={"payload": mask_sensitive(payload)})

__all__ += [
    "mask_sensitive",
    "sensitive_debug_enabled",
    "log_login_step",
    "log_login_final",
    "log_login_failure",
    "log_sensitive_payload",
    "log_step",
]
