"use client";

import { CheckIcon } from "lucide-react";
import { useActionState, useId } from "react";
import { useFormStatus } from "react-dom";
import { type NewsletterState, subscribeNewsletter } from "@/app/actions/newsletter";
import type { NewsletterLabels } from "@/components/footer/newsletter-labels";
import { Button } from "@/components/ui/button";
import { Field, FieldLabel } from "@/components/ui/field";
import { Input } from "@/components/ui/input";
import { cn } from "@/lib/utils";

const INITIAL: NewsletterState = { status: "idle" };

/**
 * `useFormStatus` reads the pending state of the enclosing `<form>`, so the
 * button has to be a child component — that is the whole reason it is split
 * out.
 */
function SubmitButton({ label, pendingLabel }: { label: string; pendingLabel: string }) {
  const { pending } = useFormStatus();

  return (
    <Button
      type="submit"
      variant="cta-inverse"
      size="cta-lg"
      disabled={pending}
      className="w-full sm:w-auto"
    >
      {pending ? pendingLabel : label}
    </Button>
  );
}

type Props = {
  labels: NewsletterLabels;
};

export default function NewsletterForm({ labels }: Props) {
  const [state, action] = useActionState(subscribeNewsletter, INITIAL);
  // The form is mounted twice on /blog (footer + journal band): hardcoded ids
  // would duplicate them and make every `htmlFor` point at the first match.
  const emailId = useId();
  const consentId = useId();
  const consentTextId = useId();
  const honeypotId = useId();
  const statusId = useId();

  const invalid = state.status === "error";
  const message = invalid
    ? labels.errors[state.error ?? "invalid"]
    : state.status === "unavailable"
      ? labels.unavailable
      : null;

  return (
    <form action={action} className="max-w-none lg:max-w-140">
      <div className="flex flex-col gap-5.5 sm:flex-row sm:flex-wrap sm:items-end sm:gap-4.5">
        <Field className="flex-1 gap-2 sm:min-w-60">
          {/* « Machined » field of docs/brand.md : Label-Caps above a 1px
              bottom rule, square corners, rule darkening on focus. */}
          <FieldLabel
            htmlFor={emailId}
            className="text-micro font-semibold uppercase tracking-caps text-on-inverse-surface/70"
          >
            {labels.emailLabel}
          </FieldLabel>
          <Input
            id={emailId}
            name="email"
            type="email"
            required
            autoComplete="email"
            inputMode="email"
            aria-invalid={invalid}
            aria-describedby={statusId}
            // `md:text-[15px]` is not redundant: the shadcn base carries
            // `md:text-sm`, which `tailwind-merge` keeps (different modifier)
            // and which would otherwise shrink the field from `lg` upwards.
            className="h-auto rounded-none border-0 border-b border-brushed-steel bg-transparent px-0.5 py-3.5 text-[15px] text-on-inverse-surface transition-colors placeholder:text-on-inverse-surface/50 focus-visible:border-on-inverse-surface focus-visible:ring-0 md:text-[15px]"
          />
        </Field>

        <SubmitButton label={labels.submit} pendingLabel={labels.sending} />
      </div>

      {/* Honeypot: out of the tab order and out of the accessibility tree.
          `display:none` also keeps it out of autofill. */}
      <div aria-hidden="true" className="hidden">
        <input
          id={honeypotId}
          name="website"
          type="text"
          aria-label="Website"
          tabIndex={-1}
          autoComplete="off"
          defaultValue=""
        />
      </div>

      <Field orientation="horizontal" className="mt-6 items-start">
        <FieldLabel
          htmlFor={consentId}
          className="items-start gap-3 text-sm leading-body font-normal text-on-inverse-surface/70"
        >
          <input
            id={consentId}
            name="consent"
            type="checkbox"
            required
            aria-labelledby={consentTextId}
            className="peer sr-only"
          />
          {/* Square control: no shadcn `Checkbox` in this project, and a native
              checkbox cannot be squared off (docs/brand.md § Shapes). The real
              input stays in the DOM and keeps every native behaviour. */}
          <span
            aria-hidden="true"
            className="mt-0.5 flex size-4 shrink-0 items-center justify-center border border-on-inverse-surface/45 text-transparent transition-colors peer-checked:border-gold-bright peer-checked:bg-gold-bright peer-checked:text-foreground peer-focus-visible:outline-2 peer-focus-visible:outline-offset-2 peer-focus-visible:outline-background"
          >
            <CheckIcon className="size-3" strokeWidth={3} />
          </span>
          <span id={consentTextId}>{labels.consent}</span>
        </FieldLabel>
      </Field>

      {/* Always mounted: a live region inserted at the same time as its content
          is not reliably announced (NVDA / JAWS / VoiceOver). `<output>` carries
          the implicit `role="status"`. */}
      <output
        id={statusId}
        aria-live="polite"
        aria-atomic="true"
        className={cn(
          "mt-4 block min-h-5 text-sm",
          state.status === "unavailable" ? "text-gold-bright" : "text-on-inverse-surface/70",
        )}
      >
        {message}
      </output>
    </form>
  );
}
