"""Per-task file logger — single shared ``pointer.task`` logger for all agents.

Each agent obtains a tagged view on the shared logger via :func:`get_agent_logger`,
which wraps the base logger in a :class:`logging.LoggerAdapter` that injects an
``agent`` field into every record. There is only one real logger — child
loggers are intentionally avoided so root-level handlers can never siphon off
our task logs.
"""

from __future__ import annotations

import base64
import logging
import secrets
from datetime import datetime
from pathlib import Path

TURN_SEPARATOR = "-" * 72
logger: logging.Logger | None = None
results_dir: Path | None = None


class _AgentLoggerAdapter(logging.LoggerAdapter):
    """Tags every record with the agent's name via the ``agent`` extra field."""

    def process(self, msg, kwargs):  # type: ignore[override]
        extra = kwargs.setdefault("extra", {})
        extra.setdefault("agent", self.extra["agent"])
        return msg, kwargs


class _DefaultAgentFilter(logging.Filter):
    """Ensures records without an ``agent`` field still format cleanly."""

    def filter(self, record: logging.LogRecord) -> bool:
        if not hasattr(record, "agent"):
            record.agent = "pointer"
        return True


def setup_task_logger(
    task_logger: logging.Logger,
    task_result_dir: str,
) -> Path:
    """Create a per-task log directory and attach a FileHandler to the task logger.

    A single ``pointer.task`` logger is used for every agent. Propagation is
    disabled so messages never bubble into any root-level handler a caller
    may have configured (e.g. via ``logging.basicConfig``).

    Returns the log directory Path (for screenshot saving, etc.).
    """
    global logger, results_dir
    logger = task_logger
    results_dir = Path(task_result_dir)


def get_agent_logger(agent_name: str) -> _AgentLoggerAdapter:
    """Return a logger adapter that tags records with ``agent_name``."""
    global logger
    return _AgentLoggerAdapter(logger, {"agent": agent_name})


def save_screenshot(screenshot_b64: str, step_idx: int) -> str:
    """Decode a base64 PNG screenshot and save it to the current task's log directory."""
    if not screenshot_b64:
        return ""
    path = save_screenshot_bytes(base64.b64decode(screenshot_b64), label=f"step_{step_idx}")
    return str(path)


def save_screenshot_bytes(png_bytes: bytes, label: str = "screenshot") -> str:
    """Save raw PNG bytes to the current task log dir and return the path.

    Unique filename per call (timestamp + short token) so repeated captures
    within a step never clobber each other.
    """
    global results_dir
    if not results_dir or not png_bytes:
        return ""

    path = results_dir / f"{label}_{datetime.now().strftime('%Y%m%d-%H%M%S')}_{secrets.token_hex(3)}.png"
    path.write_bytes(png_bytes)
    get_agent_logger("pointer").info("Screenshot saved: %s", path)
    return str(path)


def log_agent_api_call(
    agent_name: str,
    tool_calls: list,
    thinking_blocks: list,
    text_blocks: list,
    tool_call_results: list,
    agent_action: str,
) -> None:
    _task_log = get_agent_logger(agent_name)
    _task_log.info(TURN_SEPARATOR)
    for t in thinking_blocks:
        _task_log.info("Thinking: %s", t)
    for tb in text_blocks:
        _task_log.info("Text: %s", tb)
    for tc in tool_calls:
        _task_log.info("Tool call [%s][%s]: %s", tc["id"], tc["name"], tc["input"])
    for tcr in tool_call_results:
        tid, tr = tcr
        _task_log.info("Tool call [%s]:\nRESULT:\n%s\nERROR:\n%s", tid, tr.output, tr.error)
    _task_log.info("Agent action:\n%s", agent_action)
    _task_log.info("")
