import { useRef, useState } from 'react';
import { ArrowLeft, Loader2, ChevronRight } from 'lucide-react';
import { Button } from '@/components/ui/button';
import FlavioIcon from '@/components/ui/flavio-icon';
import { isAppPaused } from '@/api/client';
import { useInterventionResponse } from '@/features/interventions/useInterventionResponse';
import LocationPageCard from '@/features/interventions/detail/LocationPageCard';
import UpgradeLock from '@/features/interventions/detail/UpgradeLock';

/**
 * Detail for the `locationpages` / `select_pages` intervention (selection_per_page).
 *
 * Flavio drafts one page per service × location; the user approves or rejects each
 * proposal individually and can edit a subset of fields (h1, content_html,
 * faq_heading, faq_pairs) before submitting all decisions at once.
 *
 * Contract userResponse: { approved:[{id, ...edited fields}], rejected:[id] }.
 * Each approved entry carries only the fields the user actually edited; the
 * executor falls back to the original proposal for anything omitted (so an
 * untouched approval is just `{ id }`). Submit is enabled only once every
 * proposal has a decision. An all-rejected response is valid (approved:[]).
 * Submitted as a single `acknowledge`.
 *
 * No reordering of FAQ pairs in v1 (consistent with faqcontentsingle). Defensive
 * on metadata: every field is read with a fallback.
 */
/** Next still-pending proposal after `currentId` (wraps around); null if none. */
const findNextPending = (list, currentId) => {
	const idx = list.findIndex((i) => i.id === currentId);
	for (let k = idx + 1; k < list.length; k++) {
		if (list[k].decision === 'pending') return list[k].id;
	}
	for (let k = 0; k < idx; k++) {
		if (list[k].decision === 'pending') return list[k].id;
	}
	return null;
};

