"""Prompt builder — generate tool-specific prompts from tickets.

Given a Ticket and a ToolDefinition, produces a formatted prompt string
suitable for the tool's CLI or API input.
"""

from __future__ import annotations

from pathlib import Path
from typing import Any

from proxym.dashboard.tickets import Ticket, TicketType, TicketPriority
from proxym.tools import ToolDefinition


def build_prompt(
    ticket: Ticket,
    tool: ToolDefinition,
    mode: str = "",
    project_dir: str | None = None,
) -> str:
    """Build a prompt string for *tool* from *ticket*.

    The prompt structure adapts to the ticket type (bug, feature, refactor, etc.)
    and includes relevant context like files, description, and priority.
    """
    sections: list[str] = []

    # Header
    sections.append(f"# {ticket.id}: {ticket.title}")
    sections.append(f"Type: {ticket.type.value}  |  Priority: {ticket.priority.value}")

    # Description
    if ticket.description:
        sections.append(f"\n## Description\n{ticket.description}")

    # Files context
    if ticket.files:
        file_list = "\n".join(f"- {f}" for f in ticket.files)
        sections.append(f"\n## Affected files\n{file_list}")

    # Context files — include the actual file contents when available.
    if ticket.context_files:
        context_blocks = _context_file_blocks(ticket.context_files, project_dir=project_dir)
        if context_blocks:
            sections.append("\n## Context files\n" + "\n\n".join(context_blocks))

    # Dependencies
    if ticket.depends_on:
        dep_list = "\n".join(f"- {ticket_id}" for ticket_id in ticket.depends_on)
        sections.append(f"\n## Dependencies\n{dep_list}")

    # Tags
    if ticket.tags:
        sections.append(f"\nTags: {', '.join(ticket.tags)}")

    # Type-specific instructions
    sections.append(_type_instructions(ticket.type))

    # Tool-specific instructions
    sections.append(_tool_instructions(tool, mode or (tool.modes[0] if tool.modes else "")))

    # Constraints
    constraints = _constraints(ticket)
    if constraints:
        sections.append(f"\n## Constraints\n{constraints}")

    return "\n".join(sections)


def _resolve_context_file(path: str, project_dir: str | None = None) -> Path | None:
    """Resolve a context file path relative to the project or current working directory."""
    raw = Path(path).expanduser()
    candidates = [raw]
    if project_dir:
        base = Path(project_dir).expanduser()
        candidates.extend([base / path, base / raw])
    candidates.append(Path.cwd() / raw)

    seen: set[str] = set()
    for candidate in candidates:
        key = str(candidate)
        if key in seen:
            continue
        seen.add(key)
        try:
            if candidate.is_file():
                return candidate
        except Exception:
            continue
    return None


def _context_file_blocks(context_files: list[str], project_dir: str | None = None) -> list[str]:
    """Read context files and format them as prompt sections."""
    blocks: list[str] = []
    seen_paths: set[str] = set()

    for raw_path in context_files:
        if not raw_path:
            continue
        normalized = raw_path.strip()
        if not normalized or normalized in seen_paths:
            continue
        seen_paths.add(normalized)

        resolved = _resolve_context_file(normalized, project_dir=project_dir)
        if not resolved:
            blocks.append(f"### {normalized}\n[missing context file]")
            continue

        try:
            content = resolved.read_text(encoding="utf-8")
        except Exception as exc:
            blocks.append(f"### {normalized}\n[failed to read: {exc}]")
            continue

        if len(content) > 12000:
            content = content[:12000] + "\n\n... [truncated] ..."

        blocks.append(f"### {normalized}\n```text\n{content.rstrip()}\n```")

    return blocks


