import { useState } from 'react';
import {
	ArrowLeft,
	Check,
	Loader2,
	Info,
	Sparkles,
	AlignLeft,
	FileText,
	ExternalLink,
	Clock,
} from 'lucide-react';
import { Button } from '@/components/ui/button';
import { TextInput } from '@/components/ui/text-input';
import FlavioIcon from '@/components/ui/flavio-icon';
import ConfirmDialog from '@/components/ui/confirm-dialog';
import { isAppPaused } from '@/api/client';
import { resolvePageRef } from '@/features/interventions/detail/pageRef';
import { fileNameFromSrc, ALT_MAX } from '@/features/interventions/detail/altText';
import { useInterventionResponse } from '@/features/interventions/useInterventionResponse';
import UpgradeLock from '@/features/interventions/detail/UpgradeLock';

/**
 * Detail for the `imgaltsingle` / `propose` intervention (type selection).
 *
 * Flavio proposes alt descriptions for images that lack one. Per image the user
 * can edit the text inline, or mark it decorative (empty alt). Confirm all sends
 * the full per-image array; Dismiss sends an explicit empty array so the executor
 * can tell a dismiss from a malformed payload.
 *
 * Contract (doc + task agree): metadata has
 * `proposals:[{src,thumbnail_src?,current_alt,proposed_alt,proposed_decorative,resolved_via}]`,
 * `skipped_vision_cap`, `skipped_vision_fetch`. There are no `options`, so:
 *  - Confirm all → status "acknowledge", userResponse { confirmed:[{src,alt,decorative}] }.
 *  - Dismiss     → status "dismiss",     userResponse { confirmed:[] }.
 *
 * 125 chars is a soft limit (the counter warns, it doesn't block). A non-decorative
 * image with an empty description is the only thing that blocks Confirm.
 *
 * Defensive on metadata: every field is read with a fallback.
 */

/** How the proposal was resolved: AI vision vs surrounding-text context. */
const ResolvedVia = ({ via }) => {
	if (via === 'vision') {
		return (
			<span className="inline-flex items-center gap-1 rounded-full border border-magenta-200 bg-magenta-50 px-2 py-0.5 small-regular text-magenta-600">
				<Sparkles className="w-3 h-3" />
				vision
			</span>
		);
	}
	if (via === 'context') {
		return (
			<span className="inline-flex items-center gap-1 rounded-full bg-muted px-2 py-0.5 small-regular text-muted-foreground">
				<AlignLeft className="w-3 h-3" />
				context
			</span>
		);
	}
	return null;
};

/** A switch + label that flips an image to decorative (empty alt). */
const DecorativeToggle = ({ checked, onChange }) => (
	<button
		type="button"
		role="switch"
		aria-checked={checked}
		onClick={() => onChange(!checked)}
		className="group inline-flex items-center gap-2 cursor-pointer"
	>
		<span
			className={`relative inline-flex h-5 w-9 items-center rounded-full transition-colors ${
				checked ? 'bg-foreground' : 'bg-muted-foreground/30'
			}`}
		>
			<span
				className={`inline-block h-4 w-4 rounded-full bg-background transition-transform ${
					checked ? 'translate-x-4' : 'translate-x-0.5'
				}`}
			/>
		</span>
		<span className="small-regular text-muted-foreground group-hover:text-foreground transition-colors">
			Mark as decorative
		</span>
	</button>
);

