import { useState } from 'react';
import {
	Check,
	Loader2,
	Info,
	FileText,
	ExternalLink,
	ArrowLeft,
} from 'lucide-react';
import { Button } from '@/components/ui/button';
import { TextInput } from '@/components/ui/text-input';
import { Textarea } from '@/components/ui/textarea';
import FlavioIcon from '@/components/ui/flavio-icon';
import ConfirmDialog from '@/components/ui/confirm-dialog';
import { respondToIntervention } from '@/features/interventions/respond';
import { isAppPaused } from '@/api/client';
import UpgradeLock from '@/features/interventions/detail/UpgradeLock';
import { logError } from '@/errors/logger';

/**
 * Reusable detail UI for "approve a single line of plain text" interventions:
 * page title (`optimizetitlesingle`), meta description (`optimizedescsingle`) and
 * tagline (`tagline`). They share the same shape: a read-only current value, an
 * editable draft with a character counter, and two actions (apply / keep aside).
 *
 * Per-handler wrappers map their metadata into these props and build the
 * `user_response` for each action; this component owns the editing, validation
 * and the PATCH /interventions/:id call.
 *
 * Validation: the primary action is blocked when the draft is empty, exceeds
 * `maxLength`, or `validate` returns a message (shown below the field). A
 * `recommended` range only warns (amber counter); with just `maxLength`, the
 * counter warns as the draft approaches the cap.
 *
 * @param {object}   props
 * @param {string}   props.interventionId
 * @param {()=>void} [props.onBack]      Navigate back; also the success-state CTA.
 * @param {()=>void} [props.onResolved]  Fired once the intervention is resolved.
 * @param {string}   props.heading
 * @param {string}   props.subtitle
 * @param {string}  [props.pageLabel]      Which page this affects (chip under the subtitle).
 * @param {string}  [props.pageUrl]        When set, the page chip links to it (new tab).
 * @param {string}   props.fieldLabel      e.g. "Title" → "Current title" / "My draft · title".
 * @param {string}   props.currentValue
 * @param {string}   props.proposedValue   Initial draft.
 * @param {string}  [props.emptyText]      Placeholder when there's no current value.
 * @param {boolean} [props.multiline]      Render a textarea instead of a single-line input.
 * @param {number}   props.maxLength       Hard cap; over it blocks the primary action.
 * @param {{min:number,max:number}} [props.recommended] Range; outside it warns (amber).
 * @param {string}  [props.counterLabel]   Label next to the counter.
 * @param {(value:string)=>string|null} [props.validate] Extra validation; a returned
 *        message blocks the primary action and shows below the field.
 * @param {string}  [props.reasoning]      Optional "Why this change" context block.
 * @param {(value:string)=>React.ReactNode} [props.renderValue] Per-handler "pretty"
 *        rendering of a value; when set, decorates the current value and shows a
 *        live preview of the draft (e.g. wrapping it in a `<title>` tag).
 * @param {string}  [props.notice]         Optional advisory shown above the actions.
 * @param {string}   props.primaryLabel
 * @param {string}   props.secondaryLabel
 * @param {(value:string)=>{status:string,userResponse?:object}} props.primaryAction
 * @param {()=>{status:string,userResponse?:object}} props.secondaryAction
 * @param {string}  [props.footerNote]
 * @param {string}  [props.primaryDoneText]
 * @param {string}  [props.secondaryDoneText]
 * @param {string}  [props.dismissTitle]
 * @param {string}  [props.dismissDescription]
 * @param {string}  [props.dismissConfirmLabel]
 */
