"use client";

/**
 * Walk-in registration from the open / QR link (FR-2.10, FR-2.12).
 *
 * Differs from the tokenized form in two ways: it asks which events the visitor is
 * responding to, and it carries a Turnstile challenge. The tokenized flow stays
 * frictionless because holding the token is already evidence of an invitation.
 */
import { useCallback, useState } from "react";

import { Turnstile } from "@/components/Turnstile";
import type { Dictionary } from "@/lib/i18n";
import { eventTitle, type Locale } from "@/lib/i18n";

interface SelectableEvent {
  type: string;
  titleBn: string;
  titleEn: string;
}

export function OpenRsvpInteractive({
  slug,
  rsvpOpen,
  selectableEvents,
  defaultEventType,
  locale,
  dict,
  hostContactPhone,
  turnstileSiteKey,
}: {
  slug: string;
  rsvpOpen: boolean;
  selectableEvents: SelectableEvent[];
  defaultEventType: string;
  locale: Locale;
  dict: Dictionary;
  hostContactPhone: string | null;
  /** Resolved on the server; null when no key is configured (see `lib/public-config`). */
  turnstileSiteKey: string | null;
}) {
  const [stage, setStage] = useState<"idle" | "form" | "success">("idle");
  const [submitting, setSubmitting] = useState(false);
  const [error, setError] = useState<string | null>(null);
  const [partySize, setPartySize] = useState(1);
  const [turnstileToken, setTurnstileToken] = useState<string | null>(null);
  const [challengeEpoch, setChallengeEpoch] = useState(0);

  const handleToken = useCallback((token: string | null) => setTurnstileToken(token), []);

  // The button waits for the challenge only when there is one to wait for. Cloudflare's
  // managed widget usually resolves in well under a second with no interaction, so this is
  // a brief disabled state rather than a step the guest has to notice.
  const challengePending = Boolean(turnstileSiteKey) && turnstileToken === null;

  if (!rsvpOpen) {
    return (
      <section className="rounded-2xl border p-6 text-center">
        <h2 className="text-lg font-semibold">{dict.rsvpClosed}</h2>
        {hostContactPhone && (
          <p className="mt-2 text-sm">
            {dict.rsvpClosedHelp}{" "}
            <a className="underline" href={`tel:${hostContactPhone}`}>
              {hostContactPhone}
            </a>
          </p>
        )}
      </section>
    );
  }

  if (stage === "success") {
    return (
      <section
        className="rounded-2xl border p-6 text-center"
        role="status"
        aria-live="polite"
      >
        <h2 className="text-xl font-semibold">{dict.thankYou}</h2>
        <p className="mt-2 text-sm">
          {dict.guestsConfirmed}: <strong>{partySize}</strong>
        </p>
      </section>
    );
  }

  if (stage === "idle") {
    return (
      <button
        onClick={() => setStage("form")}
        className="min-h-12 w-full rounded-full bg-black px-6 text-base font-medium text-white"
      >
        {dict.accept}
      </button>
    );
  }

  async function handleSubmit(event: React.FormEvent<HTMLFormElement>) {
    event.preventDefault();
    if (submitting) return;

    const data = new FormData(event.currentTarget);
    const eventTypes = data.getAll("event_types").map(String);
    if (eventTypes.length === 0) {
      setError(dict.selectAtLeastOneEvent);
      return;
    }

    setSubmitting(true);
    setError(null);

    try {
      const response = await fetch(`/api/events/${encodeURIComponent(slug)}/rsvp`, {
        method: "POST",
        headers: { "content-type": "application/json" },
        body: JSON.stringify({
          name: String(data.get("name") ?? "").trim(),
          phone: String(data.get("phone") ?? "").trim(),
          email: String(data.get("email") ?? "").trim() || null,
          party_size: Number(data.get("party_size") ?? 1),
          event_types: eventTypes,
          turnstile_token: turnstileToken,
        }),
      });

      if (response.status === 429) {
        setError(dict.tooManyRequests);
        return;
      }
      if (!response.ok) {
        // A token is single-use, so any rejection burns the one we hold. Reset before the
        // guest can press the button again — otherwise every retry re-sends a token
        // Cloudflare has already spent and fails for a reason nothing on screen explains.
        setChallengeEpoch((n) => n + 1);
        setError(response.status === 403 ? dict.verificationFailed : dict.formError);
        return;
      }

      setPartySize(Number(data.get("party_size") ?? 1));
      setStage("success");
    } catch {
      // The request may or may not have reached the server, so the token's state is
      // unknown. Assume spent.
      setChallengeEpoch((n) => n + 1);
      setError(dict.formError);
    } finally {
      setSubmitting(false);
    }
  }

  return (
    <form onSubmit={handleSubmit} className="flex flex-col gap-4 rounded-2xl border p-6">
      <fieldset className="flex flex-col gap-2">
        <legend className="text-sm font-medium">{dict.whichEvents}</legend>
        {selectableEvents.map((e) => (
          <label key={e.type} className="flex min-h-11 items-center gap-3">
            <input
              type="checkbox"
              name="event_types"
              value={e.type}
              defaultChecked={e.type === defaultEventType}
              className="size-5"
            />
            <span className="text-base">{eventTitle(locale, e)}</span>
          </label>
        ))}
      </fieldset>

      <label className="flex flex-col gap-1">
        <span className="text-sm font-medium">
          {dict.yourName} <span aria-hidden>*</span>
        </span>
        <input
          name="name"
          required
          autoComplete="name"
          className="min-h-11 rounded-lg border px-3 py-2 text-base"
        />
      </label>

      <label className="flex flex-col gap-1">
        <span className="text-sm font-medium">
          {dict.phone} <span aria-hidden>*</span>
        </span>
        <input
          name="phone"
          required
          type="tel"
          inputMode="tel"
          placeholder="01712345678"
          autoComplete="tel"
          className="min-h-11 rounded-lg border px-3 py-2 text-base"
        />
      </label>

      <label className="flex flex-col gap-1">
        <span className="text-sm font-medium">
          {dict.email} <span className="font-normal opacity-60">({dict.optional})</span>
        </span>
        <input
          name="email"
          type="email"
          autoComplete="email"
          aria-describedby="open-email-help"
          className="min-h-11 rounded-lg border px-3 py-2 text-base"
        />
        <span id="open-email-help" className="text-xs opacity-70">
          {dict.emailHelp}
        </span>
      </label>

      <label className="flex flex-col gap-1">
        <span className="text-sm font-medium">{dict.guestsAttending}</span>
        <select
          name="party_size"
          defaultValue={1}
          className="min-h-11 rounded-lg border px-3 py-2 text-base"
        >
          {[1, 2, 3, 4, 5].map((n) => (
            <option key={n} value={n}>
              {n}
            </option>
          ))}
        </select>
      </label>

      {/* No key configured means no widget can be rendered. The submit stays enabled
          regardless: the server is the gate, and a guest standing at the venue should not
          be blocked by a deployment mistake they cannot see or fix. */}
      {turnstileSiteKey && (
        <Turnstile
          siteKey={turnstileSiteKey}
          onToken={handleToken}
          resetSignal={challengeEpoch}
        />
      )}

      {error && (
        <p role="alert" className="text-sm text-red-600">
          {error}
        </p>
      )}

      <button
        type="submit"
        disabled={submitting || challengePending}
        className="min-h-12 rounded-full bg-black px-6 text-base font-medium text-white disabled:opacity-60"
      >
        {submitting ? dict.submitting : challengePending ? dict.verifying : dict.confirm}
      </button>

      <p className="text-xs opacity-60">{dict.privacyNotice}</p>
    </form>
  );
}
