"""
VSS Logging Configuration
==========================
Three rotating log files under  logs/  (next to main.py):

  logs/vss_app.log      — every INFO+ message from every module
  logs/vss_trace.log    — live per-node step trace + final request summary
  logs/vss_errors.log   — WARNING / ERROR / CRITICAL only

All handlers rotate at 10 MB, keeping the last 7 backups.

Public API
----------
setup_logging(level)          — call once at startup (main.py lifespan)
log_node_step(state, node, prev_trace_len)
                              — call after EVERY orchestration node; writes
                                only the NEW trace entries for that node
log_request_trace(state)      — call at end of VSSOrchestrator.run();
                                writes the complete request summary block
"""

from __future__ import annotations

import logging
import logging.handlers
from datetime import datetime, timezone
from pathlib import Path
from typing import Any, Dict

# ─────────────────────────────────────────────────────────────────────────────
# Paths
# ─────────────────────────────────────────────────────────────────────────────

_LOGS_DIR = Path(__file__).resolve().parents[1] / "logs"
_APP_LOG   = _LOGS_DIR / "vss_app.log"
_TRACE_LOG = _LOGS_DIR / "vss_trace.log"
_ERROR_LOG = _LOGS_DIR / "vss_errors.log"

# ─────────────────────────────────────────────────────────────────────────────
# Formatters
# ─────────────────────────────────────────────────────────────────────────────

# vss_app.log  — aligned timestamp | level | module | message
_APP_FMT = logging.Formatter(
    fmt="%(asctime)s  %(levelname)-8s  %(name)-40s  %(message)s",
    datefmt="%Y-%m-%d %H:%M:%S",
)

# vss_errors.log — same layout, only WARNING+
_ERR_FMT = logging.Formatter(
    fmt="%(asctime)s  %(levelname)-8s  %(name)-40s  %(message)s",
    datefmt="%Y-%m-%d %H:%M:%S",
)

# vss_trace.log — raw pre-formatted blocks; no extra prefix
_TRACE_FMT = logging.Formatter(fmt="%(message)s")

# console — short time, level, module
_CONSOLE_FMT = logging.Formatter(
    fmt="%(asctime)s  %(levelname)-8s  %(name)s  %(message)s",
    datefmt="%H:%M:%S",
)

# ─────────────────────────────────────────────────────────────────────────────
# Internal state
# ─────────────────────────────────────────────────────────────────────────────

_trace_logger = logging.getLogger("vss.trace")
_trace_logger.propagate = False   # trace blocks go ONLY to vss_trace.log

_configured = False

_W = 78   # visual width for box-drawing


# ─────────────────────────────────────────────────────────────────────────────
# Setup
# ─────────────────────────────────────────────────────────────────────────────

def setup_logging(level: str = "INFO") -> None:
    """
    Attach all rotating file handlers and a console handler.
    Idempotent — safe to call multiple times; only configures once.
    """
    global _configured
    if _configured:
        return

    _LOGS_DIR.mkdir(parents=True, exist_ok=True)

    def _file_handler(path: Path, lvl: int, fmt: logging.Formatter):
        h = logging.handlers.RotatingFileHandler(
            path, maxBytes=10 * 1024 * 1024, backupCount=7, encoding="utf-8"
        )
        h.setFormatter(fmt)
        h.setLevel(lvl)
        return h

    app_h   = _file_handler(_APP_LOG,   logging.DEBUG,   _APP_FMT)
    err_h   = _file_handler(_ERROR_LOG, logging.WARNING, _ERR_FMT)
    trace_h = _file_handler(_TRACE_LOG, logging.DEBUG,   _TRACE_FMT)

    _trace_logger.setLevel(logging.DEBUG)
    _trace_logger.addHandler(trace_h)

    console_h = logging.StreamHandler()
    console_h.setFormatter(_CONSOLE_FMT)
    console_h.setLevel(logging.INFO)

    root = logging.getLogger()
    root.setLevel(logging.DEBUG)

    existing = {type(h) for h in root.handlers}
    if logging.handlers.RotatingFileHandler not in existing:
        root.addHandler(app_h)
        root.addHandler(err_h)
    if not any(
        isinstance(h, logging.StreamHandler) and not isinstance(h, logging.FileHandler)
        for h in root.handlers
    ):
        root.addHandler(console_h)

    ts  = datetime.now(timezone.utc).strftime("%Y-%m-%d %H:%M:%S UTC")
    sep = "═" * _W
    banner = f"\n{sep}\n  VSS SERVICE STARTED — {ts}\n{sep}"
    logging.getLogger("vss.startup").info(banner)
    _trace_logger.info(banner)

    _configured = True


# ─────────────────────────────────────────────────────────────────────────────
# Per-node intermediate step logger
# ─────────────────────────────────────────────────────────────────────────────

