'use client';

/**
 * Small interactive pieces shared by the host and admin screens: pending-aware submit buttons,
 * accessible confirm dialogs (native <dialog>, so focus trapping and Escape come for free),
 * copy-to-clipboard, action result messages and the active-aware nav.
 */
import Link from 'next/link';
import { usePathname, useRouter } from 'next/navigation';
import { startTransition, useActionState, useEffect, useId, useRef, useState, type FormEvent, type ReactNode } from 'react';
import { useFormStatus } from 'react-dom';
import { Button, type ButtonSize, type ButtonVariant } from '@/components/ui/Button';
import { CheckIcon } from '@/components/ui/icons';
import { cn } from '@/lib/cn';
import { IDLE, type ActionResult } from './action-result';
import { Notice } from './ui';

/**
 * Runs a server action from a form WITHOUT React's automatic form reset, so a validation error never
 * wipes what the user typed. Pass `pending` to <SubmitButton> (useFormStatus can't see this path).
 */
export function useActionForm(action: (prev: ActionResult, formData: FormData) => Promise<ActionResult>) {
  const [result, dispatch, pending] = useActionState(action, IDLE);
  const onSubmit = (event: FormEvent<HTMLFormElement>) => {
    event.preventDefault();
    const submitter = (event.nativeEvent as SubmitEvent).submitter;
    const formData = new FormData(event.currentTarget, submitter);
    startTransition(() => dispatch(formData));
  };
  return { result, pending, onSubmit };
}

export function SubmitButton({
  children,
  pendingLabel,
  variant = 'primary',
  size = 'md',
  disabled,
  className,
  fullWidth,
  pending: pendingProp,
}: {
  children: ReactNode;
  pendingLabel?: ReactNode;
  variant?: ButtonVariant;
  size?: ButtonSize;
  disabled?: boolean;
  className?: string;
  fullWidth?: boolean;
  /** Explicit pending state (from useActionForm); otherwise read from the enclosing form. */
  pending?: boolean;
}) {
  const status = useFormStatus();
  const pending = pendingProp ?? status.pending;
  return (
    <Button type="submit" variant={variant} size={size} disabled={disabled || pending} aria-busy={pending || undefined} className={className} fullWidth={fullWidth}>
      {pending ? (pendingLabel ?? 'Saving…') : children}
    </Button>
  );
}

/** Shows an action's outcome; errors are announced assertively, successes politely. */
export function FormMessage({ result, className }: { result: ActionResult; className?: string }) {
  const ref = useRef<HTMLDivElement>(null);
  // Long forms put the message at the top; bring it into view when a new result arrives.
  useEffect(() => {
    if (result.status === 'idle' || !ref.current) return;
    const reduce = window.matchMedia('(prefers-reduced-motion: reduce)').matches;
    ref.current.scrollIntoView({ block: 'nearest', behavior: reduce ? 'auto' : 'smooth' });
  }, [result]);
  if (result.status === 'idle') return <div aria-live="polite" className="sr-only" />;
  if (result.status === 'error') {
    return (
      <div ref={ref} role="alert" className={cn('scroll-mt-24', className)}>
        <Notice tone="error">{result.message}</Notice>
      </div>
    );
  }
  return (
    <div ref={ref} role="status" className={cn('scroll-mt-24 space-y-3', className)}>
      <Notice tone="success">{result.message}</Notice>
      {result.secret && <OneTimeSecret label={result.secret.label} value={result.secret.value} />}
      {result.link && (
        <Link href={result.link.href} className="inline-flex min-h-11 items-center font-semibold text-violet-soft underline-offset-4 hover:text-fg hover:underline">
          {result.link.label} →
        </Link>
      )}
    </div>
  );
}

/** A temporary password shown exactly once (it is never stored in plain text). */
export function OneTimeSecret({ label, value }: { label: string; value: string }) {
  return (
    <div className="rounded-xl border border-amber/50 bg-amber/10 p-4">
      <p className="text-sm font-semibold text-amber-soft">{label}</p>
      <div className="mt-2 flex flex-wrap items-center gap-3">
        <code className="num rounded-lg bg-night px-3 py-2 font-mono text-lg tracking-wide text-fg select-all">{value}</code>
        <CopyButton value={value} label="Copy password" />
      </div>
      <p className="mt-2 text-xs text-muted">
        Copy it now and share it privately. It won’t be shown again — use “Reset password” to issue a new one.
      </p>
    </div>
  );
}

