"""
logger.py — Append-only JSONL task log with automatic rotation.

Each completed tool invocation is recorded as one JSON line:
  {"ts": "...", "tool": "...", "args": {...}, "result_keys": [...], "error": null, "duration_ms": N}

Import and use: from .logger import log_task
"""
from __future__ import annotations

import json
import logging
import logging.handlers
import sys
import threading
import time
from datetime import datetime, timezone
from pathlib import Path

from .config import LOG_BACKUP_COUNT, LOG_DIR, LOG_MAX_BYTES, TASK_LOG


# ---------------------------------------------------------------------------
# Module-level logger (stderr; never causes crashes)
# ---------------------------------------------------------------------------

_console = logging.getLogger("callme")
_console.setLevel(logging.DEBUG)
if not _console.handlers:
    _handler = logging.StreamHandler(sys.stderr)
    _handler.setFormatter(logging.Formatter("[callme] %(levelname)s %(message)s"))
    _console.addHandler(_handler)


# ---------------------------------------------------------------------------
# JSONL task log (append-only, size-rotated)
# ---------------------------------------------------------------------------

_log_lock = threading.Lock()


def _ensure_log_dir() -> None:
    try:
        LOG_DIR.mkdir(parents=True, exist_ok=True)
    except OSError:
        pass


def _rotate_if_needed() -> None:
    """Simple size-based rotation: rename .jsonl → .jsonl.1, keeping up to LOG_BACKUP_COUNT."""
    try:
        if not TASK_LOG.exists():
            return
        if TASK_LOG.stat().st_size < LOG_MAX_BYTES:
            return
        # Shift existing backups
        for i in range(LOG_BACKUP_COUNT - 1, 0, -1):
            src = TASK_LOG.with_suffix(f".jsonl.{i}")
            dst = TASK_LOG.with_suffix(f".jsonl.{i + 1}")
            if src.exists():
                src.rename(dst)
        TASK_LOG.rename(TASK_LOG.with_suffix(".jsonl.1"))
    except OSError:
        pass


def log_task(tool: str, args: dict, result: dict, duration_ms: float) -> None:
    """Append a single tool execution record to the JSONL task log. Never raises."""
    try:
        _ensure_log_dir()
        with _log_lock:
            _rotate_if_needed()
            record = {
                "ts": datetime.now(tz=timezone.utc).strftime("%Y-%m-%dT%H:%M:%SZ"),
                "tool": tool,
                "args": _sanitize_args(args),
                "result_keys": list(result.keys()) if isinstance(result, dict) else [],
                "error": result.get("error") if isinstance(result, dict) else None,
                "duration_ms": round(duration_ms, 1),
            }
            with open(TASK_LOG, "a", encoding="utf-8") as f:
                f.write(json.dumps(record, ensure_ascii=False, default=str) + "\n")
    except Exception:
        pass  # Logging must never crash the agent


def _sanitize_args(args: dict) -> dict:
    """Truncate large values and redact secrets before logging."""
    if not isinstance(args, dict):
        return {}
    sensitive_keys = {"password", "secret", "key", "token", "api_key", "content"}
    out = {}
    for k, v in args.items():
        k_lower = k.lower()
        if any(s in k_lower for s in sensitive_keys):
            out[k] = "***REDACTED***"
        elif isinstance(v, str) and len(v) > 200:
            out[k] = v[:200] + "…"
        else:
            out[k] = v
    return out


def read_recent_tasks(n: int = 20) -> list[dict]:
    """Return the most recent N task log entries, newest first."""
    try:
        if not TASK_LOG.exists():
            return []
        lines = TASK_LOG.read_text(encoding="utf-8", errors="replace").splitlines()
        records: list[dict] = []
        for line in reversed(lines):
            line = line.strip()
            if not line:
                continue
            try:
                records.append(json.loads(line))
            except json.JSONDecodeError:
                pass
            if len(records) >= n:
                break
        return records
    except Exception:
        return []


# ---------------------------------------------------------------------------
# Structured agent event log (to stderr / rotating file if LOG_DIR writable)
# ---------------------------------------------------------------------------

def setup_file_logging() -> None:
    """Set up rotating file logging to LOG_DIR/agent.log. Call once at startup."""
    try:
        _ensure_log_dir()
        log_file = LOG_DIR / "agent.log"
        fh = logging.handlers.RotatingFileHandler(
            log_file,
            maxBytes=LOG_MAX_BYTES,
            backupCount=LOG_BACKUP_COUNT,
            encoding="utf-8",
        )
        fh.setFormatter(
            logging.Formatter("%(asctime)s %(levelname)-8s %(name)s  %(message)s")
        )
        _console.addHandler(fh)
        _console.info("File logging initialised → %s", log_file)
    except Exception as exc:
        _console.warning("Could not set up file logging: %s", exc)


def get_logger(name: str = "callme") -> logging.Logger:
    """Return a logger child of the callme root logger."""
    return logging.getLogger(f"callme.{name}") if name != "callme" else _console