def log_node_step(
    state: Dict[str, Any],
    node: str,
    prev_trace_len: int = 0,
) -> None:
    """
    Write the trace entries that were added by a single orchestration node.

    Call this immediately AFTER each node wrapper returns, passing the
    trace length captured BEFORE the node ran.

    Example output in  vss_trace.log:
    ┌─[ STEP: CLASSIFY ]──[ id:abc12345 ]──[ intent:sql ]──────────────────┐
    │  [14:23:01] Node: classify
    │  [14:23:01] Routing (rules): sql (0.90)
    └───────────────────────────────────────────────────────────────────────┘
    """
    if not _configured:
        return

    trace     = state.get("trace", [])
    new_entries = trace[prev_trace_len:]
    if not new_entries:
        return

    rid    = (state.get("request_id") or "????????")[:8]
    intent = state.get("intent") or "?"
    status = state.get("status") or "?"
    label  = f" STEP: {node.upper()} "
    header = f"┌─[{label}]──[ id:{rid} ]──[ intent:{intent} ]──[ {status} ]"
    header = header.ljust(_W - 1, "─") + "┐"

    body_lines = [f"│  {entry}" for entry in new_entries]
    footer = "└" + "─" * (_W - 1) + "┘"

    _trace_logger.info("\n".join(["", header] + body_lines + [footer]))

    # Mirror a single summary line to vss_app.log
    logging.getLogger("vss.step").debug(
        "STEP %-10s | id=%s | +%d trace entries",
        node.upper(), rid, len(new_entries),
    )


# ─────────────────────────────────────────────────────────────────────────────
# Full request summary (written at end of every request)
# ─────────────────────────────────────────────────────────────────────────────

def log_request_trace(state: Dict[str, Any]) -> None:
    """
    Write the complete per-request summary to vss_trace.log.

    Includes: header, query, routing decision, SQL generated, escalation
    flag, full trace list, and the final answer.

    Example:
    ════════════════════════════════════════════════════════════════════════════
      REQUEST  │  2026-03-02 14:23:01  │  id=abc12345  │  completed  │  412 ms
      QUERY    │  How many fire alerts yesterday?
      INTENT   │  sql  (conf=0.90)  via: SQL patterns: 3
      SQL      │  SELECT SUM(count) FROM default.aggregated_analytics_final
      SOURCE   │  sql
    ────────────────────────────────────────────────────────────────────────────
      FULL TRACE  (7 steps)
        [14:23:01] State initialized: How many fire alerts yesterday?
        [14:23:01] Node: classify
        [14:23:01] Routing (rules): sql (0.90)
        [14:23:01] Node: sql
        [14:23:01] SQLAgent: process_sql called
        [14:23:01] SQLAgent: completed (1 attempt(s))
        [14:23:01] Node: compose
    ────────────────────────────────────────────────────────────────────────────
      ANSWER
        There were 42 fire alerts recorded yesterday.
    ════════════════════════════════════════════════════════════════════════════
    """
    if not _configured:
        setup_logging()

    ts     = datetime.now(timezone.utc).strftime("%Y-%m-%d %H:%M:%S")
    rid    = state.get("request_id", "unknown")
    status = state.get("status", "unknown")
    ms     = state.get("processing_time_ms")
    timing = f"{ms} ms" if ms is not None else "? ms"
    trace  = state.get("trace", [])

    double = "═" * _W
    single = "─" * _W

    lines: list[str] = [
        "",
        double,
        f"  REQUEST  │  {ts}  │  id={rid}  │  {status}  │  {timing}",
        f"  QUERY    │  {state.get('user_query', '')}",
        (
            f"  INTENT   │  {state.get('intent','?')}"
            f"  (conf={state.get('intent_confidence', 0):.2f})"
            f"  via: {state.get('routing_reason', '')}"
        ),
    ]

    sql_q = state.get("sql_query") or ""
    if sql_q:
        # Wrap SQL at 110 chars
        sql_lines = [sql_q[i:i+110] for i in range(0, len(sql_q), 110)]
        lines.append(f"  SQL      │  {sql_lines[0]}")
        for part in sql_lines[1:]:
            lines.append(f"           │  {part}")

    sql_err = state.get("sql_error") or ""
    if sql_err:
        lines.append(f"  SQL ERR  │  {sql_err}")

    if state.get("escalated"):
        lines.append(f"  ESCALATED│  reason: {state.get('escalation_reason', '')}")

    lines.append(f"  SOURCE   │  {state.get('response_source', '')}")

    # ── Full trace ────────────────────────────────────────────────────────────
    lines.append(single)
    lines.append(f"  FULL TRACE  ({len(trace)} steps)")
    for entry in trace:
        lines.append(f"    {entry}")

    # ── Final answer ──────────────────────────────────────────────────────────
    lines.append(single)
    lines.append("  ANSWER")
    response = state.get("final_response") or ""
    for chunk in [response[i:i+110] for i in range(0, max(len(response), 1), 110)]:
        lines.append(f"    {chunk}")

    lines.append(double)

    _trace_logger.info("\n".join(lines))

    # Mirror a concise summary to vss_app.log
    app = logging.getLogger("vss.orchestrator")
    app.info(
        "REQUEST COMPLETE  │  id=%s  │  %s  │  intent=%s  │  source=%s  │  %s",
        rid, status,
        state.get("intent", "?"),
        state.get("response_source", "?"),
        timing,
    )
    if sql_err:
        app.warning("SQL ERROR  │  id=%s  │  %s", rid, sql_err)
    vlm_err = state.get("vlm_error") or ""
    if vlm_err:
        app.warning("VLM ERROR  │  id=%s  │  %s", rid, vlm_err)
    if state.get("error"):
        app.error("ORCHESTRATION ERROR  │  id=%s  │  %s", rid, state["error"])
