package web

import (
	"fmt"
	"strings"

	"gitea.d-ma.be/mathias/tapir/internal/adapters/store"
)

// Layout is the shared HTML shell. HTMX drives the progressive interactions
// (filters, action toggles); every interaction also degrades to a plain form
// POST/GET when JS is absent (ui-spec.md §4).
templ Layout(title string) {
	<!DOCTYPE html>
	<html lang="en">
		<head>
			<meta charset="utf-8"/>
			<meta name="viewport" content="width=device-width, initial-scale=1"/>
			<title>{ title }</title>
			<script src="/static/htmx.min.js" defer></script>
			@templ.Raw(styleTag)
		</head>
		<body>
			<header>
				<a href="/" class="brand">Tapir</a>
				<nav class="nav"><a href="/account">Account</a><a href="/auth/logout">Log out</a></nav>
			</header>
			<main>
				{ children... }
			</main>
		</body>
	</html>
}

// PublicLayout is the shell for unauthenticated pages (/welcome, /invite).
// Same structure as Layout but without the nav auth links — a visitor who is not
// logged in should not see "Account" or "Log out".
templ PublicLayout(title string) {
	<!DOCTYPE html>
	<html lang="en">
		<head>
			<meta charset="utf-8"/>
			<meta name="viewport" content="width=device-width, initial-scale=1"/>
			<title>{ title }</title>
			<script src="/static/htmx.min.js" defer></script>
			@templ.Raw(styleTag)
		</head>
		<body>
			<header>
				<a href="/" class="brand">Tapir</a>
			</header>
			<main>
				{ children... }
			</main>
		</body>
	</html>
}

// WelcomePage is the public landing page (served at /welcome, outside the auth
// guard — ADR-012). Logged out: the tapir mascot, a one-line tagline, and a
// single "Get Started" CTA into the shared Dex flow (sign-in and sign-up are the
// same URL). Logged in: a greeting plus links back into the app and to log out.
templ WelcomePage(user User, loggedIn bool) {
	@PublicLayout("Tapir — Watch less, know more") {
		<section class="welcome">
			<div class="welcome-hero">
				<pre aria-hidden="true">@templ.Raw(welcomeHero)</pre>
			</div>
			if loggedIn {
				<h1 class="welcome-title">Welcome back</h1>
				if user.Email != "" {
					<p class="welcome-tagline">Signed in as { user.Email }.</p>
				}
				<div class="welcome-cta">
					<a class="btn btn-lg" href="/">Go to my Tapir</a>
					<a class="btn-secondary" href="/auth/logout">Log Out</a>
				</div>
			} else {
				<h1 class="welcome-title">Watch less, know more</h1>
				<p class="welcome-tagline">
					Tapir summarizes the videos your subscriptions publish, so you can
					skim the gist and decide what is worth your time.
				</p>
				<div class="welcome-cta">
					<a class="btn btn-lg" href="/auth/login">Get Started</a>
				</div>
				<p class="welcome-sub">Tapir is invite-only right now. If you've been invited, sign in above. New summaries land gradually — Tapir fetches captions slowly to respect YouTube's limits.</p>
			}
		</section>
	}
}

// flashBanner renders a one-shot notification for a flash code (connect success/
// failure, disconnect, delete, registration). An empty or unknown code renders
// nothing, so it is safe to drop into any page unconditionally. Reused across the
// app — not per-page ad-hoc markup.
templ flashBanner(code string) {
	if f, ok := flashFor(code); ok {
		<div class={ "flash", "flash-" + f.Kind } role="status" aria-live="polite">{ f.Message }</div>
	}
}

// ListPage is the full summary list with the filter form. HTMX swaps only the
// #summary-list region; a non-HTMX request renders the whole page. flash carries
// a one-shot notification (e.g. "connected", "registered") surfaced on arrival
// after a POST→redirect.
templ ListPage(b listBuckets, f Filter, stats PipelineStats, flash string, hasConnected bool) {
	@Layout("Tapir — Summaries") {
		@flashBanner(flash)
		if !b.empty() || f.active() {
			@filterForm(f)
		}
		if stats.RateLimited > 0 || stats.Pending > 0 || stats.NoText > 0 {
			@pipelineBar(stats)
		}
		if stats.RateLimited+stats.Pending > 0 {
			<p class="pipeline-note muted">
				Tapir fetches captions slowly on purpose, to respect YouTube's limits —
				new summaries land gradually. Check back tomorrow.
			</p>
		}
		<div id="summary-list">
			@summaryList(b, hasConnected)
		</div>
	}
}

