import json
import logging
import uuid
from datetime import UTC, datetime
from pathlib import Path
from typing import Any

from ...config import settings

logger = logging.getLogger(__name__)

# Log rotation: maximum number of old logs to keep
MAX_ROTATED_LOGS = 5


def rotate_log_if_needed() -> None:
    """Rotate log file if it exceeds size limit and clean up old files."""
    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.info("Rotated log file to %s", rotated_path)

            # Clean up old log files exceeding limit
            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, failures don't affect main flow.

    Args:
        event: Event data to log.
    """
    if not settings.MCP_LOGGING:
        return

    try:
        if "timestamp" not in event:
            event["timestamp"] = datetime.now(UTC).isoformat()
        if "trace_id" not in event:
            event["trace_id"] = str(uuid.uuid4())[:8]
        if "level" not in event:
            kind = str(event.get("kind", "")).lower()
            event["level"] = "error" if kind.endswith("error") else "info"

        if settings.LOG_PATH.is_dir():
            logger.warning("Log path is a directory, skipping log write: %s", settings.LOG_PATH)
            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 Relace log: %s", exc)


def log_create_success(
    trace_id: str, resolved_path: Path, edit_snippet: str, instruction: str | None
) -> None:
    """Log successful new file creation.

    Args:
        trace_id: Trace ID.
        resolved_path: Resolved file path.
        edit_snippet: Edit snippet.
        instruction: Optional instruction.
    """
    log_event(
        {
            "kind": "create_success",
            "level": "info",
            "trace_id": trace_id,
            "file_path": str(resolved_path),
            "file_size_bytes": resolved_path.stat().st_size,
            "instruction": instruction,
            "edit_snippet_preview": edit_snippet[:200],
        }
    )


def log_apply_success(
    trace_id: str,
    started_at: datetime,
    resolved_path: Path,
    file_size: int,
    edit_snippet: str,
    instruction: str | None,
    usage: dict[str, Any],
) -> None:
    """Log successful edit application.

    Args:
        trace_id: Trace ID.
        started_at: Start time.
        resolved_path: Resolved file path.
        file_size: File size.
        edit_snippet: Edit snippet.
        instruction: Optional instruction.
        usage: API usage information.
    """
    latency_ms = int((datetime.now(UTC) - started_at).total_seconds() * 1000)
    log_event(
        {
            "kind": "apply_success",
            "level": "info",
            "trace_id": trace_id,
            "started_at": started_at.isoformat(),
            "latency_ms": latency_ms,
            "file_path": str(resolved_path),
            "file_size_bytes": file_size,
            "instruction": instruction,
            "edit_snippet_preview": edit_snippet[:200],
            "usage": usage,
        }
    )


def log_apply_error(
    trace_id: str,
    started_at: datetime,
    file_path: str,
    edit_snippet: str,
    instruction: str | None,
    exc: Exception,
) -> None:
    """Log error (with latency).

    Args:
        trace_id: Trace ID.
        started_at: Start time.
        file_path: File path.
        edit_snippet: Edit snippet.
        instruction: Optional instruction.
        exc: Exception.
    """
    latency_ms = int((datetime.now(UTC) - started_at).total_seconds() * 1000)
    log_event(
        {
            "kind": "apply_error",
            "level": "error",
            "trace_id": trace_id,
            "started_at": started_at.isoformat(),
            "latency_ms": latency_ms,
            "file_path": file_path,
            "instruction": instruction,
            "edit_snippet_preview": (edit_snippet or "")[:200],
            "error": str(exc),
        }
    )
