"""Skill registry and intent detection for progressive skill loading."""

from __future__ import annotations

import logging
from dataclasses import dataclass
from typing import Optional

logger = logging.getLogger(__name__)


# Fallback skill content — used when .md files are missing from the installed package.
# This ensures the agent always gets basic tool-usage guidance even on broken installs.
_FALLBACK_SKILLS: dict[str, str] = {
    "investigate": (
        "# Investigate Skill\n\n"
        "You are in **Investigate** mode. The user wants targeted code analysis.\n\n"
        "## Workflow\n\n"
        "1. Use `Grep` or `Glob` (built-in tools) to find files relevant to the user's question.\n"
        "2. `Read` each relevant file (up to 5-7 files). Read them yourself.\n"
        "3. Analyze the code in context of what the user asked.\n"
        "4. Report findings with file:line citations.\n"
        "5. Include a **Coverage** section.\n\n"
        "## Rules\n\n"
        "- Do NOT use `codebase_scan` or `review_files` -- those are for broad scans.\n"
        "- Do NOT delegate to workers. YOU read the code.\n"
        "- Always cite `filename:line_number` for every claim.\n"
        "- Read code BEFORE explaining it."
    ),
    "scan": (
        "# Broad Scan Skill\n\n"
        "You are in **Scan** mode. The user wants a comprehensive codebase scan.\n\n"
        "## Workflow\n\n"
        "1. `codebase_scan` -- get Semgrep findings + operational diagnostics.\n"
        "2. Identify files with most findings or highest severity.\n"
        "3. `review_files(paths=[top_files], focus=<user's request>)`.\n"
        "4. Present findings table: | File:Line | Issue | Severity | Confidence | Source |\n"
        "5. Include a **Coverage** section."
    ),
    "fix_flaky": (
        "# Fix Flaky Test Skill\n\n"
        "You are in **Fix** mode. Detect and heal flaky test patterns.\n\n"
        "## Workflow\n\n"
        "1. `project_health_snapshot` to detect flaky patterns on the SPECIFIC file.\n"
        "2. `Read` (built-in) to understand context.\n"
        "3. `generate_fix_candidates` with the path and `dry_run=True` -- preview only.\n"
        "4. ONLY apply with `dry_run=False` if user explicitly asked to apply."
    ),
    "production_debug": (
        "# Production Debug Skill\n\n"
        "You are in **Production Debug** mode. Debug production errors with replay.\n\n"
        "## Workflow\n\n"
        "1. `incident_overview` -- identify the target error and get details.\n"
        "2. `Read` (built-in) on the file from the stack trace.\n"
        "3. `validate_fix_candidate` with replay mode -- confirm reproduction.\n"
        "4. `generate_fix_candidates` -- propose a fix."
    ),
    "deep_dive": (
        "# Deep Dive Skill\n\n"
        "You are in **Deep Dive** mode. Continue investigating from previous findings.\n\n"
        "## Workflow\n\n"
        "1. Review previous findings, pick TOP unresolved issue by severity.\n"
        "2. `Read` (built-in) on that specific file.\n"
        "3. Analyze with code citations -- do NOT re-run `codebase_scan`."
    ),
    "self_analysis": (
        "# Self-Analysis Skill\n\n"
        "You are in **Self-Analysis** mode. Analyze your own source code.\n\n"
        "## Workflow\n\n"
        "1. `Grep`/`Glob` (built-in) to find your code: `autodebug/agent/`, `autodebug/cli_v2.py`.\n"
        "2. `Read` (built-in) on your own source files.\n"
        "3. Look for: broken code paths, missing error handling, duplicated logic.\n"
        "4. Propose concrete fixes with diffs."
    ),
    "test_runner": (
        "# Test Runner Skill\n\n"
        "You are in **Test Runner** mode. The user wants to run tests and investigate results.\n\n"
        "## Workflow\n\n"
        "1. `run_tests` with the path to the test file or directory.\n"
        "2. Read the output: pass/fail counts, failure messages.\n"
        "3. For failures: `Read` (built-in) the failing test file.\n"
        "4. Diagnose the failure — is it a bug, flaky, or environment issue?\n"
        "5. If flaky: `project_health_snapshot` to detect patterns.\n"
        "6. Offer to fix or investigate further."
    ),
    "local_debug": (
        "# Local Debug Skill\n\n"
        "You are in **Local Debug** mode. The user wants to debug a local test or code issue.\n\n"
        "## Workflow\n\n"
        "1. `Read` (built-in) the file the user is asking about.\n"
        "2. `Grep` (built-in) to trace function calls, imports, and dependencies.\n"
        "3. `run_tests` to reproduce the issue if it's a test failure.\n"
        "4. `project_health_snapshot` to check for flaky patterns.\n"
        "5. Diagnose and explain the root cause with file:line citations.\n"
        "6. Propose a fix using `Edit` or `Write` (built-in tools)."
    ),
}


@dataclass(frozen=True)
class SkillMetadata:
    """Metadata for a loadable skill."""

    name: str
    description: str
    triggers: tuple[str, ...]
    filename: str


