"""Configuration: dataclass, persisted config file, and dynamic system prompt."""

from __future__ import annotations

import json
import os
from dataclasses import dataclass, field
from pathlib import Path
from typing import Optional

HOME = Path.home()
PID_FILE = HOME / ".ai-interview.pid"
META_FILE = HOME / ".ai-interview.meta.json"
LOG_FILE = HOME / ".ai-interview.log"
SCREENSHOTS_DIR = HOME / ".ai-interview" / "screenshots"
CONFIG_FILE = HOME / ".config" / "ai-interview" / "v2" / "config.json"

CHALLENGE_TYPES = ["algorithms", "system-design", "debugging", "frontend", "database", "general"]
CLAUDE_MODELS = {
    "sonnet": "claude-sonnet-4-6",   # fast
    "opus": "claude-opus-4-6",       # smarter, slower
    "haiku": "claude-haiku-4-5-20251001",  # fastest, lighter
}


def read_codebase_context(path: str, max_chars: int = 40_000) -> str:
    """Read directory tree + key source files from a repo and return as a text block."""
    root = Path(path).expanduser().resolve()
    if not root.is_dir():
        return ""

    SKIP_DIRS = {
        ".git", "__pycache__", "node_modules", ".venv", "venv",
        "dist", "build", ".next", "target", ".idea", ".gradle",
    }
    CODE_EXT = {".py", ".java", ".ts", ".tsx", ".js", ".go", ".rs",
                ".kt", ".swift", ".cpp", ".c", ".cs", ".rb", ".scala"}

    # --- File tree (depth ≤ 4) ---
    tree_lines: list[str] = []

    def _tree(d: Path, prefix: str = "", depth: int = 0) -> None:
        if depth > 4:
            return
        try:
            children = sorted(d.iterdir(), key=lambda p: (p.is_file(), p.name))
        except PermissionError:
            return
        for i, child in enumerate(children):
            if child.name.startswith(".") or child.name in SKIP_DIRS:
                continue
            connector = "└── " if i == len(children) - 1 else "├── "
            tree_lines.append(prefix + connector + child.name)
            if child.is_dir() and depth < 4 and len(tree_lines) < 200:
                ext = "    " if i == len(children) - 1 else "│   "
                _tree(child, prefix + ext, depth + 1)

    _tree(root)
    sections: list[str] = [
        f"# Codebase: {root.name}\n",
        "## File tree\n```\n" + root.name + "/\n" + "\n".join(tree_lines) + "\n```\n",
    ]
    total = sum(len(s) for s in sections)

    # --- Key doc files ---
    for fname in ("README.md", "README.rst", "package.json", "pyproject.toml", "pom.xml"):
        fpath = root / fname
        if fpath.exists() and total < max_chars:
            try:
                content = fpath.read_text(errors="ignore")[:5_000]
                sec = f"## {fname}\n```\n{content}\n```\n"
                sections.append(sec)
                total += len(sec)
            except Exception:
                pass

    # --- Source files (entry points first, then by size) ---
    source_files = [
        p for p in root.rglob("*")
        if p.suffix in CODE_EXT
        and not any(s in p.parts for s in SKIP_DIRS)
    ]

    def _priority(p: Path) -> tuple:
        if p.stem.lower() in ("main", "app", "index", "server", "api", "application"):
            return (0, p.stat().st_size)
        return (1, p.stat().st_size)

    source_files.sort(key=_priority)

    for fpath in source_files[:40]:
        if total >= max_chars:
            break
        try:
            content = fpath.read_text(errors="ignore")
            rel = fpath.relative_to(root)
            remaining = max_chars - total
            excerpt = content[: min(4_000, remaining)]
            sec = f"## {rel}\n```{fpath.suffix[1:]}\n{excerpt}\n```\n"
            sections.append(sec)
            total += len(sec)
        except Exception:
            pass

    return "\n".join(sections)