const LocationPages = ({
	intervention = {},
	interventionId,
	onBack,
	onResolved,
}) => {
	const proposals = Array.isArray(intervention.metadata?.proposals)
		? intervention.metadata.proposals
		: [];

	// Trial ended: the intervention can't be acted on, so its action controls are
	// swapped for the single "unlock" upgrade CTA.
	const isPaused = isAppPaused();

	const [items, setItems] = useState(() =>
		proposals.map((p) => ({
			id: p.id,
			decision: 'pending', // 'pending' | 'approved' | 'rejected'
			edited: false,
			editedFields: {}, // which editable fields the user actually touched
			h1: p.h1 ?? '',
			content_html: p.content_html ?? '',
			faq_heading: p.faq_heading ?? '',
			faq_pairs: (Array.isArray(p.faq_pairs) ? p.faq_pairs : []).map(
				(f, i) => ({
					id: i,
					question: f?.question ?? '',
					answer: f?.answer ?? '',
				})
			),
		}))
	);
	const [expandedId, setExpandedId] = useState(proposals[0]?.id ?? null);
	const faqId = useRef(100000); // monotonic ids for newly added FAQ pairs

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

	const proposalsById = Object.fromEntries(proposals.map((p) => [p.id, p]));

	const updateItem = (id, patch) =>
		setItems((prev) =>
			prev.map((i) => {
				if (i.id !== id) return i;
				const editedFields = { ...i.editedFields };
				for (const key of Object.keys(patch)) editedFields[key] = true;
				return { ...i, ...patch, edited: true, editedFields };
			})
		);
	const setDecision = (id, decision) => {
		setItems((prev) =>
			prev.map((i) => (i.id === id ? { ...i, decision } : i))
		);
		// Deciding the open card advances to the next pending one (or closes it
		// when none are left). Undo (back to pending) doesn't move the focus.
		if (decision !== 'pending' && id === expandedId) {
			const decided = items.map((i) =>
				i.id === id ? { ...i, decision } : i
			);
			setExpandedId(findNextPending(decided, id));
		}
	};
	const setAll = (decision) =>
		setItems((prev) => prev.map((i) => ({ ...i, decision })));
	const addFaq = (id) => {
		const newId = faqId.current++;
		setItems((prev) =>
			prev.map((i) =>
				i.id === id && i.faq_pairs.length < 5
					? {
							...i,
							edited: true,
							editedFields: { ...i.editedFields, faq_pairs: true },
							faq_pairs: [
								...i.faq_pairs,
								{ id: newId, question: '', answer: '' },
							],
						}
					: i
			)
		);
	};

	const decidedCount = items.filter((i) => i.decision !== 'pending').length;
	const approvedCount = items.filter((i) => i.decision === 'approved').length;
	const rejectedCount = items.filter((i) => i.decision === 'rejected').length;
	const pendingCount = items.length - decidedCount;
	const allDecided = pendingCount === 0;

	// Approved pages must have a non-empty H1 and complete FAQ pairs.
	const hasInvalidApproved = items.some(
		(i) =>
			i.decision === 'approved' &&
			(!i.h1.trim() ||
				i.faq_pairs.some(
					(f) => !f.question.trim() || !f.answer.trim()
				))
	);

	const canSubmit =
		items.length > 0 &&
		allDecided &&
		!hasInvalidApproved &&
		!pending &&
		!resolved;

	const submit = () => {
		// Only send fields the user actually edited; the executor falls back to
		// the original proposal for anything omitted.
		const approved = items
			.filter((i) => i.decision === 'approved')
			.map((i) => {
				const ef = i.editedFields || {};
				const entry = { id: i.id };
				if (ef.h1) entry.h1 = i.h1.trim();
				if (ef.content_html) entry.content_html = i.content_html;
				if (ef.faq_heading) entry.faq_heading = i.faq_heading.trim();
				if (ef.faq_pairs) {
					entry.faq_pairs = i.faq_pairs
						.filter((f) => f.question.trim() && f.answer.trim())
						.map((f) => ({
							question: f.question.trim(),
							answer: f.answer.trim(),
						}));
				}
				return entry;
			});
		const rejected = items
			.filter((i) => i.decision === 'rejected')
			.map((i) => i.id);
		run('primary', {
			status: 'acknowledge',
			userResponse: { approved, rejected },
		});
	};

	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">All set</h1>
				<div className="max-w-md mx-auto">
					<p className="paragraph-regular text-muted-foreground mb-0!">
						{approvedCount > 0
							? `Great. I'll publish ${approvedCount} page${approvedCount === 1 ? '' : 's'} shortly.`
							: "No problem. I won't create these pages."}
					</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-3xl mx-auto">
			<div className="text-center">
				<h1 className="heading-h1 mt-0! leading-tight mb-3">
					Review {items.length} new location page
					{items.length === 1 ? '' : 's'}
				</h1>
				<div className="max-w-xl mx-auto mb-8">
					<p className="paragraph-regular text-muted-foreground mb-0!">
						I drafted one page per service and location based on your
						top local searches. Decide which to publish and edit
						anything before they go live.
					</p>
				</div>
			</div>

			{/* Decisions bar */}
			<div className="rounded-2xl border border-border p-4 mb-5 flex items-center gap-4">
				<div className="min-w-0 flex-1">
					<p className="small-semibold text-foreground my-0!">
						{decidedCount} of {items.length} decided
					</p>
					<p className="small-regular text-muted-foreground my-0! mt-0.5!">
						<span className="text-emerald-600">
							{approvedCount} approved
						</span>{' '}
						·{' '}
						<span className="text-red-600">
							{rejectedCount} rejected
						</span>{' '}
						· {pendingCount} pending
					</p>
				</div>
				{isPaused ? (
					<UpgradeLock />
				) : (
					<Button
						onClick={submit}
						disabled={!canSubmit}
						className="bg-foreground text-background! hover:bg-foreground/90"
					>
						{pending === 'primary' ? (
							<Loader2 className="animate-spin" />
						) : null}
						Submit decisions
						<ChevronRight />
					</Button>
				)}
			</div>

			{/* Proposals header + bulk actions */}
			<div className="flex items-center gap-2 mb-3">
				<p className="small-semibold uppercase tracking-wide text-muted-foreground my-0!">
					Proposals
				</p>
				<span className="small-regular text-muted-foreground/70">
					tap any card to review and edit
				</span>
				{!isPaused && (
					<span className="ml-auto flex items-center gap-3">
						<button
							type="button"
							onClick={() => setAll('approved')}
							className="small-medium text-muted-foreground hover:text-foreground transition-colors cursor-pointer"
						>
							Approve all
						</button>
						<button
							type="button"
							onClick={() => setAll('rejected')}
							className="small-medium text-muted-foreground hover:text-foreground transition-colors cursor-pointer"
						>
							Reject all
						</button>
					</span>
				)}
			</div>

			<div className="space-y-3">
				{items.map((item) => (
					<LocationPageCard
						key={item.id}
						proposal={proposalsById[item.id] || {}}
						item={item}
						isPaused={isPaused}
						expanded={expandedId === item.id}
						onToggle={() =>
							setExpandedId((cur) =>
								cur === item.id ? null : item.id
							)
						}
						onDecision={(d) => setDecision(item.id, d)}
						onChange={(patch) => updateItem(item.id, patch)}
						onAddFaq={() => addFaq(item.id)}
					/>
				))}
			</div>

			{error && (
				<p className="small-regular text-destructive text-center mt-4 mb-0!">
					{error}
				</p>
			)}
			{!error && allDecided && hasInvalidApproved && (
				<p className="small-regular text-amber-600 text-center mt-4 mb-0!">
					Fill in the heading and every question and answer on the
					pages you're approving, or remove the empty ones.
				</p>
			)}
			{!error && !allDecided && (
				<p className="small-regular text-muted-foreground text-center mt-4 mb-0!">
					Decide on every page to submit. Nothing goes live until you
					do.
				</p>
			)}
		</div>
	);
};

export default LocationPages;