export function CopyButton({ value, label = 'Copy link', size = 'sm', variant = 'secondary' }: { value: string; label?: string; size?: ButtonSize; variant?: ButtonVariant }) {
  const [state, setState] = useState<'idle' | 'copied' | 'failed'>('idle');
  useEffect(() => {
    if (state === 'idle') return;
    const t = setTimeout(() => setState('idle'), 2000);
    return () => clearTimeout(t);
  }, [state]);
  return (
    <Button
      size={size}
      variant={variant}
      onClick={async () => {
        try {
          await navigator.clipboard.writeText(value);
          setState('copied');
        } catch {
          // Clipboard can be blocked (insecure origin, permissions); the value stays selectable.
          setState('failed');
        }
      }}
    >
      {state === 'copied' ? (
        <>
          <CheckIcon /> Copied
        </>
      ) : state === 'failed' ? (
        'Copy blocked — select the text'
      ) : (
        label
      )}
      <span className="sr-only" aria-live="polite">
        {state === 'copied' ? 'Copied to clipboard' : ''}
      </span>
    </Button>
  );
}

export function PrintButton({ label = 'Print poster' }: { label?: string }) {
  return (
    <Button size="lg" onClick={() => window.print()}>
      {label}
    </Button>
  );
}

/** Modal confirmation built on <dialog>. Rendered closed until `open`. */
export function ConfirmDialog({
  open,
  onClose,
  title,
  description,
  confirmLabel,
  tone = 'danger',
  busy = false,
  requireText,
  onConfirm,
  children,
  confirmType = 'button',
}: {
  open: boolean;
  onClose: () => void;
  title: ReactNode;
  description?: ReactNode;
  confirmLabel: ReactNode;
  tone?: 'danger' | 'primary';
  busy?: boolean;
  /** Typed confirmation: the confirm button stays disabled until this exact text is entered. */
  requireText?: string;
  onConfirm?: () => void;
  /** Extra content (e.g. hidden inputs when the dialog sits inside a form). */
  children?: ReactNode;
  confirmType?: 'button' | 'submit';
}) {
  const ref = useRef<HTMLDialogElement>(null);
  const titleId = useId();
  const descId = useId();
  const [typed, setTyped] = useState('');

  useEffect(() => {
    const dialog = ref.current;
    if (!dialog) return;
    if (open && !dialog.open) {
      setTyped('');
      dialog.showModal();
    } else if (!open && dialog.open) dialog.close();
  }, [open]);

  const blocked = requireText !== undefined && typed.trim() !== requireText;
  return (
    <dialog
      ref={ref}
      aria-labelledby={titleId}
      aria-describedby={description ? descId : undefined}
      onCancel={(e) => {
        e.preventDefault();
        if (!busy) onClose();
      }}
      onClick={(e) => {
        // Click on the backdrop (the dialog element itself) closes it.
        if (e.target === ref.current && !busy) onClose();
      }}
      className="m-auto w-[min(32rem,calc(100%-2rem))] rounded-card border border-line-strong bg-surface p-0 text-fg shadow-lift backdrop:bg-night/80 backdrop:backdrop-blur-sm"
    >
      <div className="p-6">
        <h2 id={titleId} className="font-display text-xl font-bold">
          {title}
        </h2>
        {description && (
          <div id={descId} className="mt-3 text-sm leading-relaxed text-muted">
            {description}
          </div>
        )}
        {children}
        {requireText !== undefined && (
          <label className="mt-4 flex flex-col gap-2 text-sm font-semibold">
            <span>
              Type <code className="rounded bg-night px-1.5 py-0.5 text-amber-soft">{requireText}</code> to confirm
            </span>
            <input
              value={typed}
              onChange={(e) => setTyped(e.target.value)}
              autoComplete="off"
              spellCheck={false}
              className="min-h-12 rounded-xl border border-line-strong bg-night/70 px-4 text-base font-normal text-fg focus-visible:border-cyan"
            />
          </label>
        )}
        <div className="mt-6 flex flex-col-reverse gap-3 sm:flex-row sm:justify-end">
          <Button variant="secondary" onClick={onClose} disabled={busy} autoFocus>
            Cancel
          </Button>
          <Button
            type={confirmType}
            variant={tone === 'danger' ? 'danger' : 'primary'}
            disabled={busy || blocked}
            aria-busy={busy || undefined}
            onClick={confirmType === 'button' ? onConfirm : undefined}
          >
            {busy ? 'Working…' : confirmLabel}
          </Button>
        </div>
      </div>
    </dialog>
  );
}