def _build_system_prompt(
    language: str = "",
    challenge_type: str = "",
    codebase_context: str = "",
) -> str:
    context_lines = []
    if language:
        context_lines.append(f"- Programming language: {language}")
    if challenge_type:
        context_lines.append(f"- Challenge type: {challenge_type}")

    context_block = ""
    if context_lines:
        context_block = "\nInterview context:\n" + "\n".join(context_lines) + "\n"

    codebase_block = ""
    if codebase_context:
        codebase_block = (
            "\n\n---\nThe interview is about the following codebase. "
            "Use it to answer questions about the code directly.\n\n"
            + codebase_context
        )

    return f"""\
You are a silent assistant helping during a live technical interview. The user needs to answer NOW.
{context_block}
Rules — follow strictly:
- Give ONLY what the user should say or do. Nothing else.
- For behavioral questions: 3-5 bullet points max. Each bullet = one thing to say. No frameworks, no section headers, no tips.
- For coding questions: working code first, then 1-2 sentences on the approach. Time/space complexity on one line.
- Never use headers like "Answer Framework", "Key Tips", "Suggested Response". Just the answer.
- Never explain what you're doing. Never add "want me to tailor this?" or similar.
- Mobile screen width. Short lines. Dense.{codebase_block}"""


# ---------------------------------------------------------------------------
# Persisted config (stored in ~/.config/ai-interview/config.json)
# ---------------------------------------------------------------------------

def load_saved_config() -> dict:
    """Load saved config. Returns {} if not found."""
    try:
        return json.loads(CONFIG_FILE.read_text())
    except Exception:
        return {}


def save_config(data: dict) -> None:
    """Save config dict to disk."""
    CONFIG_FILE.parent.mkdir(parents=True, exist_ok=True)
    CONFIG_FILE.write_text(json.dumps(data, indent=2))


# ---------------------------------------------------------------------------
# Runtime config dataclass
# ---------------------------------------------------------------------------

GEMINI_MODELS = {
    "flash": "gemini-2.5-flash",
    "pro": "gemini-2.5-pro",
}


@dataclass
class Config:
    anthropic_api_key: str = ""
    google_api_key: str = ""
    deepgram_api_key: str = ""
    port: int = 7890
    buffer_minutes: int = 10
    context_minutes: int = 5
    timeout_hours: int = 8
    claude_model: str = "claude-sonnet-4-6"
    gemini_model: str = "gemini-2.5-flash"
    max_tokens: int = 4096
    interview_language: str = ""
    challenge_type: str = ""
    transcription_language: str = "en"   # Deepgram language code, e.g. "en", "pt", "es"
    context_path: str = ""               # optional repo path for codebase context
    max_history_turns: int = 10
    preferred_provider: str = ""  # "claude", "gemini", or "" (auto-detect)
    system_prompt: str = field(default="")

    @property
    def provider(self) -> str:
        """Resolve active provider: preferred > auto-detect from keys."""
        if self.preferred_provider == "claude" and self.anthropic_api_key:
            return "claude"
        if self.preferred_provider == "gemini" and self.google_api_key:
            return "gemini"
        # Auto-detect fallback
        if self.anthropic_api_key:
            return "claude"
        if self.google_api_key:
            return "gemini"
        return "none"

    def __post_init__(self):
        if not self.system_prompt:
            codebase = read_codebase_context(self.context_path) if self.context_path else ""
            self.system_prompt = _build_system_prompt(
                self.interview_language, self.challenge_type, codebase
            )

    @classmethod
    def from_saved(cls, overrides: Optional[dict] = None) -> "Config":
        """Build Config from saved file, with optional CLI overrides."""
        saved = load_saved_config()
        merged = {**saved, **(overrides or {})}

        anthropic_key = (merged.get("anthropic_api_key") or os.environ.get("ANTHROPIC_API_KEY", "")).strip()
        google_key = (merged.get("google_api_key") or os.environ.get("GOOGLE_API_KEY", "")).strip()
        deepgram_key = merged.get("deepgram_api_key", "").strip()
        language = merged.get("interview_language", "")
        challenge = merged.get("challenge_type", "")
        model_alias = merged.get("claude_model", "sonnet")
        claude_model = CLAUDE_MODELS.get(model_alias, model_alias)
        gemini_alias = merged.get("gemini_model", "flash")
        gemini_model = GEMINI_MODELS.get(gemini_alias, gemini_alias)
        transcription_language = merged.get("transcription_language", "en")
        context_path = merged.get("context_path", "")

        return cls(
            anthropic_api_key=anthropic_key,
            google_api_key=google_key,
            deepgram_api_key=deepgram_key,
            port=int(merged.get("port", 7890)),
            buffer_minutes=int(merged.get("buffer_minutes", 10)),
            timeout_hours=int(merged.get("timeout_hours", 8)),
            claude_model=claude_model,
            gemini_model=gemini_model,
            interview_language=language,
            challenge_type=challenge,
            transcription_language=transcription_language,
            context_path=context_path,
            preferred_provider=merged.get("preferred_provider", ""),
        )