// pipelineBar is the one-line backlog status. Counts are framed by what the user
// can read NOW ("ready"), what is waiting behind the honest caption rate limit
// ("in queue" = pending + rate-limited, never "fetching soon" — see A3/ADR-014),
// and what is permanently unreadable ("no captions").
templ pipelineBar(s PipelineStats) {
	<div class="pipeline-bar">
		if s.Summarized > 0 {
			<span>{ fmt.Sprintf("%d ready", s.Summarized) }</span>
		}
		if s.RateLimited+s.Pending > 0 {
			<span>{ fmt.Sprintf("%d in queue", s.RateLimited+s.Pending) }</span>
		}
		if s.NoText > 0 {
			<span class="muted">{ fmt.Sprintf("%d no captions", s.NoText) }</span>
		}
	</div>
}

templ filterForm(f Filter) {
	<form
		class="filters"
		method="get"
		action="/"
		hx-get="/"
		hx-target="#summary-list"
		hx-swap="innerHTML"
		hx-indicator="#filter-indicator"
	>
		<label>Channel <input type="text" name="channel" value={ f.Channel } placeholder="any"/></label>
		<label class="filter-check">
			<input type="checkbox" name="summarized" value="1" if f.OnlySummarized { checked }/>
			Summarized only
		</label>
		<button type="submit" class="btn">Filter</button>
		<span id="filter-indicator" class="htmx-indicator">filtering…</span>
	</form>
}

// summaryList is the swappable list fragment. It leads with readable summaries +
// recent un-summarized cards (b.Main), then collapses the noise so it does not
// bury the payload (UX review B3/B4): a one-line count of caption-less videos,
// and a single disclosure holding the older un-summarized back-catalogue. Cards
// reflow to a single column on mobile; an empty list shows a friendly first-run
// state instead of a blank table.
templ summaryList(b listBuckets, hasConnected bool) {
	if b.empty() {
		if hasConnected {
			<div class="empty empty-connected">
				<strong>Your account is connected</strong>
				<span>Tapir is finding your subscriptions and fetching captions — summaries appear here gradually. Check back later.</span>
			</div>
		} else {
			<div class="empty">
				<strong>No videos yet</strong>
				<span>Connect your YouTube account to get started.</span>
				<p><a class="btn" href="/oauth/youtube/connect">Connect YouTube</a></p>
			</div>
		}
	} else {
		<ul class="cards">
			for _, r := range b.Main {
				@VideoCard(r)
			}
		</ul>
		if b.NoCaption > 0 {
			<p class="list-note muted">{ fmt.Sprintf("%d video(s) have no captions and can't be summarized.", b.NoCaption) }</p>
		}
		if len(b.Older) > 0 {
			<details class="older-videos">
				<summary>{ fmt.Sprintf("Show %d older videos — summarize on demand", len(b.Older)) }</summary>
				<ul class="cards">
					for _, r := range b.Older {
						@VideoCard(r)
					}
				</ul>
			</details>
		}
	}
}