const TextProposal = ({
	interventionId,
	onBack,
	onResolved,
	heading,
	subtitle,
	pageLabel,
	pageUrl,
	fieldLabel,
	currentValue = '',
	proposedValue = '',
	emptyText = 'Empty, nothing set yet',
	multiline = false,
	maxLength,
	recommended,
	counterLabel = 'Recommended length',
	validate,
	reasoning,
	renderValue,
	notice,
	primaryLabel,
	secondaryLabel,
	primaryAction,
	secondaryAction,
	footerNote,
	primaryDoneText = "Done. I'll take it from here.",
	secondaryDoneText = "No problem. I'll leave it as it is.",
	dismissTitle = 'Dismiss this suggestion?',
	dismissDescription = "Nothing on your site changes, and I won't suggest this again.",
	dismissConfirmLabel = 'Yes, dismiss',
}) => {
	const [value, setValue] = useState(proposedValue);
	const [focused, setFocused] = useState(false);
	const [pending, setPending] = useState(null); // 'primary' | 'secondary' | null
	const [resolved, setResolved] = useState(null); // 'primary' | 'secondary' | null
	const [error, setError] = useState(null);
	const [confirmingDismiss, setConfirmingDismiss] = useState(false);
	// Trial ended: the intervention can't be acted on, so the action buttons are
	// swapped for the single "unlock" upgrade CTA.
	const isPaused = isAppPaused();

	const length = value.length;
	const isEmpty = value.trim().length === 0;
	// `maxLength` is a hard cap that blocks the primary action; `recommended` is
	// a soft range that only warns (amber) when the draft falls outside it.
	const tooLong = typeof maxLength === 'number' && length > maxLength;
	const validationError = !isEmpty && validate ? validate(value) : null;
	const canSubmit =
		!isEmpty && !tooLong && !validationError && !pending && !resolved;

	// The counter denominator is the recommended max (e.g. 60 for titles, 155 for
	// meta descriptions); fall back to the hard cap when there's no range.
	const limit =
		recommended?.max ?? (typeof maxLength === 'number' ? maxLength : null);

	// Warn (amber) outside the recommended range, or, when there's only a hard
	// cap, as the draft approaches it (last ~10%).
	const warn = recommended
		? length < recommended.min || length > recommended.max
		: typeof maxLength === 'number'
			? length >= Math.floor(maxLength * 0.9)
			: false;

	const counterTone =
		isEmpty || tooLong
			? 'text-destructive'
			: warn
				? 'text-amber-600'
				: 'text-muted-foreground';

	const submit = async (which, payload) => {
		setError(null);
		setPending(which);
		try {
			await respondToIntervention(interventionId, payload);
			setResolved(which);
			onResolved?.();
			return true;
		} catch (err) {
			setError("I couldn't save that just now. Please try again.");
			logError(err, {
				action: 'respond_intervention',
				component: 'TextProposal',
			});
			return false;
		} finally {
			setPending(null);
		}
	};

	// Dismiss is irreversible (we won't surface the suggestion again), so route
	// any secondary action with status "dismiss" through a confirmation step.
	const handleSecondary = () => {
		const payload = secondaryAction();
		if (payload.status === 'dismiss') {
			setConfirmingDismiss(true);
		} else {
			submit('secondary', payload);
		}
	};

	const confirmDismiss = async () => {
		const ok = await submit('secondary', secondaryAction());
		// On success we flip to the resolved view (the dialog unmounts with it);
		// on failure, close the dialog so the error shows on the form.
		if (!ok) setConfirmingDismiss(false);
	};

	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'
							? primaryDoneText
							: secondaryDoneText}
					</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>
		);
	}

	const Field = multiline ? Textarea : TextInput;

	return (
		<div className="max-w-2xl mx-auto text-center">
			<h1 className="heading-h1 mt-0! leading-tight mb-3">{heading}</h1>
			<div className="max-w-xl mx-auto mb-8">
				<p className="paragraph-regular text-muted-foreground mb-0!">
					{subtitle}
				</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 className="rounded-2xl border border-border p-6 text-left space-y-5">
				<div>
					<p className="small-semibold uppercase tracking-wide text-muted-foreground my-0!">
						Current {fieldLabel}
					</p>
					{renderValue ? (
						<div className="mt-2 rounded-lg bg-muted/50 px-3 py-2 break-words">
							{currentValue ? (
								renderValue(currentValue)
							) : (
								<span className="small-regular italic text-muted-foreground/70">
									{emptyText}
								</span>
							)}
						</div>
					) : currentValue ? (
						<p className="paragraph-regular text-muted-foreground my-0! mt-1!">
							{currentValue}
						</p>
					) : (
						<p className="paragraph-regular italic text-muted-foreground/70 my-0! mt-1!">
							{emptyText}
						</p>
					)}
				</div>

				<div>
					<div className="flex items-center gap-2 mb-2">
						<p className="small-semibold uppercase tracking-wide text-magenta-600 my-0!">
							My draft · {fieldLabel}
						</p>
						{focused && (
							<span className="small-regular text-magenta-500">
								Editing
							</span>
						)}
					</div>
					<Field
						value={value}
						onChange={(e) => setValue(e.target.value)}
						onFocus={() => setFocused(true)}
						onBlur={() => setFocused(false)}
						error={isEmpty || tooLong || !!validationError}
						rows={multiline ? 3 : undefined}
					/>
					{renderValue && (
						<div className="mt-3">
							<p className="small-regular text-muted-foreground mt-0! mb-1!">
								In your HTML
							</p>
							<div className="rounded-lg bg-muted/50 px-3 py-2 break-words">
								{renderValue(value || '')}
							</div>
						</div>
					)}
					<div className="flex items-center justify-between mt-2">
						<span className="small-regular text-muted-foreground">
							{counterLabel}
						</span>
						<span className={`small-regular ${counterTone}`}>
							{length}
							{limit != null ? ` / ${limit}` : ''}
						</span>
					</div>
					{validationError && (
						<p className="small-regular text-destructive mt-2 mb-0!">
							{validationError}
						</p>
					)}
				</div>
			</div>

			{reasoning && (
				<div className="mt-4 rounded-xl border border-border bg-muted/30 p-4 text-left">
					<p className="small-semibold uppercase tracking-wide text-muted-foreground my-0! mb-1!">
						Why this change
					</p>
					<p className="small-regular text-muted-foreground my-0!">
						{reasoning}
					</p>
				</div>
			)}

			{notice && (
				<div className="max-w-xl mx-auto mt-4 flex items-start gap-2 rounded-xl border border-amber-200 bg-amber-50 p-3 text-left small-regular text-amber-800">
					<Info className="w-4 h-4 shrink-0 mt-0.5 text-amber-600" />
					<span>{notice}</span>
				</div>
			)}

			<div className="flex items-center justify-center gap-3 mt-8">
				{isPaused ? (
					<UpgradeLock />
				) : (
					<>
						<Button
							onClick={() =>
								submit('primary', primaryAction(value))
							}
							disabled={!canSubmit}
							size="lg"
							className="bg-foreground text-background! hover:bg-foreground/90"
						>
							{pending === 'primary' ? (
								<Loader2 className="animate-spin" />
							) : (
								<Check />
							)}
							{primaryLabel}
						</Button>
						<Button
							onClick={handleSecondary}
							disabled={!!pending || !!resolved}
							size="lg"
							variant="outline"
						>
							{pending === 'secondary' && (
								<Loader2 className="animate-spin" />
							)}
							{secondaryLabel}
						</Button>
					</>
				)}
			</div>

			{error && (
				<p className="small-regular text-destructive mt-3 mb-0!">
					{error}
				</p>
			)}
			{footerNote && (
				<p className="small-regular text-muted-foreground mt-4 mb-0!">
					{footerNote}
				</p>
			)}

			<ConfirmDialog
				open={confirmingDismiss}
				onOpenChange={(open) => {
					if (!pending) setConfirmingDismiss(open);
				}}
				title={dismissTitle}
				description={dismissDescription}
				confirmLabel={dismissConfirmLabel}
				cancelLabel="Cancel"
				pending={pending === 'secondary'}
				onConfirm={confirmDismiss}
			/>
		</div>
	);
};

export default TextProposal;