SKILL_REGISTRY: tuple[SkillMetadata, ...] = (
    SkillMetadata(
        name="investigate",
        description="Targeted code analysis: read code, trace logic, find bugs",
        triggers=(
            "how does", "why is", "analyze", "find issues", "improve",
            "what does", "explain", "trace", "understand",
        ),
        filename="investigate.md",
    ),
    SkillMetadata(
        name="scan",
        description="Broad codebase scan with Semgrep + AI workers",
        triggers=(
            "scan", "find bugs", "check codebase", "what's wrong",
            "audit", "review codebase", "security", "issues",
        ),
        filename="scan.md",
    ),
    SkillMetadata(
        name="test_runner",
        description="Run tests, investigate failures, diagnose issues",
        triggers=(
            "run my tests", "run tests", "run pytest", "execute tests",
            "test suite", "run the tests",
        ),
        filename="test_runner.md",
    ),
    SkillMetadata(
        name="local_debug",
        description="Debug local test failures and code issues",
        triggers=(
            "debug this", "why is this failing", "this test fails",
            "debug test", "debug local", "why does this fail",
            "what's wrong with this", "fix this test",
        ),
        filename="local_debug.md",
    ),
    SkillMetadata(
        name="fix_flaky",
        description="Detect and heal flaky test patterns",
        triggers=("fix flaky", "heal", "repair flaky", "flaky", "unstable test"),
        filename="fix_flaky.md",
    ),
    SkillMetadata(
        name="production_debug",
        description="Debug production errors with deterministic replay",
        triggers=(
            "production error", "prod error", "replay error",
            "production issue", "production bug", "production",
        ),
        filename="production_debug.md",
    ),
    SkillMetadata(
        name="deep_dive",
        description="Continue investigation from previous findings",
        triggers=("continue", "dig deeper", "next", "keep going", "more detail"),
        filename="deep_dive.md",
    ),
    SkillMetadata(
        name="self_analysis",
        description="Analyze and improve Hikaflow's own source code",
        triggers=(
            "analyze yourself", "self-improve", "find your own bugs",
            "improve yourself", "your own code",
        ),
        filename="self_analysis.md",
    ),
)

_SKILL_BY_NAME: dict[str, SkillMetadata] = {s.name: s for s in SKILL_REGISTRY}

# Per-skill tool-turn budgets — how many agent turns a skill should use
# before wrapping up. Prevents any single workflow from monopolizing the session.
SKILL_BUDGETS: dict[str, int] = {
    "scan": 15,
    "investigate": 25,
    "test_runner": 20,
    "local_debug": 20,
    "fix_flaky": 20,
    "production_debug": 25,
    "deep_dive": 30,
    "self_analysis": 15,
}

_GREETING_PATTERNS = frozenset({
    "hi", "hello", "hey", "what can you do", "help",
})


def detect_skill(user_message: str) -> Optional[str]:
    """Detect which skill to load based on the user's message.

    Returns the skill name or None if no skill matches (greeting/chat mode).
    """
    msg_lower = user_message.lower().strip()

    if msg_lower in _GREETING_PATTERNS or len(msg_lower) < 3:
        return None

    best_skill: Optional[str] = None
    best_score: int = 0
    best_trigger_len: int = 0

    for skill in SKILL_REGISTRY:
        score = 0
        max_trigger_len = 0
        for trigger in skill.triggers:
            if trigger in msg_lower:
                score += 1
                max_trigger_len = max(max_trigger_len, len(trigger))
        if score > best_score or (
            score == best_score and max_trigger_len > best_trigger_len
        ):
            best_skill = skill.name
            best_score = score
            best_trigger_len = max_trigger_len

    return best_skill if best_score > 0 else None


def load_skill_content(skill_name: str) -> Optional[str]:
    """Load a skill's markdown instructions.

    Tries importlib.resources first (works with all install modes including
    zip imports and frozen apps), falls back to embedded content if the
    .md file is missing from the installed package.
    """
    skill = _SKILL_BY_NAME.get(skill_name)
    if not skill:
        logger.warning("Unknown skill: %s", skill_name)
        return None

    # Try importlib.resources (Python 3.9+)
    try:
        from importlib.resources import files
        ref = files("autodebug.agent.skills").joinpath(skill.filename)
        content = ref.read_text(encoding="utf-8").strip()
        if content:
            return content
    except Exception:
        pass

    # Fall back to embedded content
    fallback = _FALLBACK_SKILLS.get(skill_name)
    if fallback:
        logger.info("Using fallback skill content for '%s' (md file not in package)", skill_name)
        return fallback

    logger.warning("Skill file not found and no fallback for: %s", skill_name)
    return None


def build_skill_menu() -> str:
    """Build a compact menu of available skills for the static core prompt."""
    lines = ["## Available Skills", ""]
    for skill in SKILL_REGISTRY:
        lines.append(f"- **{skill.name}**: {skill.description}")
    lines.append("")
    lines.append(
        "Skills are loaded automatically based on your request, "
        "or the agent can use `knowledge_memory(action='load_skill', skill_name=...)` to load one explicitly."
    )
    lines.append("")
    lines.append(
        "You also have a **dynamic skill library** of learned procedures. "
        "Use `knowledge_memory(action='search_skills', query=...)` to find relevant skills, "
        "`knowledge_memory(action='use_skill', skill_id=...)` to load one, "
        "and `knowledge_memory(action='create_skill', ...)` to save new ones."
    )
    return "\n".join(lines)


def get_skill_names() -> list[str]:
    """Return list of all registered skill names."""
    return [s.name for s in SKILL_REGISTRY]
