"""Load site-specific browser skills for domain-aware automation.

Skills are YAML files that provide per-domain procedures, anti-detection configs,
and authentication check patterns. They're loaded when the agent navigates to a
known domain, giving the LLM domain-specific context.
"""

from __future__ import annotations

import os
from dataclasses import dataclass, field
from pathlib import Path
from typing import Any

from loguru import logger

try:
    import yaml
except ImportError:
    yaml = None  # type: ignore[assignment]


@dataclass(frozen=True)
class AuthCheck:
    """How to verify if the browser is still authenticated on this domain."""
    url: str
    success_indicators: list[str]
    login_wall_indicators: list[str]


@dataclass(frozen=True)
class Procedure:
    """A named browser procedure for a specific action on a domain."""
    name: str
    description: str
    steps: list[str]
    warnings: list[str]


@dataclass(frozen=True)
class AntiDetectionConfig:
    """Domain-specific anti-detection settings."""
    min_delay_between_actions_ms: int = 500
    max_delay_between_actions_ms: int = 2000
    scroll_naturally: bool = True
    warm_up_seconds: float = 2.0
    max_actions_per_session: int = 50
    avoid_patterns: list[str] = field(default_factory=list)


@dataclass(frozen=True)
class DomainSkill:
    """Complete browser skill definition for a domain."""
    domain: str
    aliases: list[str]
    version: int
    auth_check: AuthCheck | None
    procedures: dict[str, Procedure]
    anti_detection: AntiDetectionConfig

    def get_procedure(self, name: str) -> Procedure | None:
        return self.procedures.get(name)

    def format_for_llm(self, *, include_procedures: bool = True) -> str:
        """Format skill as context for the LLM."""
        parts = [f"Site-specific knowledge for {self.domain}:"]

        if self.anti_detection.avoid_patterns:
            parts.append("\nIMPORTANT — Anti-detection rules:")
            for pattern in self.anti_detection.avoid_patterns:
                parts.append(f"  - {pattern}")

        if include_procedures and self.procedures:
            parts.append(f"\nAvailable procedures ({len(self.procedures)}):")
            for name, proc in self.procedures.items():
                parts.append(f"\n  {name}: {proc.description}")
                for i, step in enumerate(proc.steps, 1):
                    parts.append(f"    {i}. {step}")
                if proc.warnings:
                    parts.append("    Warnings:")
                    for w in proc.warnings:
                        parts.append(f"      - {w}")

        return "\n".join(parts)

    def format_metadata_only(self) -> str:
        """Compact metadata for progressive disclosure (first tier)."""
        procs = ", ".join(self.procedures.keys())
        return (
            f"[{self.domain}] Procedures: {procs} | "
            f"Delays: {self.anti_detection.min_delay_between_actions_ms}-"
            f"{self.anti_detection.max_delay_between_actions_ms}ms | "
            f"Max actions/session: {self.anti_detection.max_actions_per_session}"
        )


class SkillLibrary:
    """Loads and caches domain-specific browser skills from YAML files."""

    def __init__(self, skills_dirs: list[Path] | None = None):
        self._skills: dict[str, DomainSkill] = {}
        self._alias_map: dict[str, str] = {}
        self._loaded = False
        self._skills_dirs = skills_dirs or self._default_dirs()

    @staticmethod
    def _default_dirs() -> list[Path]:
        """Default locations to look for browser skill files."""
        dirs = []
        # Agent workspace skills
        workspace = os.environ.get("MADPOD_WORKSPACE")
        if workspace:
            dirs.append(Path(workspace) / "skills" / "browser_skills")
        # System-level skills (deployed with the API)
        api_skills = Path(__file__).resolve().parents[4] / "api" / "src" / "skills" / "browser_skills"
        if api_skills.exists():
            dirs.append(api_skills)
        # Package-bundled skills
        pkg_skills = Path(__file__).resolve().parent / "skills"
        if pkg_skills.exists():
            dirs.append(pkg_skills)
        return dirs

    def load(self) -> None:
        """Load all skill files from configured directories."""
        if yaml is None:
            logger.warning("PyYAML not installed — browser skills disabled")
            return

        for skills_dir in self._skills_dirs:
            if not skills_dir.exists():
                continue
            for path in sorted(skills_dir.glob("*.yaml")):
                try:
                    skill = self._parse_skill_file(path)
                    self._skills[skill.domain] = skill
                    for alias in skill.aliases:
                        self._alias_map[alias] = skill.domain
                    logger.debug("Loaded browser skill: {}", skill.domain)
                except Exception as exc:
                    logger.warning("Failed to load skill {}: {}", path.name, exc)

        self._loaded = True
        logger.info("Loaded {} browser skills", len(self._skills))

    def get(self, domain: str) -> DomainSkill | None:
        """Get skill for a domain, resolving aliases."""
        if not self._loaded:
            self.load()

        clean = domain.lower().strip().removeprefix("www.")
        if clean in self._skills:
            return self._skills[clean]
        resolved = self._alias_map.get(clean)
        if resolved:
            return self._skills.get(resolved)
        return None

    def get_all_domains(self) -> list[str]:
        """Return all known domains (including aliases)."""
        if not self._loaded:
            self.load()
        domains = list(self._skills.keys())
        domains.extend(self._alias_map.keys())
        return sorted(set(domains))

    @staticmethod
    def _parse_skill_file(path: Path) -> DomainSkill:
        """Parse a YAML skill file into a DomainSkill."""
        data = yaml.safe_load(path.read_text())

        # Auth check
        auth_raw = data.get("auth_check")
        auth_check = None
        if auth_raw:
            auth_check = AuthCheck(
                url=auth_raw.get("url", ""),
                success_indicators=auth_raw.get("success_indicators", []),
                login_wall_indicators=auth_raw.get("login_wall_indicators", []),
            )

        # Procedures
        procedures = {}
        for name, proc_data in (data.get("procedures") or {}).items():
            procedures[name] = Procedure(
                name=name,
                description=proc_data.get("description", ""),
                steps=proc_data.get("steps", []),
                warnings=proc_data.get("warnings", []),
            )

        # Anti-detection
        ad_raw = data.get("anti_detection") or {}
        anti_detection = AntiDetectionConfig(
            min_delay_between_actions_ms=ad_raw.get("min_delay_between_actions_ms", 500),
            max_delay_between_actions_ms=ad_raw.get("max_delay_between_actions_ms", 2000),
            scroll_naturally=ad_raw.get("scroll_naturally", True),
            warm_up_seconds=ad_raw.get("warm_up_seconds", 2.0),
            max_actions_per_session=ad_raw.get("max_actions_per_session", 50),
            avoid_patterns=ad_raw.get("avoid_patterns", []),
        )

        return DomainSkill(
            domain=data.get("domain", path.stem),
            aliases=data.get("aliases", []),
            version=data.get("version", 1),
            auth_check=auth_check,
            procedures=procedures,
            anti_detection=anti_detection,
        )


# Module-level singleton
_library: SkillLibrary | None = None


def get_skill_library() -> SkillLibrary:
    """Get or create the global skill library singleton."""
    global _library
    if _library is None:
        _library = SkillLibrary()
    return _library


def get_domain_skill(domain: str) -> DomainSkill | None:
    """Convenience: look up a domain skill from the global library."""
    return get_skill_library().get(domain)