// VideoCard is one list card, returned standalone by POST /v/{id}/summarize and
// /v/{id}/retry-now (HTMX swaps outerHTML). Five footer states, status-primary:
//  1. Summarized — preview + chip + actions; no button.
//  2. No captions (TranscriptStatus=="none") — terminal; "No transcript available"; no button.
//  3. Queued (SummarizeRequested) — "Queued · summarizing shortly"; no button.
//  4. Rate-limited — "In queue" + quiet "Summarize" → /retry-now.
//  5. Pending (else) — "Not summarized" + quiet "Summarize" → /summarize.
// States 4 and 5 use one verb ("Summarize") and one style (.btn-quiet); the
// backend side-effect difference (clear-backoff vs. set-flag) is invisible to users.
templ VideoCard(r store.SummaryRow) {
	<li class={ "card", templ.KV("card-pending", !r.Summarized) } id={ "video-" + r.VideoID }>
		if r.Summarized {
			<div class="card-title"><a href={ videoURL(r.VideoID) }>{ displayTitle(r) }</a></div>
		} else {
			<div class="card-title">{ displayTitle(r) }</div>
		}
		if cardMeta(r) != "" {
			<div class="card-meta">{ cardMeta(r) }</div>
		}
		if r.Summarized {
			if p := previewText(r.Summary, 160); p != "" {
				<div class="card-preview">{ p }</div>
			}
		}
		<div class="card-foot">
			if r.Summarized {
				// State 1: summarized — provider chip, fallback badge, action state.
				if r.AIProvider != "" {
					<span class="chip">{ r.AIProvider }</span>
				}
				if r.FallbackUsed {
					<span class="badge" title="summarized with the fallback model" aria-label="summarized with the fallback model">fallback</span>
				}
				if len(r.Actions) > 0 {
					<span class="card-state">{ strings.Join(r.Actions, ", ") }</span>
				}
			} else if r.TranscriptStatus == "none" {
				// State 2: no captions — terminal dead-end; nothing the user can do.
				<span class="card-state muted">No transcript available</span>
			} else if r.SummarizeRequested {
				// State 3: queued — being summarized on the next pass; no scheduler jargon.
				<span class="chip">Queued</span>
				<span class="card-state muted">summarizing shortly</span>
			} else if r.TranscriptStatus == "rate_limited" {
				// State 4: rate-limited — honest "in queue" status (NOT "fetching soon",
				// which oversells imminence) + a quiet nudge → retry-now handler.
				<span class="card-state muted">In queue</span>
				<form
					method="post"
					action={ retryNowURL(r.VideoID) }
					hx-post={ string(retryNowURL(r.VideoID)) }
					hx-target={ "#video-" + r.VideoID }
					hx-swap="outerHTML"
					class="card-nudge-form"
				>
					<button type="submit" class="btn-quiet" title="Summarize this video">Summarize</button>
				</form>
			} else {
				// State 5: pending — discovered, not yet attempted; nudge button → summarize handler.
				<span class="card-state muted">Not summarized</span>
				<form
					method="post"
					action={ summarizeURL(r.VideoID) }
					hx-post={ string(summarizeURL(r.VideoID)) }
					hx-target={ "#video-" + r.VideoID }
					hx-swap="outerHTML"
					class="card-nudge-form"
				>
					<button type="submit" class="btn-quiet" title="Summarize this video">Summarize</button>
				</form>
			}
		</div>
	</li>
}

// TapirSpinner is the summarizing animation: a Charmbracelet-style TUI panel —
// three richly coloured ASCII tapir frames (inline span colours, snout wiggling
// ∩→∪→~) cross-faded by CSS, plus a lipgloss-style progress bar whose mint fill
// grows over the dim track. The panel is aria-hidden (decorative); the
// "Summarizing…" label below carries the meaning for assistive tech.
templ TapirSpinner() {
	<div class="tapir-charm" aria-hidden="true">
		<pre class="tapir-f1">@templ.Raw(tapirFrameHTML1)</pre>
		<pre class="tapir-f2">@templ.Raw(tapirFrameHTML2)</pre>
		<pre class="tapir-f3">@templ.Raw(tapirFrameHTML3)</pre>
		<div class="tapir-bar"><span class="tapir-bar-fill" style={ "color:" + CharmMint }>{ tapirBarFill }</span></div>
	</div>
	<p class="tapir-label" role="status" aria-live="polite"><em>Summarizing…</em></p>
}

// processingCard is the in-flight summarization card. It replaces the Summarize
// button card and polls /v/{id}/status every 2s, swapping itself (outerHTML, same
// id as VideoCard) for whatever state comes back: it keeps polling while still
// processing, and the summary/queued card it is eventually replaced by carries no
// poll, so polling stops on its own when the fragment changes.
templ processingCard(r store.SummaryRow) {
	<li
		class="card card-processing"
		id={ "video-" + r.VideoID }
		hx-get={ string(statusURL(r.VideoID)) }
		hx-trigger="every 2s"
		hx-swap="outerHTML"
	>
		<div class="card-title">{ displayTitle(r) }</div>
		if cardMeta(r) != "" {
			<div class="card-meta">{ cardMeta(r) }</div>
		}
		@TapirSpinner()
	</li>
}

