"""蓝图生成过程日志：控制台 + 落盘，便于对账。

环境变量：
- GENERATE_LOG_DIR：日志目录（默认仓库 .runtime/logs/generate）
- GENERATE_LOG_LEVEL：DEBUG/INFO/WARNING（默认 INFO）
"""
from __future__ import annotations

import logging
import os
import threading
import uuid
from contextvars import ContextVar
from datetime import datetime, timezone
from pathlib import Path
from typing import Any

_root = Path(__file__).resolve().parents[1]
_LOGGER_NAME = "aijianzhan.generate"
_setup_lock = threading.Lock()
_configured = False

_current: ContextVar["GenerateTrace | None"] = ContextVar("generate_trace", default=None)


def _log_dir() -> Path:
    raw = (os.getenv("GENERATE_LOG_DIR") or "").strip()
    if raw:
        return Path(raw).expanduser().resolve()
    return (_root / ".runtime" / "logs" / "generate").resolve()


def _level() -> int:
    name = (os.getenv("GENERATE_LOG_LEVEL") or "INFO").strip().upper()
    return getattr(logging, name, logging.INFO)


def ensure_logging() -> logging.Logger:
    """幂等配置：stdout + 按日滚动的 generate.log。"""
    global _configured
    logger = logging.getLogger(_LOGGER_NAME)
    if _configured:
        return logger
    with _setup_lock:
        if _configured:
            return logger
        logger.setLevel(_level())
        logger.propagate = False
        fmt = logging.Formatter(
            "%(asctime)s [%(levelname)s] %(message)s",
            datefmt="%Y-%m-%d %H:%M:%S",
        )
        sh = logging.StreamHandler()
        sh.setLevel(_level())
        sh.setFormatter(fmt)
        logger.addHandler(sh)
        try:
            d = _log_dir()
            d.mkdir(parents=True, exist_ok=True)
            fh = logging.FileHandler(d / "generate.log", encoding="utf-8")
            fh.setLevel(_level())
            fh.setFormatter(fmt)
            logger.addHandler(fh)
        except OSError as e:
            logger.warning("无法写入生成日志目录: %s", e)
        _configured = True
    return logger


class GenerateTrace:
    """单次 /apps/generate 请求的对账轨迹。"""

    def __init__(self, run_id: str | None = None) -> None:
        self.run_id = run_id or uuid.uuid4().hex[:10]
        self.started_at = datetime.now(timezone.utc).isoformat()
        self.lines: list[dict[str, Any]] = []
        self._token = None
        self._logger = ensure_logging()
        self._run_file: Path | None = None
        try:
            d = _log_dir()
            d.mkdir(parents=True, exist_ok=True)
            self._run_file = d / f"run_{self.run_id}.log"
        except OSError:
            self._run_file = None

    def __enter__(self) -> "GenerateTrace":
        self._token = _current.set(self)
        self.info("generate.start", run_id=self.run_id)
        return self

    def __exit__(self, exc_type, exc, tb) -> None:
        if exc_type is not None:
            self.error(f"generate.failed: {exc}", error=str(exc))
        else:
            self.info("generate.done")
        if self._token is not None:
            _current.reset(self._token)
        return False

    def _emit(self, level: str, message: str, **fields: Any) -> None:
        ts = datetime.now(timezone.utc).isoformat()
        entry = {"ts": ts, "level": level, "message": message}
        if fields:
            # 仅保留可 JSON 序列化的简单字段
            clean: dict[str, Any] = {}
            for k, v in fields.items():
                if v is None:
                    continue
                if isinstance(v, (str, int, float, bool)):
                    clean[k] = v
                elif isinstance(v, (list, tuple)) and all(
                    isinstance(x, (str, int, float, bool)) for x in v
                ):
                    clean[k] = list(v)
                else:
                    clean[k] = str(v)[:500]
            entry.update(clean)
        self.lines.append(entry)

        extra = " ".join(f"{k}={v}" for k, v in entry.items() if k not in {"ts", "level", "message"})
        line = f"[{self.run_id}] {message}" + (f" | {extra}" if extra else "")
        log_fn = getattr(self._logger, level if level in {"debug", "info", "warning", "error"} else "info")
        log_fn(line)
        if self._run_file is not None:
            try:
                with self._run_file.open("a", encoding="utf-8") as f:
                    f.write(f"{ts} [{level.upper()}] {line}\n")
            except OSError:
                pass

    def debug(self, message: str, **fields: Any) -> None:
        self._emit("debug", message, **fields)

    def info(self, message: str, **fields: Any) -> None:
        self._emit("info", message, **fields)

    def warning(self, message: str, **fields: Any) -> None:
        self._emit("warning", message, **fields)

    def error(self, message: str, **fields: Any) -> None:
        self._emit("error", message, **fields)

    def stage(self, name: str, detail: str = "", **fields: Any) -> None:
        msg = f"stage.{name}" + (f": {detail}" if detail else "")
        self.info(msg, stage=name, **fields)

    def note(self, text: str) -> None:
        """把 warnings/notes 同步进日志。"""
        t = (text or "").strip()
        if t:
            self.info(t)

    def notes(self, items: list[str] | None) -> None:
        for n in items or []:
            self.note(str(n))

    def as_payload(self) -> dict[str, Any]:
        return {
            "run_id": self.run_id,
            "started_at": self.started_at,
            "log_file": str(self._run_file) if self._run_file else "",
            "lines": list(self.lines),
        }


def get_trace() -> GenerateTrace | None:
    return _current.get()


def glog() -> GenerateTrace | None:
    """当前请求轨迹；无上下文时返回 None（调用方需判空）。"""
    return get_trace()
