import json
import logging
import threading
from datetime import UTC, datetime
from typing import Any

from ..config import settings
from .context import get_trace_id, tool_name
from .settings import MCP_LOG_REDACT

logger = logging.getLogger(__name__)

MAX_ROTATED_LOGS = 5
_LOG_LOCK = threading.Lock()


def redact_value(value: str, max_len: int = 200) -> str:
    if not value:
        return value
    if not MCP_LOG_REDACT:
        return value
    if len(value) <= max_len:
        return value
    suffix = f"... [truncated, len={len(value)}]"
    if max_len <= len(suffix):
        return value[:max_len]
    return f"{value[: max_len - len(suffix)]}{suffix}"


def rotate_log_if_needed() -> None:
    try:
        if (
            settings.LOG_PATH.exists()
            and settings.LOG_PATH.stat().st_size > settings.MAX_LOG_SIZE_BYTES
        ):
            rotated_path = settings.LOG_PATH.with_suffix(
                f".{datetime.now(UTC).strftime('%Y%m%d_%H%M%S')}.log"
            )
            settings.LOG_PATH.rename(rotated_path)
            logger.debug("Rotated log file to %s", rotated_path)

            rotated_logs = sorted(settings.LOG_PATH.parent.glob("relace.*.log"), reverse=True)
            for old_log in rotated_logs[MAX_ROTATED_LOGS:]:
                old_log.unlink(missing_ok=True)
                logger.debug("Cleaned up old log file: %s", old_log)
    except Exception as exc:
        logger.warning("Failed to rotate log file: %s", exc)


def log_event(event: dict[str, Any]) -> None:
    """Write a single JSON event to local log file.

    Args:
        event: Event data to log. Will be enriched with timestamp, trace_id, tool, level.
    """
    if not settings.MCP_LOGGING:
        return

    event = dict(event)
    try:
        if "timestamp" not in event:
            event["timestamp"] = datetime.now(UTC).isoformat()
        if "trace_id" not in event:
            event["trace_id"] = get_trace_id()
        if "tool" not in event:
            current_tool = tool_name.get()
            if current_tool:
                event["tool"] = current_tool
        if "level" not in event:
            kind = str(event.get("kind", "")).lower()
            event["level"] = "error" if kind.endswith("error") else "info"

        with _LOG_LOCK:
            if settings.LOG_PATH.is_dir():
                logger.warning("Log path is a directory, skipping log write")
                return
            settings.LOG_PATH.parent.mkdir(parents=True, exist_ok=True)
            rotate_log_if_needed()
            with open(settings.LOG_PATH, "a", encoding="utf-8") as f:
                f.write(json.dumps(event, ensure_ascii=False) + "\n")
    except Exception as exc:
        logger.warning("Failed to write event log: %s", exc)


def log_tool_start(tool: str, params: dict[str, Any] | None = None) -> None:
    event: dict[str, Any] = {"kind": "tool_start", "tool": tool}
    if params:
        event["params_keys"] = list(params.keys())
        event["params_preview"] = {k: f"len={len(str(v))}" for k, v in params.items()}
    log_event(event)


def log_tool_complete(tool: str, latency_ms: float, result_keys: list[str] | None = None) -> None:
    log_event(
        {
            "kind": "tool_complete",
            "tool": tool,
            "latency_ms": int(latency_ms),
            "result_keys": result_keys,
        }
    )


def log_tool_error(tool: str, latency_ms: float, error: str, error_type: str | None = None) -> None:
    log_event(
        {
            "kind": "tool_error",
            "tool": tool,
            "latency_ms": int(latency_ms),
            "error": redact_value(error, 500),
            "error_type": error_type or "Exception",
        }
    )
