"""Profile screen: view/edit profile, stats, link accounts, send feedback."""

from textual import work
from textual.app import ComposeResult
from textual.binding import Binding
from textual.containers import Horizontal, Vertical
from rich.markup import escape
from textual.widgets import Button, Input, Static, SelectionList
from textual.widgets.selection_list import Selection

from skilark_cli.client import SkilarkClient
from skilark_cli.config_store import ConfigStore
from skilark_cli.display import _progress_bar, _topic_bars
from skilark_cli.tui.errors import friendly_error_message

_MAX_DESCRIPTION_WORDS = 150


class ProfilePane(Vertical, can_focus=True):
    """Profile view/edit, stats, link accounts, feedback.

    can_focus=True is needed so self.focus() works after closing edit/feedback,
    reclaiming focus for keybindings (e/l/f). SignalsPane doesn't need this
    because it delegates focus to its DataTable child.
    """

    BINDINGS = [
        Binding("e", "toggle_edit", "Edit", show=True),
        Binding("i", "edit_interests", "Interests", show=True),
        Binding("l", "link_account", "Link", show=True),
        Binding("f", "send_feedback", "Feedback", show=True),
    ]

    def __init__(
        self,
        client: SkilarkClient | None = None,
        config_store: ConfigStore | None = None,
        **kwargs,
    ) -> None:
        super().__init__(**kwargs)
        self.client = client
        self._config_store = config_store
        self._profile: dict = {}
        self._stats: dict = {}
        self._editing: bool = False
        self._picking_interests: bool = False
        self._interests_nudged: bool = False
        self._interest_presets: list[dict] = []

    def compose(self) -> ComposeResult:
        yield Static("", id="profile-error")
        yield Static("Loading profile...", id="profile-loading")
        with Vertical(id="profile-content"):
            # --- Profile info with inline edit ---
            with Vertical(id="profile-info"):
                yield Static("[bold]Your Profile[/bold]", id="profile-header")
                yield Static("", id="profile-identity")
                # Self-description (Job Match)
                with Horizontal(classes="profile-field profile-field-description"):
                    yield Static("[dim]Job Match[/dim]", classes="field-label")
                    yield Static("", id="profile-val-description", classes="field-value")
                    yield Input(
                        placeholder="Your background, skills, and experience (150 words max)",
                        id="profile-edit-description",
                    )
                yield Static(
                    "",
                    id="profile-description-hint",
                )
                yield Static(
                    "[dim]Describe yourself here — used to match you with relevant jobs[/dim]",
                    id="profile-description-context",
                )
                # Interests (read-only, replaces old Topics display)
                with Horizontal(classes="profile-field"):
                    yield Static("[dim]Interests[/dim]", classes="field-label")
                    yield Static("", id="profile-val-topics", classes="field-value")
                # Interest picker (inline, hidden by default)
                with Vertical(id="interest-picker"):
                    yield Static(
                        "[bold]Select your interests[/bold]  "
                        "[dim]Space to toggle, then Save[/dim]",
                        id="interest-picker-header",
                    )
                    with Horizontal(id="interest-preset-bar"):
                        yield Static(
                            "[dim]Quick start:[/dim] ",
                            id="interest-preset-label",
                        )
                    yield SelectionList[str](id="interest-list")
                    with Horizontal(id="interest-picker-actions"):
                        yield Button(
                            "Save", id="interest-save", variant="primary"
                        )
                        yield Button(
                            "Cancel", id="interest-cancel", variant="default"
                        )
                # Edit actions
                yield Static(
                    "[dim]Save when done, Escape to cancel[/dim]",
                    id="profile-edit-hint",
                )
                yield Button("Save", id="profile-edit-save", variant="primary")

            yield Static("", id="profile-stats")
            yield Static("", id="profile-link-code")
            yield Input(placeholder="Type your feedback...", id="profile-feedback-input")
            yield Static("", id="profile-feedback-status")

    def on_mount(self) -> None:
        self.query_one("#profile-error").display = False
        self.query_one("#profile-identity").display = False
        self.query_one("#profile-link-code").display = False
        self.query_one("#profile-feedback-input").display = False
        self.query_one("#profile-feedback-status").display = False
        self.query_one("#profile-content").display = False
        self._hide_edit_widgets()
        self.query_one("#interest-picker").display = False
        self.focus()
        self._fetch_data()

    def _hide_edit_widgets(self) -> None:
        """Hide all edit-mode widgets."""
        self.query_one("#profile-edit-description").display = False
        self.query_one("#profile-description-hint").display = False
        self.query_one("#profile-edit-hint").display = False
        self.query_one("#profile-edit-save").display = False

    def _show_edit_widgets(self) -> None:
        """Show edit-mode widgets, hide display values."""
        self.query_one("#profile-info").add_class("editing")
        self.query_one("#profile-val-description").display = False
        self.query_one("#profile-edit-description").display = True
        self.query_one("#profile-description-hint").display = True
        self.query_one("#profile-edit-hint").display = True
        self.query_one("#profile-edit-save").display = True

    def _show_display_widgets(self) -> None:
        """Show display values, hide edit widgets."""
        self.query_one("#profile-info").remove_class("editing")
        self.query_one("#profile-val-description").display = True
        self._hide_edit_widgets()

    @work(thread=True, exit_on_error=False)
    def _fetch_data(self) -> None:
        if not self.client:
            self.app.call_from_thread(self._show_error, "No API client configured.")
            return
        try:
            profile = self.client.get_profile() or {}
            stats = self.client.get_stats()
            self.app.call_from_thread(self._populate, profile, stats)
        except Exception as exc:
            self.app.call_from_thread(self._show_error, friendly_error_message(exc))

    def _show_error(self, message: str) -> None:
        if not self.is_attached:
            return
        self.query_one("#profile-loading").display = False
        error = self.query_one("#profile-error", Static)
        error.update(message)
        error.display = True

    def _populate(self, profile: dict, stats: dict) -> None:
        if not self.is_attached:
            return
        self._profile = profile
        self._stats = stats
        self.query_one("#profile-loading").display = False
        self.query_one("#profile-error").display = False
        self.query_one("#profile-content").display = True
        self._render_info()
        self._render_stats()

        # Nudge: if no interests, show one-time notification
        interests = profile.get("interests") or []
        if not interests and not self._interests_nudged:
            self._interests_nudged = True
            self.notify("Choose your interests to personalize Skilark! Press [bold]i[/bold] to get started.")

    def _render_info(self) -> None:
        description = self._config_store.get_self_description() if self._config_store else ""

        # Identity line: shown when display_name is available (OAuth sign-in)
        display_name = self._profile.get("display_name")
        if display_name:
            provider = self._profile.get("auth_provider", "")
            masked = self._profile.get("email_masked", "")
            identity = f"[bold]{escape(display_name)}[/bold]"
            if provider:
                identity += f" via {escape(provider)}"
            if masked:
                identity += f"  [dim]{escape(masked)}[/dim]"
            self.query_one("#profile-identity", Static).update(identity)
            self.query_one("#profile-identity", Static).display = True
        else:
            self.query_one("#profile-identity", Static).display = False

        desc_display = escape(description) if description else "[dim]Not set — describe yourself to power job matches[/dim]"
        self.query_one("#profile-val-description", Static).update(desc_display)

        # Interests (from API profile response, replaces old topics display)
        interests = self._profile.get("interests") or []
        if interests:
            by_cat: dict[str, list[str]] = {}
            for i in interests:
                cat = i.get("category", "other")
                by_cat.setdefault(cat, []).append(i["name"])
            parts = []
            for cat, names in by_cat.items():
                label = cat.replace("_", " ").title()
                parts.append(f"[dim]{label}:[/dim] {escape(', '.join(names))}")
            self.query_one("#profile-val-topics", Static).update("\n".join(parts))
        else:
            self.query_one("#profile-val-topics", Static).update(
                "[dim]No interests set[/dim]"
            )

    def _render_stats(self) -> None:
        streak = self._stats.get("streak", 0)
        total = self._stats.get("total_completed", 0)
        week_done = self._stats.get("week_completed", 0)
        week_goal = self._stats.get("week_goal", 10)
        topic_counts = self._stats.get("topic_counts") or {}

        week_bar = _progress_bar(week_done, week_goal, 10)

        lines = [
            f"[bold]Stats[/bold]  --  {streak} day streak",
            "",
            f"  [dim]This week[/dim]   {week_bar}  {week_done}/{week_goal}",
            f"  [dim]All time[/dim]    {total} challenges completed",
        ]

        topic_rows = _topic_bars(topic_counts)
        if topic_rows:
            lines.append("")
            max_label = max(len(t) for t, _, _ in topic_rows)
            for topic, bar, count in topic_rows:
                padding = " " * (max_label - len(topic))
                lines.append(f"  [dim]{escape(topic)}{padding}[/dim]  {bar}  {count:>3}")

        self.query_one("#profile-stats", Static).update("\n".join(lines))

    def action_toggle_edit(self) -> None:
        if self._editing:
            self._close_edit()
        else:
            description = self._config_store.get_self_description() if self._config_store else ""
            desc_input = self.query_one("#profile-edit-description", Input)
            desc_input.value = description
            self._update_description_hint(description)

            self._show_edit_widgets()
            self._editing = True
            desc_input.focus()

    def _close_edit(self) -> None:
        self._show_display_widgets()
        self._editing = False
        self.focus()

    def on_key(self, event) -> None:
        if event.key == "escape" and self._picking_interests:
            self._close_interest_picker()
            event.prevent_default()
            event.stop()
        elif event.key == "escape" and self._editing:
            self._close_edit()
            event.prevent_default()
            event.stop()
        elif event.key == "escape":
            feedback = self.query_one("#profile-feedback-input")
            if feedback.display:
                feedback.display = False
                self.focus()
                event.prevent_default()
                event.stop()

    def on_input_changed(self, event: Input.Changed) -> None:
        if event.input.id == "profile-edit-description":
            self._update_description_hint(event.value)

    def _update_description_hint(self, text: str) -> None:
        word_count = len(text.split()) if text.strip() else 0
        hint = self.query_one("#profile-description-hint", Static)
        if word_count > _MAX_DESCRIPTION_WORDS:
            hint.update(f"[red]{word_count}/{_MAX_DESCRIPTION_WORDS} words[/red]")
        else:
            hint.update(f"[dim]{word_count}/{_MAX_DESCRIPTION_WORDS} words[/dim]")

    def on_input_submitted(self, event: Input.Submitted) -> None:
        if event.input.id == "profile-feedback-input":
            self._submit_feedback()

    def on_button_pressed(self, event: Button.Pressed) -> None:
        if event.button.id == "profile-edit-save" and self._editing:
            self._save_profile()
        elif event.button.id == "interest-save" and self._picking_interests:
            self._save_interests()
        elif event.button.id == "interest-cancel" and self._picking_interests:
            self._close_interest_picker()
        elif (
            event.button.id
            and event.button.id.startswith("preset-")
            and self._picking_interests
        ):
            self._apply_preset(event.button.id)

    def _save_profile(self) -> None:
        description = self.query_one("#profile-edit-description", Input).value.strip()

        # Validate word count
        if description and len(description.split()) > _MAX_DESCRIPTION_WORDS:
            self._show_feedback_status(
                f"Description too long ({len(description.split())} words). "
                f"Max {_MAX_DESCRIPTION_WORDS} words."
            )
            return

        # Save description locally (not synced to API)
        if self._config_store:
            self._config_store.update_profile(self_description=description)

        self._close_edit()
        self._render_info()

    def action_link_account(self) -> None:
        self._generate_link_code()

    @work(thread=True, exit_on_error=False)
    def _generate_link_code(self) -> None:
        if not self.client:
            return
        try:
            result = self.client.create_link_code()
            code = result.get("code", "")
            self.app.call_from_thread(self._show_link_code, code)
        except Exception as exc:
            self.app.call_from_thread(self._show_link_error, friendly_error_message(exc))

    def _show_link_code(self, code: str) -> None:
        if not self.is_attached:
            return
        display = self.query_one("#profile-link-code", Static)
        display.update(f"Link code: {code}\nSend this to your other Skilark channel. Expires in 10 min.")
        display.display = True
        self.focus()

    def _show_link_error(self, message: str) -> None:
        if not self.is_attached:
            return
        display = self.query_one("#profile-link-code", Static)
        display.update(f"Failed to generate link code: {message}")
        display.display = True
        self.focus()

    def action_send_feedback(self) -> None:
        feedback = self.query_one("#profile-feedback-input", Input)
        feedback.value = ""
        feedback.display = True
        feedback.focus()
        self.query_one("#profile-feedback-status").display = False

    def _submit_feedback(self) -> None:
        feedback_input = self.query_one("#profile-feedback-input", Input)
        message = feedback_input.value.strip()
        if not message:
            return
        feedback_input.display = False
        self.focus()
        self._send_feedback_api(message)

    @work(thread=True, exit_on_error=False)
    def _send_feedback_api(self, message: str) -> None:
        if not self.client:
            return
        try:
            self.client.send_feedback(message)
            self.app.call_from_thread(self._show_feedback_status, "Thanks for your feedback!")
        except Exception:
            self.app.call_from_thread(self._show_feedback_status, "Failed to send feedback. Try again later.")

    def _show_feedback_status(self, text: str) -> None:
        if not self.is_attached:
            return
        status = self.query_one("#profile-feedback-status", Static)
        status.update(text)
        status.display = True

    # --- Interest picker ---

    def action_edit_interests(self) -> None:
        """Open the interest picker (fetches catalog first)."""
        if self._picking_interests or self._editing:
            return
        self._fetch_catalog_and_show_picker()

    @work(thread=True, exit_on_error=False)
    def _fetch_catalog_and_show_picker(self) -> None:
        if not self.client:
            self.app.call_from_thread(self._show_error, "No API client configured.")
            return
        try:
            catalog = self.client.get_interest_catalog()
            presets = self.client.get_interest_presets()
            current_interests = self._profile.get("interests") or []
            current_names = {i["name"] for i in current_interests}
            self.app.call_from_thread(
                self._show_interest_picker, catalog, presets, current_names
            )
        except Exception as exc:
            self.app.call_from_thread(self._show_error, friendly_error_message(exc))

    def _show_interest_picker(
        self,
        catalog: list[dict],
        presets: list[dict],
        current_names: set[str],
    ) -> None:
        if not self.is_attached:
            return

        self._interest_presets = presets
        self._picking_interests = True

        # Build preset buttons dynamically
        preset_bar = self.query_one("#interest-preset-bar", Horizontal)
        # Remove old preset buttons (keep label)
        for btn in preset_bar.query(Button):
            btn.remove()
        if presets and not current_names:
            for idx, preset in enumerate(presets):
                btn = Button(
                    preset.get("label", preset["name"]),
                    id=f"preset-{idx}",
                    variant="default",
                )
                preset_bar.mount(btn)
            preset_bar.display = True
        else:
            preset_bar.display = False

        # Populate the SelectionList grouped by category
        sel = self.query_one("#interest-list", SelectionList)
        sel.clear_options()

        # Group catalog by category
        by_cat: dict[str, list[dict]] = {}
        for item in catalog:
            cat = item.get("category", "other")
            by_cat.setdefault(cat, []).append(item)

        selections: list[Selection[str]] = []
        for cat in sorted(by_cat.keys()):
            cat_label = cat.replace("_", " ").title()
            # Add category separator as a disabled item
            selections.append(
                Selection(f"── {cat_label} ──", f"__cat__{cat}", False)
            )
            for item in sorted(by_cat[cat], key=lambda x: x["name"]):
                name = item["name"]
                selected = name in current_names
                selections.append(Selection(name, name, selected))

        sel.add_options(selections)

        # Show the picker
        self.query_one("#interest-picker").display = True
        sel.focus()

    def _apply_preset(self, button_id: str) -> None:
        """Apply a preset's interests to the SelectionList."""
        idx = int(button_id.split("-")[1])
        if idx >= len(self._interest_presets):
            return
        preset = self._interest_presets[idx]
        preset_names = set(preset.get("interest_names", []))

        sel = self.query_one("#interest-list", SelectionList)
        # Deselect all first, then select preset interests
        sel.deselect_all()
        for name in preset_names:
            try:
                sel.select(name)
            except Exception:
                pass  # Interest name not in catalog

        # Hide preset bar after use
        self.query_one("#interest-preset-bar").display = False
        sel.focus()

    def _close_interest_picker(self) -> None:
        """Hide the interest picker without saving."""
        self.query_one("#interest-picker").display = False
        self._picking_interests = False
        self.focus()

    def _save_interests(self) -> None:
        """Collect selected interests and save via API."""
        sel = self.query_one("#interest-list", SelectionList)
        selected_names = [
            v for v in sel.selected
            if isinstance(v, str) and not v.startswith("__cat__")
        ]
        self._save_interests_api(selected_names)

    @work(thread=True, exit_on_error=False)
    def _save_interests_api(self, names: list[str]) -> None:
        if not self.client:
            return
        try:
            updated = self.client.update_user_interests(names)
            self.app.call_from_thread(self._on_interests_saved, updated)
        except Exception as exc:
            self.app.call_from_thread(self._show_error, friendly_error_message(exc))

    def _on_interests_saved(self, updated: list[dict]) -> None:
        if not self.is_attached:
            return
        self._profile["interests"] = updated
        self._render_info()
        self._close_interest_picker()