/**
 * A button that asks for confirmation, then submits a server action with fixed hidden fields.
 * The result message renders next to the button; a success with `redirectTo` navigates afterwards.
 */
export function ConfirmActionButton({
  action,
  fields,
  label,
  title,
  description,
  confirmLabel,
  variant = 'secondary',
  size = 'sm',
  tone = 'danger',
  requireText,
  disabled,
  align = 'start',
}: {
  action: (prev: ActionResult, formData: FormData) => Promise<ActionResult>;
  fields: Record<string, string | number>;
  label: ReactNode;
  title: ReactNode;
  description?: ReactNode;
  confirmLabel: ReactNode;
  variant?: ButtonVariant;
  size?: ButtonSize;
  tone?: 'danger' | 'primary';
  requireText?: string;
  disabled?: boolean;
  /** Table action cells align to the end so the button stays put when a message appears. */
  align?: 'start' | 'end';
}) {
  const [open, setOpen] = useState(false);
  const [result, formAction, pending] = useActionState(action, IDLE);
  const router = useRouter();
  const lastHandled = useRef<ActionResult>(IDLE);

  useEffect(() => {
    if (result === lastHandled.current || pending) return;
    lastHandled.current = result;
    if (result.status !== 'idle') setOpen(false);
    if (result.status === 'success' && result.redirectTo) router.push(result.redirectTo);
  }, [result, pending, router]);

  return (
    <div className={cn('flex flex-col gap-2', align === 'end' ? 'items-end text-left' : 'items-start')}>
      <Button variant={variant} size={size} onClick={() => setOpen(true)} disabled={disabled}>
        {label}
      </Button>
      <form action={formAction}>
        {Object.entries(fields).map(([name, value]) => (
          <input key={name} type="hidden" name={name} value={value} />
        ))}
        <ConfirmDialog
          open={open}
          onClose={() => setOpen(false)}
          title={title}
          description={description}
          confirmLabel={confirmLabel}
          tone={tone}
          busy={pending}
          requireText={requireText}
          confirmType="submit"
        />
      </form>
      {result.status !== 'idle' && !(result.status === 'success' && result.redirectTo) && <FormMessage result={result} className="w-full" />}
    </div>
  );
}

export interface NavItem {
  href: string;
  label: string;
  /** Exact match only (for section roots like /host). */
  exact?: boolean;
}

export function NavLinks({ items, label }: { items: readonly NavItem[]; label: string }) {
  const pathname = usePathname();
  return (
    <nav aria-label={label} className="-mx-1 overflow-x-auto [scrollbar-width:none] [&::-webkit-scrollbar]:hidden">
      <ul className="flex items-center gap-1 px-1">
        {items.map((item) => {
          const active = item.exact ? pathname === item.href : pathname === item.href || pathname.startsWith(`${item.href}/`);
          return (
            <li key={item.href}>
              <Link
                href={item.href}
                aria-current={active ? 'page' : undefined}
                className={cn(
                  'inline-flex min-h-11 items-center rounded-xl px-3.5 text-sm font-semibold whitespace-nowrap transition-colors',
                  active ? 'bg-violet/20 text-fg shadow-[inset_0_0_0_1px_rgb(124_92_255/0.55)]' : 'text-muted hover:bg-white/[0.06] hover:text-fg',
                )}
              >
                {item.label}
              </Link>
            </li>
          );
        })}
      </ul>
    </nav>
  );
}