def _type_instructions(ticket_type: TicketType) -> str:
    """Return task-type-specific instructions."""
    mapping = {
        TicketType.BUG: (
            "\n## Instructions\n"
            "1. Identify the root cause of the bug\n"
            "2. Write a minimal fix (prefer single-line changes when sufficient)\n"
            "3. Add a regression test\n"
            "4. Do not change unrelated code"
        ),
        TicketType.FEATURE: (
            "\n## Instructions\n"
            "1. Implement the feature as described\n"
            "2. Follow existing code patterns and conventions\n"
            "3. Add tests for new functionality\n"
            "4. Update any relevant documentation"
        ),
        TicketType.REFACTOR: (
            "\n## Instructions\n"
            "1. Refactor the code without changing behavior\n"
            "2. Ensure all existing tests still pass\n"
            "3. Keep changes focused — one concern per commit\n"
            "4. Improve readability and maintainability"
        ),
        TicketType.TEST: (
            "\n## Instructions\n"
            "1. Write tests covering the described scenario\n"
            "2. Follow existing test patterns (pytest)\n"
            "3. Include edge cases\n"
            "4. Ensure tests are deterministic"
        ),
        TicketType.DOCS: (
            "\n## Instructions\n"
            "1. Update or create documentation\n"
            "2. Use clear, concise language\n"
            "3. Include code examples where helpful"
        ),
        TicketType.TASK: (
            "\n## Instructions\n"
            "1. Complete the task as described\n"
            "2. Follow existing code style\n"
            "3. Test your changes"
        ),
    }
    return mapping.get(ticket_type, "")


def _tool_instructions(tool: ToolDefinition, mode: str) -> str:
    """Return tool-specific prompt suffix."""
    parts: list[str] = []

    if tool.name == "aider":
        if mode == "architect":
            parts.append("\n## Mode: architect\nDesign the solution first, then implement.")
        else:
            parts.append("\n## Mode: code\nImplement the changes directly.")

    elif tool.name == "claude-code":
        if mode == "debug":
            parts.append("\n## Mode: debug\nDiagnose and fix the issue step by step.")
        elif mode == "architect":
            parts.append("\n## Mode: architect\nPlan the implementation before writing code.")
        else:
            parts.append("\n## Mode: code\nImplement the changes.")

    elif tool.name in ("windsurf", "cursor"):
        parts.append(f"\n## Tool: {tool.name}\nUse the IDE's agent capabilities to implement.")

    elif tool.name == "codex":
        parts.append("\n## Tool: Codex CLI\nImplement the changes directly using full-auto mode.")

    elif tool.name == "goose":
        parts.append("\n## Tool: Goose\nAnalyze the project, write files, and run commands as needed.")

    elif tool.name == "open-interpreter":
        parts.append("\n## Tool: Open Interpreter\nExecute code and shell commands to implement changes.")

    elif tool.name == "plandex":
        if mode == "architect":
            parts.append("\n## Mode: architect\nPlan the multi-file implementation before building.")
        else:
            parts.append("\n## Tool: Plandex\nPlan and implement changes across multiple files.")

    elif tool.name == "llm-cli":
        parts.append("\n## Tool: llm CLI\nProvide implementation guidance and code snippets.")

    return "\n".join(parts)


def _constraints(ticket: Ticket) -> str:
    """Build constraint section from ticket metadata."""
    lines: list[str] = []
    if ticket.estimated_hours:
        lines.append(f"- Time budget: ~{ticket.estimated_hours:.1f}h")
    if ticket.priority == TicketPriority.CRITICAL:
        lines.append("- CRITICAL priority — fix immediately, minimal changes only")
    elif ticket.priority == TicketPriority.HIGH:
        lines.append("- High priority — address promptly")
    return "\n".join(lines)


def build_prompt_dict(
    ticket: Ticket,
    tool: ToolDefinition,
    mode: str = "",
    project_dir: str | None = None,
) -> dict[str, Any]:
    """Return structured prompt data (useful for API payloads)."""
    return {
        "ticket_id": ticket.id,
        "tool": tool.name,
        "mode": mode or (tool.modes[0] if tool.modes else "code"),
        "model": tool.default_model,
        "project_dir": project_dir,
        "prompt": build_prompt(ticket, tool, mode, project_dir=project_dir),
        "files": ticket.files,
        "context_files": ticket.context_files,
        "tags": ticket.tags,
    }