// DetailPage is the full summary view: text, highlights, takeaways, metadata,
// and the action button group.
templ DetailPage(r store.SummaryRow) {
	@Layout("Tapir — " + displayTitle(r)) {
		<article class="detail">
			<p class="back"><a href="/">← Summaries</a></p>
			<h1>{ displayTitle(r) }</h1>
			<p class="meta">
				if detailMeta(r) != "" {
					<span>{ detailMeta(r) }</span>
				}
				if r.FallbackUsed {
					<span class="badge" title="summarized with the fallback model" aria-label="summarized with the fallback model">fallback</span>
				}
			</p>
			if url, ok := embedURL(r.ProviderVideoID); ok {
				<div class="embed">
					<iframe
						src={ url }
						title={ displayTitle(r) }
						loading="lazy"
						referrerpolicy="strict-origin-when-cross-origin"
						allow="accelerometer; clipboard-write; encrypted-media; gyroscope; picture-in-picture; web-share"
						allowfullscreen
					></iframe>
				</div>
			}
			if r.URL != "" {
				<p class="source"><a href={ externalURL(r.URL) } rel="noopener noreferrer">watch on source ↗</a></p>
			}
			@ActionButtons(r.VideoID, actionSet(r.Actions))
			// Lead with the attention-saving payload: Takeaways ("is this worth my
			// time?") first, then Highlights, then the full Summary last (UX review
			// A8). Takeaways/Highlights are conditional, so a video without them falls
			// through to the Summary leading naturally.
			if len(r.Takeaways) > 0 {
				<section>
					<h2>Takeaways</h2>
					<ul>
						for _, t := range r.Takeaways {
							<li>{ t }</li>
						}
					</ul>
				</section>
			}
			if len(r.Highlights) > 0 {
				<section>
					<h2>Highlights</h2>
					<ul>
						for _, h := range r.Highlights {
							<li>{ h }</li>
						}
					</ul>
				</section>
			}
			<section>
				<h2>Summary</h2>
				<p class="body">{ r.Summary }</p>
			</section>
		</article>
	}
}

// RegisterPage is the explicit registration step (ADR-012): an authenticated Dex
// subject with no tapir user picks a display name and accepts the terms to create
// their account. errMsg, when set, reports a validation problem on the prior POST.
templ RegisterPage(email, errMsg string) {
	@Layout("Tapir — Register") {
		<article class="register">
			<h1>Complete your registration</h1>
			if email != "" {
				<p class="meta">Signed in as { email }.</p>
			}
			<p>Choose a display name to finish setting up your Tapir account.</p>
			if errMsg != "" {
				<p class="error" role="alert">{ errMsg }</p>
			}
			<form method="post" action="/register" class="register-form">
				<label>
					Display name
					<input type="text" name="display_name" required autofocus/>
				</label>
				<label class="checkbox">
					<input type="checkbox" name="accept_terms" value="yes" required/>
					I accept the terms of use
				</label>
				<button type="submit" class="btn">Register</button>
			</form>
		</article>
	}
}

