from __future__ import annotations

import json
import os
import re
import traceback
from datetime import datetime
from typing import Any

from app.services.log_config import LOG_DATE_FORMAT, LOG_DIR, is_enabled

MAX_LOG_FIELD_LENGTH = 20000
MAX_TRACEBACK_LENGTH = 12000
MODULE_LOG_ROOT = os.path.join(os.path.dirname(LOG_DIR), "ModuleGeneration")


def _safe_module_name(module: str | None) -> str:
    value = str(module or "unknown_module").strip() or "unknown_module"
    value = re.sub(r"[^a-zA-Z0-9_.-]+", "_", value)
    return value[:120] or "unknown_module"


def _safe_dump_value(value: Any) -> Any:
    """限制单字段长度，避免超长 base64 / 响应体把日志打爆。"""
    if value is None:
        return None
    if isinstance(value, str):
        if len(value) > MAX_LOG_FIELD_LENGTH:
            return value[:MAX_LOG_FIELD_LENGTH] + f"...<truncated:{len(value) - MAX_LOG_FIELD_LENGTH}>"
        return value
    if isinstance(value, dict):
        return {str(k): _safe_dump_value(v) for k, v in value.items()}
    if isinstance(value, list):
        return [_safe_dump_value(v) for v in value]
    return value


def build_exception_detail(exc: BaseException | None, extra: dict[str, Any] | None = None) -> dict[str, Any]:
    """构造统一异常日志 detail。日志方法必须吞异常，业务不能被日志影响。"""
    detail: dict[str, Any] = dict(extra or {})
    if exc is not None:
        tb = "".join(traceback.format_exception(type(exc), exc, exc.__traceback__))
        if len(tb) > MAX_TRACEBACK_LENGTH:
            tb = tb[:MAX_TRACEBACK_LENGTH] + f"...<traceback_truncated:{len(tb) - MAX_TRACEBACK_LENGTH}>"
        detail.update(
            {
                "exception_type": type(exc).__name__,
                "exception_message": str(exc),
                "traceback": tb,
            }
        )
    return detail


def _append_module_log(module: str, entry: dict[str, Any]) -> None:
    if not is_enabled():
        return
    try:
        module_dir = os.path.join(MODULE_LOG_ROOT, _safe_module_name(module))
        os.makedirs(module_dir, exist_ok=True)
        today = datetime.now().strftime(LOG_DATE_FORMAT)
        log_file = os.path.join(module_dir, f"{today}.log")
        with open(log_file, "a", encoding="utf-8") as f:
            f.write(json.dumps(entry, ensure_ascii=False, default=str) + "\n")
    except Exception:
        # 日志失败绝不能影响业务主流程。
        pass


def log_module_event_file(
    *,
    module: str,
    event_type: str,
    project_id: str | None = None,
    step_id: str | None = None,
    user_id: str | None = None,
    message: str | None = None,
    detail: dict[str, Any] | None = None,
    error: str | None = None,
) -> None:
    """记录模块流程事件到 JSONL 文件。

    统一落盘目录：log/ModuleGeneration/{module}/YYYY-MM-DD.log
    不再写 module_generation_events 表。
    """
    entry = {
        "timestamp": datetime.now().strftime("%Y-%m-%d %H:%M:%S"),
        "log_type": "module_event",
        "module": module,
        "event_type": event_type,
        "project_id": project_id,
        "step_id": step_id,
        "user_id": user_id,
        "message": message,
        "detail": _safe_dump_value(detail or {}),
        "error": error,
    }
    _append_module_log(module, entry)


def log_module_prompt_event(
    *,
    event_type: str,
    project_id: str,
    step_id: str,
    user_id: str,
    module: str,
    prompt_type: str,
    request: dict[str, Any] | None = None,
    response: dict[str, Any] | None = None,
    token_usage: dict[str, Any] | None = None,
    error: str | None = None,
) -> None:
    """记录模块 AI 提词/分析请求和响应到 JSONL 文件。"""
    entry = {
        "timestamp": datetime.now().strftime("%Y-%m-%d %H:%M:%S"),
        "log_type": "module_prompt",
        "module": module,
        "event_type": event_type,
        "prompt_type": prompt_type,
        "project_id": project_id,
        "step_id": step_id,
        "user_id": user_id,
        "request": _safe_dump_value(request or {}),
        "response": _safe_dump_value(response or {}),
        "token_usage": _safe_dump_value(token_usage or {}),
        "error": error,
    }
    _append_module_log(module, entry)


def log_module_error(
    *,
    module: str,
    event_type: str,
    project_id: str | None = None,
    step_id: str | None = None,
    user_id: str | None = None,
    message: str | None = None,
    detail: dict[str, Any] | None = None,
    error: str | None = None,
    exc: BaseException | None = None,
) -> None:
    """记录模块异常日志。

    - 兼容原有 detail/error 参数。
    - 新增 exc 后自动记录 exception_type、message、traceback。
    - 日志写入失败会被底层吞掉，不影响主流程。
    """
    merged_detail = build_exception_detail(exc, detail)
    entry = {
        "timestamp": datetime.now().strftime("%Y-%m-%d %H:%M:%S"),
        "log_type": "module_error",
        "module": module,
        "event_type": event_type,
        "project_id": project_id,
        "step_id": step_id,
        "user_id": user_id,
        "message": message,
        "detail": _safe_dump_value(merged_detail),
        "error": error if error is not None else (str(exc) if exc is not None else None),
    }
    _append_module_log(module, entry)