const ImgAlt = ({ intervention = {}, interventionId, onBack, onResolved }) => {
	const m = intervention.metadata || {};
	const { pageLabel, pageUrl } = resolvePageRef(m);
	const proposals = Array.isArray(m.proposals) ? m.proposals : [];
	// Trial ended: the intervention can't be acted on, so its action buttons are
	// swapped for the single "unlock" upgrade CTA.
	const isPaused = isAppPaused();

	const [items, setItems] = useState(() =>
		proposals.map((p, i) => ({
			key: i,
			src: p?.src ?? '',
			thumbnail: p?.thumbnail_src || p?.src || '',
			resolvedVia: p?.resolved_via || '',
			alt: p?.proposed_alt ?? '',
			decorative: p?.proposed_decorative === true,
		}))
	);

	const {
		pending,
		resolved,
		error,
		confirmingDismiss,
		run,
		confirmDismiss,
		cancelDismiss,
	} = useInterventionResponse(interventionId, { onResolved });

	const update = (key, patch) =>
		setItems((prev) =>
			prev.map((it) => (it.key === key ? { ...it, ...patch } : it))
		);

	const skippedCount =
		(Array.isArray(m.skipped_vision_cap) ? m.skipped_vision_cap.length : 0) +
		(Array.isArray(m.skipped_vision_fetch)
			? m.skipped_vision_fetch.length
			: 0);

	// A non-decorative image needs a description; that's the only block on Confirm.
	const hasInvalid = items.some((it) => !it.decorative && !it.alt.trim());
	const canConfirm =
		items.length > 0 && !hasInvalid && !pending && !resolved;

	const confirm = () =>
		run('primary', {
			status: 'acknowledge',
			userResponse: {
				confirmed: items.map((it) => ({
					src: it.src,
					alt: it.decorative ? '' : it.alt.trim(),
					decorative: it.decorative,
				})),
			},
		});

	if (resolved) {
		return (
			<div className="max-w-2xl mx-auto text-center">
				<FlavioIcon className="w-12 h-12 mx-auto mb-4" />
				<h1 className="heading-h2 mt-0! leading-tight mb-3">
					{resolved === 'primary' ? 'All set' : 'Got it'}
				</h1>
				<div className="max-w-md mx-auto">
					<p className="paragraph-regular text-muted-foreground mb-0!">
						{resolved === 'primary'
							? "Great. I'll add these descriptions shortly."
							: "No problem. I won't change these images."}
					</p>
				</div>
				{onBack && (
					<Button
						onClick={onBack}
						size="lg"
						className="mt-6 bg-foreground text-background! hover:bg-foreground/90"
					>
						<ArrowLeft />
						Back to your list
					</Button>
				)}
			</div>
		);
	}

	return (
		<div className="max-w-2xl mx-auto">
			<div className="text-center">
				<h1 className="heading-h1 mt-0! leading-tight mb-3">
					Approve missing image descriptions
				</h1>
				<div className="max-w-xl mx-auto mb-6">
					<p className="paragraph-regular text-muted-foreground mb-0!">
						I found {items.length} image
						{items.length === 1 ? '' : 's'} on this page without a
						description. Alt text helps screen readers and improves SEO.
						Edit anything before you publish.
					</p>
					{pageLabel &&
						(pageUrl ? (
							<a
								href={pageUrl}
								target="_blank"
								rel="noopener noreferrer"
								className="mt-3 inline-flex items-center gap-1.5 rounded-full border border-border px-3 py-1 small-medium text-muted-foreground! hover:bg-muted/60 hover:text-foreground! transition-colors"
							>
								<FileText className="w-3.5 h-3.5" />
								{pageLabel}
								<ExternalLink className="w-3 h-3" />
							</a>
						) : (
							<div className="mt-3 inline-flex items-center gap-1.5 rounded-full border border-border px-3 py-1 small-medium text-muted-foreground">
								<FileText className="w-3.5 h-3.5" />
								{pageLabel}
							</div>
						))}
				</div>
			</div>

			<div className="flex items-start gap-2 rounded-xl border border-border bg-muted/30 p-3 mb-5 small-regular text-muted-foreground">
				<Info className="w-4 h-4 shrink-0 mt-0.5" />
				<span>
					These descriptions are applied on the frontend only. If you open
					your editor, the image will still show without an alt, and
					that's expected.
				</span>
			</div>

			<div className="space-y-3">
				{items.map((it) => {
					const filename = fileNameFromSrc(it.src);
					const over = it.alt.length > ALT_MAX;
					const invalid = !it.decorative && !it.alt.trim();
					const counterTone = it.decorative
						? 'text-muted-foreground/50'
						: over
							? 'text-amber-600'
							: 'text-muted-foreground';
					return (
						<div
							key={it.key}
							className="rounded-2xl border border-border p-4"
						>
							<div className="flex gap-3">
								<img
									src={it.thumbnail}
									alt=""
									className="w-14 h-14 rounded-lg object-cover border border-border bg-muted shrink-0"
								/>
								<div className="min-w-0 flex-1">
									<div className="flex items-center gap-2 mb-2">
										<span className="small-semibold font-mono text-foreground truncate min-w-0">
											{filename}
										</span>
										<span className="ml-auto shrink-0">
											<ResolvedVia via={it.resolvedVia} />
										</span>
									</div>

									{it.decorative ? (
										<div className="rounded-lg border border-input bg-muted/40 px-3 py-2 small-regular italic text-muted-foreground/70">
											alt="" (decorative, hidden from screen
											readers)
										</div>
									) : (
										<TextInput
											value={it.alt}
											onChange={(e) =>
												update(it.key, {
													alt: e.target.value,
												})
											}
											error={invalid}
											placeholder="Describe what's in the image"
										/>
									)}

									<div className="flex items-center justify-between gap-3 mt-2">
										<span
											className={`small-regular ${counterTone}`}
										>
											{it.decorative
												? 'Will be applied as alt=""'
												: `${it.alt.length} / ${ALT_MAX} chars`}
										</span>
										<DecorativeToggle
											checked={it.decorative}
											onChange={(v) =>
												update(it.key, { decorative: v })
											}
										/>
									</div>
								</div>
							</div>
						</div>
					);
				})}
			</div>

			{skippedCount > 0 && (
				<div className="flex items-start gap-2 rounded-xl bg-muted/40 p-3 mt-3 small-regular text-muted-foreground">
					<Clock className="w-4 h-4 shrink-0 mt-0.5" />
					<span>
						There {skippedCount === 1 ? 'is' : 'are'} {skippedCount}{' '}
						more image{skippedCount === 1 ? '' : 's'} on this page I
						couldn't process this pass. I'll pick {skippedCount === 1
							? 'it'
							: 'them'}{' '}
						up in a future run.
					</span>
				</div>
			)}

			<div className="flex items-center justify-center gap-3 mt-8">
				{isPaused ? (
					<UpgradeLock />
				) : (
					<>
						<Button
							onClick={confirm}
							disabled={!canConfirm}
							size="lg"
							className="bg-foreground text-background! hover:bg-foreground/90"
						>
							{pending === 'primary' ? (
								<Loader2 className="animate-spin" />
							) : (
								<Check />
							)}
							Confirm all
						</Button>
						<Button
							onClick={() =>
								run('secondary', {
									status: 'dismiss',
									userResponse: { confirmed: [] },
								})
							}
							disabled={!!pending || !!resolved}
							size="lg"
							variant="outline"
						>
							{pending === 'secondary' && (
								<Loader2 className="animate-spin" />
							)}
							Dismiss
						</Button>
					</>
				)}
			</div>

			{(error || hasInvalid) && (
				<p
					className={`small-regular text-center mt-3 mb-0! ${
						error ? 'text-destructive' : 'text-muted-foreground'
					}`}
				>
					{error ||
						'Add a description for each image, or mark it as decorative.'}
				</p>
			)}
			<p className="small-regular text-muted-foreground text-center mt-4 mb-0!">
				Nothing goes live until you confirm.
			</p>

			<ConfirmDialog
				open={confirmingDismiss}
				onOpenChange={(open) => {
					if (!pending && !open) cancelDismiss();
				}}
				title="Dismiss these descriptions?"
				description="Nothing on your site changes, and I won't suggest these again."
				confirmLabel="Yes, dismiss"
				cancelLabel="Cancel"
				pending={pending === 'secondary'}
				onConfirm={confirmDismiss}
			/>
		</div>
	);
};

export default ImgAlt;