// AccountPage is the account-management view: the registered display name and
// signed-in email, the user's connected video accounts (each with a Disconnect
// control), a Connect-YouTube link when none is connected, and the delete-account
// danger zone. flash surfaces a one-shot notification (disconnect/connect).
templ AccountPage(displayName, email string, conns []store.Connection, autoSummarize bool, channelErrors []store.ChannelError, flash string) {
	@Layout("Tapir — Account") {
		@flashBanner(flash)
		<article class="account">
			<h1>Account</h1>
			<dl class="account-meta">
				<dt>Display name</dt>
				<dd>{ displayNameOr(displayName) }</dd>
				if email != "" {
					<dt>Signed in as</dt>
					<dd>{ email }</dd>
				}
			</dl>
			<section>
				<h2>Summarization</h2>
				<p class="muted">
					Automatic summarizes new videos from about the last week as they are
					discovered. Older videos stay browsable — summarize them on demand.
					Manual lets you pick which videos to summarize — every new video appears
					in your list with a Summarize button.
				</p>
				@summarizeModeControl(autoSummarize)
			</section>
			if len(channelErrors) > 0 {
				<section class="channel-errors">
					<h2>Unavailable channels</h2>
					<p class="muted">
						{ fmt.Sprintf("%d channel(s) returned errors on the last discovery pass.", len(channelErrors)) }
						These may have been deleted or made private on YouTube.
					</p>
					<ul class="channel-error-list">
						for _, ce := range channelErrors {
							<li>
								<span class="channel-error-name">{ ce.ChannelName }</span>
								<span class="chip chip-warn">unavailable</span>
								<span class="muted channel-error-since">since { ce.FirstSeen.Format("2006-01-02") }</span>
							</li>
						}
					</ul>
				</section>
			}
			<section>
				<h2>Connected accounts</h2>
				if len(conns) == 0 {
					<p class="muted">No connected video accounts yet.</p>
				} else {
					<ul class="conn-list">
						for _, c := range conns {
							<li class="conn">
								<div class="conn-main">
									<span class="conn-provider">{ providerLabel(c.Provider) }</span>
									if c.ProviderAccount != "" {
										<span class="muted">{ c.ProviderAccount }</span>
									}
									<span class="chip">{ c.Status }</span>
								</div>
								<div class="conn-meta muted">connected { c.ConnectedAt.Format("2006-01-02") }</div>
								<form method="post" action={ disconnectURL(c.Provider) }>
									<button type="submit" class="btn-secondary">Disconnect</button>
								</form>
							</li>
						}
					</ul>
				}
				if !hasYouTube(conns) {
					<p><a class="btn" href="/oauth/youtube/connect">Connect YouTube</a></p>
				}
			</section>
			<section class="danger-zone">
				<h2>Delete account</h2>
				<p class="muted">
					Permanently remove your Tapir account and all of its data — summaries,
					watch/skip/save actions, and connected accounts. This cannot be undone.
				</p>
				<details class="confirm-delete">
					<summary class="btn-danger">Delete account…</summary>
					<div class="confirm-body">
						<p>This permanently deletes your account and all data. Are you sure?</p>
						<form method="post" action="/account/delete">
							<button type="submit" class="btn-danger">Yes, permanently delete my account</button>
						</form>
					</div>
				</details>
			</section>
		</article>
	}
}

// summarizeModeControl is the auto/manual toggle, also returned standalone by
// POST /account/summarize-mode (HTMX swaps it via outerHTML). The hidden field
// submits the desired NEW value, so a single submit flips the mode; without JS the
// form posts and the handler redirects back to /account.
templ summarizeModeControl(auto bool) {
	<div id="summarize-mode" class="summarize-mode">
		<p>Current mode: <strong>{ summarizeModeLabel(auto) }</strong></p>
		<form
			method="post"
			action="/account/summarize-mode"
			hx-post="/account/summarize-mode"
			hx-target="#summarize-mode"
			hx-swap="outerHTML"
		>
			<input type="hidden" name="enabled" value={ boolStr(!auto) }/>
			<button type="submit" class="btn-secondary">{ summarizeModeToggleLabel(auto) }</button>
		</form>
	</div>
}

// ActionButtons is the toggle group fragment returned by POST /v/{id}/action.
// Each button submits its verb; HTMX swaps this element in place (outerHTML),
// and without JS the form POSTs and the handler redirects back to the detail
// page. active marks the verbs currently set for (user, video).
templ ActionButtons(videoID string, active map[string]bool) {
	<form
		id="action-buttons"
		class="actions"
		method="post"
		action={ actionURL(videoID) }
		hx-post={ string(actionURL(videoID)) }
		hx-target="#action-buttons"
		hx-swap="outerHTML"
	>
		for _, v := range actionVerbs {
			<button
				type="submit"
				name="action"
				value={ v }
				class={ "action", templ.KV("active", active[v]) }
				aria-pressed={ ariaPressed(active[v]) }
			>
				if active[v] {
					{ "✓ " + actionLabel(v) }
				} else {
					{ actionLabel(v) }
				}
			</button>
		}
	</form>
}
