"""CursorAdapter direct query mode — lightweight agent queries without session overhead.

This module provides the CursorAdapter class implementing the CodingAgentBridge
protocol. It wraps orchestrator.handle_user_message_direct for 1:1 agent queries,
parallel batch queries, quizzes, and brief presentations.

Unlike the presentation engine (sessions/engine.py), the direct mode keeps
briefing content front and center in the agent's system prompt, making it
ideal for knowledge checks, quick questions, and plan presentations.

Windows-safe: sanitizes Unicode characters that cp1252 can't encode.
"""

from __future__ import annotations

import logging
import re
from datetime import UTC, datetime
from pathlib import Path
from typing import Any

logger = logging.getLogger(__name__)

# Characters that Windows cp1252 can't encode
_UNICODE_REPLACEMENTS = {
    "\u2192": "->",  # →
    "\u2190": "<-",  # ←
    "\u2014": "--",  # —
    "\u2013": "-",  # –
    "\u2018": "'",  # '
    "\u2019": "'",  # '
    "\u201c": '"',  # "
    "\u201d": '"',  # "
    "\u2026": "...",  # …
    "\u2022": "*",  # •
    "\u2713": "[x]",  # ✓
    "\u2717": "[ ]",  # ✗
}


def sanitize_windows(text: str) -> str:
    """Replace Unicode characters that Windows cp1252 can't encode.

    Handles known replacements first, then strips any remaining non-cp1252
    characters to prevent UnicodeEncodeError on Windows terminals.
    """
    for char, replacement in _UNICODE_REPLACEMENTS.items():
        text = text.replace(char, replacement)
    # Strip any remaining characters that cp1252 can't encode
    try:
        text.encode("cp1252")
    except UnicodeEncodeError:
        text = text.encode("cp1252", errors="replace").decode("cp1252")
    return text


class CursorAdapter:
    """Implements the CodingAgentBridge protocol for Cursor.

    Uses orchestrator.handle_user_message_direct for reliable agent queries
    where briefing content is included in the system prompt.

    Usage:
        adapter = CursorAdapter(orchestrator)
        response = await adapter.query_agent("henry", "What is your role?")
        results = await adapter.query_agents(["henry", "liam"], "What phase are we in?")
    """

    def __init__(self, orchestrator: Any) -> None:
        self.orchestrator = orchestrator
        self._project_root = Path(__file__).resolve().parents[4]

    async def query_agent(self, agent_id: str, message: str) -> str:
        """Send a message to a single agent and return the response."""
        response = await self.orchestrator.handle_user_message_direct(
            content=message,
            agent_id=agent_id,
        )
        return sanitize_windows(response)

    async def query_agents(self, agent_ids: list[str], message: str) -> dict[str, str]:
        """Send the same message to multiple agents in parallel."""
        tasks = {
            aid: self.orchestrator.handle_user_message_direct(content=message, agent_id=aid)
            for aid in agent_ids
        }

        results = {}
        for aid, coro in tasks.items():
            try:
                response = await coro
                results[aid] = sanitize_windows(response)
            except Exception as e:
                logger.error("Error querying %s: %s", aid, e)
                results[aid] = f"[Error: {e}]"

        return results

    async def quiz_agents(self, agent_ids: list[str], questions: str) -> dict[str, str]:
        """Quiz multiple agents with structured questions.

        Each agent receives the same question block. Responses are collected
        in parallel for speed.
        """
        quiz_prompt = (
            "Answer each question below with a short, precise answer. "
            "Number your answers to match the questions.\n\n"
            f"{questions}"
        )
        return await self.query_agents(agent_ids, quiz_prompt)

    async def present(self, agent_ids: list[str], brief: str, topic: str = "") -> dict:
        """Present a brief to agents and collect their responses.

        Each agent receives the brief and is asked for concerns, commitments,
        and questions. Responses are collected in parallel.
        """
        present_prompt = (
            f"You are being briefed on: {topic}\n\n"
            f"--- BRIEF ---\n{brief}\n--- END BRIEF ---\n\n"
            "After reading this brief carefully, respond with:\n"
            "1. Your understanding of how this affects your role\n"
            "2. Any concerns or risks you see\n"
            "3. Your commitments for this phase\n"
            "4. Any questions you have"
        )

        responses = await self.query_agents(agent_ids, present_prompt)

        return {
            "topic": topic,
            "agent_count": len(agent_ids),
            "responses": responses,
            "timestamp": datetime.now(UTC).isoformat(),
        }

    def persist_briefs(self, results: dict[str, str], topic: str) -> list[str]:
        """Write results to docs/{agent_id}/briefs/ and return file paths.

        Creates a markdown file for each agent with their response and
        key takeaways section that _build_agent_briefings will pick up.
        """
        docs_root = self._project_root / "docs"
        date_str = datetime.now(UTC).strftime("%Y%m%d")
        topic_slug = re.sub(r"[^a-z0-9]+", "-", topic.lower()).strip("-")[:50]
        filename = f"{date_str}_{topic_slug}.md"

        paths = []
        for agent_id, response in results.items():
            briefs_dir = docs_root / agent_id / "briefs"
            briefs_dir.mkdir(parents=True, exist_ok=True)

            brief_path = briefs_dir / filename
            content = (
                f"# {topic}\n"
                f"**Date**: {date_str}\n"
                f"**Agent**: {agent_id}\n"
                f"**Source**: CursorAdapter direct presentation\n\n"
                f"## Agent Response\n\n{response}\n\n"
                f"## Key Takeaways\n\n"
                f"- Briefed on: {topic}\n"
                f"- Response captured via CodingAgentBridge direct mode\n"
            )

            brief_path.write_text(content, encoding="utf-8")
            paths.append(str(brief_path))
            logger.info("Persisted brief for %s: %s", agent_id, brief_path)

        return paths


__all__ = ["CursorAdapter", "sanitize_windows"]
