from __future__ import annotations

from dataclasses import dataclass, field
from datetime import date, timedelta
from string.templatelib import Template

from markupsafe import Markup
from pyview import LiveView, LiveViewSocket, Depends, is_connected
from pyview.events import AutoEventDispatch, event, info, InfoEvent
from pyview.meta import PyViewMeta
from pyview.template import TemplateView

from ...data.models import SessionMetadata
from ...data.search import SearchResult
from ...data.session_store import SessionIndex, get_index


@dataclass
class SessionListContext:
    all_sessions: list[SessionMetadata] = field(default_factory=list)
    project_names: list[str] = field(default_factory=list)
    search_query: str = ""
    selected_project: str = ""  # empty = all projects
    loading: bool = True
    search_results: dict[str, SearchResult] | None = None


class SessionListView(AutoEventDispatch, TemplateView, LiveView[SessionListContext]):
    async def mount(self, socket: LiveViewSocket[SessionListContext], session, index: SessionIndex = Depends(get_index)):
        if index.loaded:
            socket.context = SessionListContext(
                all_sessions=index.all_sessions,
                project_names=index.project_names,
                loading=False,
            )
        else:
            socket.context = SessionListContext(loading=True)
            if is_connected(socket):
                socket.schedule_info_once(InfoEvent("check_loaded"), 0.2)

    @info("check_loaded")
    async def handle_check_loaded(self, event: InfoEvent, socket: LiveViewSocket[SessionListContext]):
        index = get_index()
        if index.loaded:
            socket.context = SessionListContext(
                all_sessions=index.all_sessions,
                project_names=index.project_names,
                loading=False,
            )
        else:
            socket.schedule_info_once(InfoEvent("check_loaded"), 0.2)

    def _filtered_sessions(self, context: SessionListContext) -> list[SessionMetadata]:
        sessions = context.all_sessions

        if context.selected_project:
            sessions = [s for s in sessions if s.project_short_name == context.selected_project]

        if context.search_query:
            index = get_index()
            si = index.search_index
            if si and si.ready:
                results = si.search(context.search_query, limit=100)
                result_map = {
                    f"{r.project_dir}/{r.session_id}": r for r in results
                }
                context.search_results = result_map

                # Filter to matching sessions, preserving relevance order
                session_lookup = {
                    f"{s.project_dir}/{s.session_id}": s for s in sessions
                }
                sessions = [
                    session_lookup[key]
                    for key in result_map
                    if key in session_lookup
                ]
            else:
                # Fallback to substring matching while index builds
                q = context.search_query.lower()
                sessions = [
                    s for s in sessions
                    if q in (s.summary or "").lower()
                    or q in (s.first_message_preview or "").lower()
                    or q in (s.git_branch or "").lower()
                ]
        else:
            context.search_results = None

        return sessions

    @event("search")
    async def handle_search(self, socket: LiveViewSocket[SessionListContext], query: str = "") -> None:
        socket.context.search_query = query

    @event("filter_project")
    async def handle_filter_project(self, socket: LiveViewSocket[SessionListContext], project: str = "") -> None:
        current = socket.context.selected_project
        # Toggle: clicking the active filter clears it
        socket.context.selected_project = "" if current == project else project

    def template(self, context: SessionListContext, meta: PyViewMeta) -> Template:
        if context.loading:
            return t"""<div class="loading">Loading sessions...</div>"""

        filtered = self._filtered_sessions(context)
        search_query = context.search_query
        selected_project = context.selected_project
        search_results = context.search_results

        total_count = str(len(context.all_sessions))
        result_count = str(len(filtered))

        project_chips = [self._render_project_chip(name, selected_project) for name in context.project_names]

        # Date-grouped sessions
        date_groups = self._group_by_date(filtered)
        session_elements = []
        for label, group_sessions in date_groups:
            session_elements.append(t"""<div class="date-separator">{label}</div>""")
            for s in group_sessions:
                key = f"{s.project_dir}/{s.session_id}"
                sr = search_results.get(key) if search_results else None
                session_elements.append(self._render_session_row(s, sr))

        return t"""<div class="session-list-app">
            <div class="header">
                <h1><span>&#10095;</span> claude sessions</h1>
                <div class="header-right">
                    <span class="session-count">{result_count} of {total_count} sessions</span>
                </div>
            </div>
            <div class="search-bar">
                <span class="search-icon">&#8981;</span>
                <form phx-change="search" phx-submit="search">
                    <input type="text" placeholder="Search sessions…" value="{search_query}" name="query" phx-debounce="200" />
                </form>
                <span class="kbd">/</span>
            </div>
            <div class="filters">
                {project_chips}
            </div>
            <div class="session-list">
                {session_elements}
            </div>
        </div>"""

    def _render_project_chip(self, project_name: str, selected: str) -> Template:
        is_selected = project_name == selected
        cls = "chip active" if is_selected else "chip"
        return t"""<span
            class="{cls}"
            phx-click="filter_project"
            phx-value-project="{project_name}"
        >{project_name}</span>"""

    def _render_session_row(self, s: SessionMetadata, search_result: SearchResult | None = None) -> Template:
        href = f"/sessions/{s.project_dir}/{s.session_id}"
        short_project = s.project_short_name

        has_summary = bool(s.summary)
        display_text = s.summary or s.first_message_preview or s.session_id
        display_text = display_text[:120]
        summary_cls = "session-summary" if has_summary else "session-summary empty"

        branch_tag = self._render_branch_tag(s.git_branch)
        msg_count = str(s.message_count)
        duration = s.duration_display
        token_el = self._render_token_count(s.token_display)
        sparkline = self._render_sparkline(s.sparkline_buckets)
        time_display = s.time_display
        highlights_html = self._render_highlights(search_result) if search_result else t""""""

        duration_el = t"""<span class="meta-sep">&middot;</span>
            <span class="meta-item">{duration}</span>""" if duration else t""""""

        token_sep = t"""<span class="meta-sep">&middot;</span>""" if s.token_display else t""""""

        return t"""<a class="session-row" href="{href}" data-phx-link="redirect" data-phx-link-state="push">
            <span class="session-project">{short_project}</span>
            <div class="session-content">
                <div class="{summary_cls}">{display_text}</div>
                {highlights_html}
                <div class="session-meta">
                    {branch_tag}
                    <span class="meta-item">{msg_count} msgs</span>
                    {duration_el}
                    {token_sep}
                    {token_el}
                    {sparkline}
                </div>
            </div>
            <div class="session-right">
                <div class="session-time">{time_display}</div>
            </div>
        </a>"""

    def _render_branch_tag(self, branch: str) -> Template:
        if not branch:
            return t""""""
        return t"""<span class="branch-tag">{branch}</span>
            <span class="meta-sep">&middot;</span>"""

    def _render_token_count(self, display: str) -> Template:
        if not display:
            return t""""""
        return t"""<span class="token-count">{display}</span>"""

    def _render_sparkline(self, buckets: list[int]) -> Template:
        if not buckets:
            return t""""""
        max_val = max(buckets)
        if max_val == 0:
            return t""""""
        bars = []
        for count in buckets:
            height = str(max(1, round((count / max_val) * 12)))
            bars.append(t"""<div class="bar" style="height:{height}px"></div>""")
        return t"""<div class="sparkline">{bars}</div>"""

    def _render_highlights(self, result: SearchResult) -> Template:
        if not result.highlights:
            return t""""""
        highlight_items = [self._render_highlight_item(h) for h in result.highlights]
        return t"""<div class="search-highlights">
            {highlight_items}
        </div>"""

    def _render_highlight_item(self, highlight) -> Template:
        label = highlight.field_label
        snippet_html = Markup(highlight.snippet_html)
        return t"""<div class="search-highlight">
            <span class="highlight-field">{label}:</span>
            <span class="highlight-snippet">{snippet_html}</span>
        </div>"""

    def _group_by_date(self, sessions: list[SessionMetadata]) -> list[tuple[str, list[SessionMetadata]]]:
        if not sessions:
            return []

        today = date.today()
        yesterday = today - timedelta(days=1)
        groups: list[tuple[str, list[SessionMetadata]]] = []
        current_label = ""
        current_group: list[SessionMetadata] = []

        for s in sessions:
            if not s.timestamp:
                d = date.min
            else:
                d = s.timestamp.date()

            if d == today:
                label = f"Today \u2014 {d.strftime('%b %-d')}"
            elif d == yesterday:
                label = f"Yesterday \u2014 {d.strftime('%b %-d')}"
            else:
                label = d.strftime("%b %-d")

            if label != current_label:
                if current_group:
                    groups.append((current_label, current_group))
                current_label = label
                current_group = [s]
            else:
                current_group.append(s)

        if current_group:
            groups.append((current_label, current_group))

        return groups
