"""Dashboard screen — start interview form OR live status when running."""

from __future__ import annotations

import json
import os
import signal
import subprocess
import sys
import time
import urllib.request

import flet as ft

from ai_interview.i18n import t
from ai_interview.config import (
    CLAUDE_MODELS,
    GEMINI_MODELS,
    load_saved_config,
    save_config,
)
from ai_interview.daemon import is_process_running, kill_existing, read_meta, read_pid

TEAL = "#4ec9b0"
ERROR_RED = "#f14c4c"
MUTED = "#8b949e"
SZ = 13  # consistent field text size


_CHALLENGE_TYPES = [
    ("code_challenge", t("code_challenge")),
    ("system_design", t("system_design")),
    ("general", t("general_behavioural")),
]

_LANGUAGES = ["en", "es", "pt", "fr", "de", "it", "ja", "ko", "zh", "auto"]

_BRIEF_HINTS = {
    "code_challenge": t("hint_code_challenge"),
    "system_design": t("hint_system_design"),
    "general": t("hint_general"),
}


def _section(title: str) -> ft.Text:
    return ft.Text(title, size=12, weight=ft.FontWeight.W_600, color=MUTED)


class DashboardScreen(ft.Column):
    """Shows start form when idle, live status when interview is running."""

    def __init__(self, page: ft.Page, saved_config: dict):
        super().__init__(
            expand=True,
            scroll=ft.ScrollMode.AUTO,
            spacing=20,
            horizontal_alignment=ft.CrossAxisAlignment.STRETCH,
        )
        self._page = page
        self._config = saved_config
        self._running = False

        # ---- Interview Context ----
        self._lang_field = ft.TextField(
            label=t("programming_language"),
            value=saved_config.get("interview_language", "Python"),
            hint_text="Python, Java, Go...",
            border_radius=8, text_size=SZ, expand=True,
        )
        self._challenge_dropdown = ft.Dropdown(
            label=t("challenge_type"),
            value=saved_config.get("challenge_type", "code_challenge"),
            options=[ft.dropdown.Option(key=k, text=label) for k, label in _CHALLENGE_TYPES],
            border_radius=8, text_size=SZ, expand=True,
            on_select=self._on_challenge_change,
        )
        challenge = saved_config.get("challenge_type", "code_challenge")
        self._brief_field = ft.TextField(
            label=t("interview_brief"),
            hint_text=_BRIEF_HINTS.get(challenge, _BRIEF_HINTS["code_challenge"]),
            border_radius=8, text_size=SZ, expand=True,
            multiline=True, min_lines=2, max_lines=5,
        )
        self._context_path = ft.TextField(
            label=t("codebase_path"),
            value=saved_config.get("context_path", ""),
            hint_text="/path/to/repo (optional)",
            border_radius=8, text_size=SZ, expand=True,
        )
        self._file_picker = ft.FilePicker()
        page.services.append(self._file_picker)
        browse_btn = ft.IconButton(
            icon=ft.Icons.FOLDER_OPEN, tooltip="Browse",
            on_click=self._browse_path,
        )

        # ---- AI Provider ----
        provider_options = [ft.dropdown.Option("local", t("ollama_free"))]
        if saved_config.get("anthropic_api_key"):
            provider_options.append(ft.dropdown.Option("claude", "Claude"))
        if saved_config.get("google_api_key"):
            provider_options.append(ft.dropdown.Option("gemini", t("gemini_free")))

        self._provider_dropdown = ft.Dropdown(
            label=t("provider"),
            value=saved_config.get("preferred_provider", "local") or "local",
            options=provider_options,
            border_radius=8, text_size=SZ, expand=True,
            on_select=self._on_provider_change,
        )
        self._model_dropdown = ft.Dropdown(
            label=t("model"),
            border_radius=8, text_size=SZ, expand=True,
            on_select=self._on_model_change,
        )
        self._model_warning = ft.Text("", size=11, color=ERROR_RED, visible=False)
        self._update_model_options()
        self._update_model_warning()

        # ---- Transcription ----
        transcriber_options = [ft.dropdown.Option("whisper", t("whisper_free"))]
        if saved_config.get("deepgram_api_key"):
            transcriber_options.append(ft.dropdown.Option("deepgram", "Deepgram"))

        self._transcriber_dropdown = ft.Dropdown(
            label=t("transcriber"),
            value=saved_config.get("preferred_transcriber", "whisper") or "whisper",
            options=transcriber_options,
            border_radius=8, text_size=SZ, expand=True,
            on_select=self._on_transcriber_change,
        )
        self._trans_lang = ft.Dropdown(
            label=t("language"),
            value=saved_config.get("transcription_language", "auto"),
            options=[ft.dropdown.Option(l) for l in _LANGUAGES],
            border_radius=8, text_size=SZ, expand=True,
        )
        self._update_trans_lang_options()

        # ---- Start button ----
        self._start_btn = ft.ElevatedButton(
            t("start_interview"), icon=ft.Icons.PLAY_ARROW,
            bgcolor=TEAL, color="white", on_click=self._start_interview, height=42,
        )
        self._start_status = ft.Text("", size=12)

        self._lang_field.visible = challenge in ("code_challenge", "system_design")
        self._codebase_row = ft.Row(
            [self._context_path, browse_btn], spacing=8,
            visible=challenge in ("code_challenge", "system_design"),
        )

        context_card = ft.Card(
            content=ft.Container(
                content=ft.Column([
                    _section(t("interview_context")),
                    ft.Row([self._challenge_dropdown, self._lang_field], spacing=12),
                    self._brief_field,
                    self._codebase_row,
                ], spacing=10),
                padding=24,
            ),
        )

        provider_card = ft.Card(
            content=ft.Container(
                content=ft.Column([
                    _section(t("ai_provider")),
                    ft.Row([self._provider_dropdown, self._model_dropdown], spacing=12),
                    self._model_warning,
                ], spacing=10),
                padding=24,
            ),
        )

        transcription_card = ft.Card(
            content=ft.Container(
                content=ft.Column([
                    _section(t("transcription")),
                    ft.Row([self._transcriber_dropdown, self._trans_lang], spacing=12),
                ], spacing=10),
                padding=24,
            ),
        )

        # ---- CV toggle ----
        cv_path = saved_config.get("cv_path", "")
        cv_name = os.path.basename(cv_path) if cv_path else ""
        self._cv_check = ft.Checkbox(
            label=f"Include CV: {cv_name}" if cv_name else "No CV attached (set in Settings)",
            value=bool(cv_name),
            disabled=not bool(cv_name),
        )

        self._start_form = ft.Column([
            context_card,
            provider_card,
            transcription_card,
            self._cv_check,
            ft.Row([self._start_btn, self._start_status], spacing=12),
        ], spacing=12)

        # ---- Running status view ----
        self._status_icon = ft.Icon(ft.Icons.CIRCLE, color=ft.Colors.OUTLINE, size=20)
        self._status_text = ft.Text("Checking...", size=15, weight=ft.FontWeight.W_500)
        self._status_detail = ft.Text("", size=12, color=MUTED)

        self._info_rows: list[ft.Row] = []
        self._info_labels = {}
        for key, icon, label in [
            ("provider", ft.Icons.CLOUD_OUTLINED, t("provider")),
            ("model", ft.Icons.SMART_TOY_OUTLINED, t("model")),
            ("transcriber", ft.Icons.MIC_NONE, t("transcriber")),
            ("language", ft.Icons.CODE, t("language_label")),
            ("transcription", ft.Icons.TRANSLATE, t("transcription_label")),
            ("uptime", ft.Icons.TIMER_OUTLINED, t("uptime_label")),
            ("codebase", ft.Icons.FOLDER_OUTLINED, t("codebase_label")),
        ]:
            val = ft.Text("—", size=SZ)
            self._info_labels[key] = val
            self._info_rows.append(
                ft.Row([
                    ft.Icon(icon, size=16, color=MUTED),
                    ft.Text(label, size=12, color=MUTED, width=90),
                    val,
                ], spacing=8)
            )

        self._stop_btn = ft.ElevatedButton(
            t("stop"), icon=ft.Icons.STOP, bgcolor=ERROR_RED, color="white",
            on_click=self._stop_interview,
        )
        self._restart_btn = ft.OutlinedButton(
            t("restart"), icon=ft.Icons.REFRESH, on_click=self._restart_interview,
        )

        self._running_card = ft.Card(
            content=ft.Container(
                content=ft.Column([
                    ft.Row([self._status_icon, self._status_text], spacing=8),
                    self._status_detail,
                    ft.Divider(height=1),
                    *self._info_rows,
                    ft.Divider(height=1),
                    ft.Row([self._stop_btn, self._restart_btn], spacing=12),
                ], spacing=6),
                padding=24,
            ),
        )

        self._build_layout()

    # ---- Lifecycle ----

    def on_activate(self):
        """Reload config from disk when navigating to this screen."""
        fresh = load_saved_config()
        self._config.update(fresh)

        self._lang_field.value = fresh.get("interview_language", "Python")
        self._challenge_dropdown.value = fresh.get("challenge_type", "code_challenge")
        self._context_path.value = fresh.get("context_path", "")
        self._trans_lang.value = fresh.get("transcription_language", "auto")

        provider_options = [ft.dropdown.Option("local", t("ollama_free"))]
        if fresh.get("anthropic_api_key"):
            provider_options.append(ft.dropdown.Option("claude", "Claude"))
        if fresh.get("google_api_key"):
            provider_options.append(ft.dropdown.Option("gemini", t("gemini_free")))
        self._provider_dropdown.options = provider_options
        self._provider_dropdown.value = fresh.get("preferred_provider", "local") or "local"

        transcriber_options = [ft.dropdown.Option("whisper", t("whisper_free"))]
        if fresh.get("deepgram_api_key"):
            transcriber_options.append(ft.dropdown.Option("deepgram", "Deepgram"))
        self._transcriber_dropdown.options = transcriber_options
        self._transcriber_dropdown.value = fresh.get("preferred_transcriber", "whisper") or "whisper"
        self._update_trans_lang_options()

        # Refresh CV checkbox
        cv_path = fresh.get("cv_path", "")
        cv_name = os.path.basename(cv_path) if cv_path else ""
        self._cv_check.label = f"Include CV: {cv_name}" if cv_name else "No CV attached (set in Settings)"
        self._cv_check.value = bool(cv_name)
        self._cv_check.disabled = not bool(cv_name)

        self._update_model_options()
        try:
            self._page.update()
        except Exception:
            pass

    def _build_layout(self):
        self.controls = [
            ft.Container(
                content=ft.Column([
                    ft.Text(t("dashboard"), size=22, weight=ft.FontWeight.W_600),
                    self._running_card if self._running else self._start_form,
                ], spacing=16, horizontal_alignment=ft.CrossAxisAlignment.STRETCH),
                padding=30,
            ),
        ]

    @staticmethod
    def _get_installed_models() -> list[str]:
        """Return list of installed Ollama models (local or remote)."""
        from ai_interview.ollama_utils import get_installed_models
        host = load_saved_config().get("ollama_host", "http://localhost:11434")
        return get_installed_models(host=host)

    _LOCAL_MODELS = [
        "qwen2.5-coder:3b", "qwen2.5-coder:7b", "qwen2.5-coder:14b", "qwen2.5-coder:32b",
        "deepseek-coder-v2:16b", "codellama:7b", "codellama:13b",
        "llama3.2-vision:11b", "moondream:1.8b",
    ]
    _MODEL_SPEED_KEY = {
        "qwen2.5-coder:3b": "speed_fastest",
        "qwen2.5-coder:7b": "speed_fast",
        "qwen2.5-coder:14b": "speed_balanced",
        "qwen2.5-coder:32b": "speed_slow",
        "deepseek-coder-v2:16b": "speed_balanced",
        "codellama:7b": "speed_fast",
        "codellama:13b": "speed_balanced",
        "llama3.2-vision:11b": "speed_balanced",
        "moondream:1.8b": "speed_fast",
    }

    def _update_model_options(self):
        prov = self._provider_dropdown.value
        config = self._config
        if prov == "local":
            models = self._get_installed_models()
            if not models:
                models = ["(no models — download in Settings)"]
            default = config.get("local_model", "qwen2.5-coder:3b")
        elif prov == "claude":
            models = list(CLAUDE_MODELS.keys())
            default = config.get("claude_model", "sonnet")
        elif prov == "gemini":
            models = list(GEMINI_MODELS.keys())
            default = config.get("gemini_model", "flash")
        else:
            models = self._get_installed_models() or ["(no models)"]
            default = config.get("local_model", "qwen2.5-coder:3b")

        self._model_dropdown.options = [
            ft.dropdown.Option(key=m, text=self._model_display(m))
            for m in models
        ]
        self._model_dropdown.value = default if default in models else models[0]

    def _update_trans_lang_options(self):
        """Hide 'auto' language when Deepgram is selected (requires explicit language)."""
        is_deepgram = self._transcriber_dropdown.value == "deepgram"
        langs = [l for l in _LANGUAGES if l != "auto"] if is_deepgram else _LANGUAGES
        self._trans_lang.options = [ft.dropdown.Option(l) for l in langs]
        if is_deepgram and self._trans_lang.value == "auto":
            self._trans_lang.value = "en"

    def _on_transcriber_change(self, e):
        self._update_trans_lang_options()
        self._page.update()

    def _on_challenge_change(self, e):
        challenge = self._challenge_dropdown.value or "code_challenge"
        self._brief_field.hint_text = _BRIEF_HINTS.get(challenge, _BRIEF_HINTS["code_challenge"])
        self._lang_field.visible = challenge in ("code_challenge", "system_design")
        if challenge not in ("code_challenge", "system_design"):
            self._lang_field.value = ""
        show_codebase = challenge in ("code_challenge", "system_design")
        self._codebase_row.visible = show_codebase
        if not show_codebase:
            self._context_path.value = ""
        self._page.update()

    def _on_provider_change(self, e):
        self._update_model_options()
        self._update_model_warning()
        self._page.update()

    def _on_model_change(self, e):
        self._update_model_warning()
        self._page.update()

    @staticmethod
    def _is_vision_model(model: str) -> bool:
        _vision = {"llava", "llama3.2-vision", "moondream", "qwen2-vl", "minicpm-v", "bakllava"}
        base = model.split(":")[0].lower()
        return base in _vision or "vision" in base

    def _model_display(self, model: str) -> str:
        """Build display label for a model: 'name (speed, vision)'."""
        parts = []
        if model in self._MODEL_SPEED_KEY:
            parts.append(t(self._MODEL_SPEED_KEY[model]))
        if self._is_vision_model(model):
            parts.append(t("vision_tag"))
        return f"{model} ({', '.join(parts)})" if parts else model

    def _update_model_warning(self):
        prov = self._provider_dropdown.value
        model = self._model_dropdown.value or ""
        # Only show warning for local (Ollama) provider with non-vision models
        if prov == "local" and model and not self._is_vision_model(model):
            self._model_warning.value = t("no_screenshot_support")
            self._model_warning.visible = True
        else:
            self._model_warning.visible = False

    async def _browse_path(self, e):
        path = await self._file_picker.get_directory_path(
            dialog_title="Select Codebase Directory",
        )
        if path:
            self._context_path.value = path
            self._page.update()

    # ---- Actions ----

    def _start_interview(self, e):
        prov = self._provider_dropdown.value
        model = self._model_dropdown.value or ""
        is_local = prov == "local"

        if is_local and model:
            # Start ollama, refresh cache, verify model
            from ai_interview.ollama_utils import get_installed_models
            host = load_saved_config().get("ollama_host", "http://localhost:11434")
            installed = get_installed_models(start_server=True, host=host)
            if model not in installed:
                self._start_status.value = f"Model {model} not available. Download it in Settings."
                self._start_status.color = "#f14c4c"
                self._page.update()
                return

        self._start_btn.disabled = True
        self._start_status.value = t("starting")
        self._start_status.color = TEAL
        self._page.update()

        def _do_start():
            try:
                config = load_saved_config()

                config["interview_language"] = self._lang_field.value or ""
                config["challenge_type"] = self._challenge_dropdown.value or "code_challenge"
                config["transcription_language"] = self._trans_lang.value or "auto"
                config["context_path"] = (self._context_path.value or "").strip()

                # Per-session CV toggle — don't permanently clear the path
                if not self._cv_check.value:
                    config["_session_cv_path"] = ""
                else:
                    config["_session_cv_path"] = config.get("cv_path", "")

                prov = self._provider_dropdown.value
                config["preferred_provider"] = "" if prov == "auto" else prov

                model = self._model_dropdown.value
                effective_prov = prov
                if prov == "auto":
                    if config.get("anthropic_api_key"):
                        effective_prov = "claude"
                    elif config.get("google_api_key"):
                        effective_prov = "gemini"

                if effective_prov == "local":
                    config["local_model"] = model
                elif effective_prov == "gemini":
                    config["gemini_model"] = model
                else:
                    config["claude_model"] = model

                trans = self._transcriber_dropdown.value
                config["preferred_transcriber"] = "" if trans == "auto" else trans

                save_config(config)
                self._config.update(config)

                pid = read_pid()
                if pid and is_process_running(pid):
                    kill_existing()
                    time.sleep(0.5)

                # Use the CLI binary (correct venv/shebang/arch/permissions)
                # Config is already saved — daemon reads it via Config.from_saved()
                from pathlib import Path as _P
                _cli = _P(sys.executable).parent / "ai-interview"
                if not _cli.exists():
                    import shutil as _sh
                    _found = _sh.which("ai-interview")
                    _cli = _P(_found) if _found else _P("ai-interview")

                cmd = [str(_cli), "start", "--daemon"]
                if model and effective_prov not in ("gemini", "local"):
                    cmd += ["--model", model]

                subprocess.Popen(
                    cmd, start_new_session=True,
                    stdout=subprocess.DEVNULL, stderr=subprocess.DEVNULL, stdin=subprocess.DEVNULL,
                )

                # Poll health endpoint — more reliable than PID file
                port = int(self._config.get("port", 7890))
                for _ in range(30):
                    time.sleep(1)
                    try:
                        req = urllib.request.Request(f"http://127.0.0.1:{port}/health")
                        urllib.request.urlopen(req, timeout=1)
                        # Daemon is up — also start watchdog
                        subprocess.Popen(
                            [str(_cli), "_watchdog"],
                            start_new_session=True,
                            stdout=subprocess.DEVNULL, stderr=subprocess.DEVNULL, stdin=subprocess.DEVNULL,
                        )
                        # Fetch interview info immediately
                        try:
                            import json as _json
                            req2 = urllib.request.Request(f"http://127.0.0.1:{port}/interview-info")
                            with urllib.request.urlopen(req2, timeout=2) as resp:
                                info = _json.loads(resp.read().decode())
                            self.update_status(True, info)
                        except Exception:
                            self._running = True
                            self._build_layout()
                        try:
                            self._page.update()
                        except Exception:
                            pass
                        return
                    except Exception:
                        pass

                self._start_status.value = t("failed_to_start")
                self._start_status.color = ERROR_RED
            except Exception as exc:
                self._start_status.value = f"Error: {exc}"
                self._start_status.color = ERROR_RED
            finally:
                self._start_btn.disabled = False
                try:
                    self._page.update()
                except Exception:
                    pass

        self._page.run_thread(_do_start)

    def _stop_interview(self, e):
        self._stop_btn.disabled = True
        self._restart_btn.disabled = True
        self._status_text.value = t("stopping")
        self._status_icon.color = "#d29922"
        self._page.update()

        def _do_stop():
            from ai_interview.config import WATCHDOG_PID_FILE
            if WATCHDOG_PID_FILE.exists():
                try:
                    wpid = int(WATCHDOG_PID_FILE.read_text().strip())
                    os.kill(wpid, signal.SIGTERM)
                except Exception:
                    pass
                WATCHDOG_PID_FILE.unlink(missing_ok=True)

            kill_existing()
            time.sleep(0.5)
            self._running = False
            self._stop_btn.disabled = False
            # Clear per-session fields from UI and config
            self._brief_field.value = ""
            self._context_path.value = ""
            self._start_status.value = ""
            config = load_saved_config()
            config["context_path"] = ""
            save_config(config)
            self._config.update(config)
            self._build_layout()
            try:
                self._page.update()
            except Exception:
                pass

        self._page.run_thread(_do_stop)

    def _restart_interview(self, e):
        self._restart_btn.disabled = True
        self._status_detail.value = t("restarting")
        self._page.update()

        def _do_restart():
            try:
                port = int(self._config.get("port", 7890))
                req = urllib.request.Request(f"http://127.0.0.1:{port}/restart", method="POST")
                urllib.request.urlopen(req, timeout=3)
            except Exception:
                pass
            time.sleep(2)
            self._restart_btn.disabled = False
            try:
                self._page.update()
            except Exception:
                pass

        self._page.run_thread(_do_restart)

    # ---- Status updates from polling ----

    def update_status(self, running: bool, info: dict):
        was_running = self._running
        self._running = running

        if running:
            self._status_icon.color = TEAL
            self._status_icon.name = ft.Icons.CHECK_CIRCLE
            self._status_text.value = t("interview_in_progress")

            self._info_labels["provider"].value = info.get("provider", "—").capitalize()
            self._info_labels["model"].value = info.get("model", "—")
            self._info_labels["transcriber"].value = info.get("transcriber", "—")
            self._info_labels["language"].value = info.get("interview_language", "—")
            self._info_labels["transcription"].value = info.get("transcription_language", "—")

            uptime = info.get("uptime", 0)
            mins, secs = divmod(uptime, 60)
            self._info_labels["uptime"].value = f"{mins}m {secs}s"

            ctx = info.get("context_path", "")
            self._info_labels["codebase"].value = ctx if ctx else "—"
            self._status_detail.value = ""
        else:
            self._status_icon.color = ft.Colors.OUTLINE
            self._status_icon.name = ft.Icons.CIRCLE
            self._status_text.value = t("no_interview")
            self._status_detail.value = ""

        if running != was_running:
            self._build_layout()
