import { Form } from "@ariakit/react";
import {
  getSubmitFailureMessage,
  Letterhead,
  LetterheadFormActions,
  LetterheadHeader,
  LetterheadHeading,
  LetterheadParagraph,
  LetterheadSubmitButton,
  LetterheadSubmitError,
  LetterheadTextField,
  useForm,
  validEmail,
} from "@indietabletop/appkit";
import {
  useState,
  type Dispatch,
  type ReactNode,
  type SetStateAction,
} from "react";
import { useAppConfig, useClient } from "../AppConfig/AppConfig.tsx";
import { LetterheadInfoCard } from "./LetterheadInfoCard.tsx";
import * as css from "./style.css.ts";

type SetStep = Dispatch<SetStateAction<SubscribeStep>>;

function SubscribeView(
  props: ViewContent & {
    setStep: SetStep;
  },
) {
  const { placeholders } = useAppConfig();
  const { title, description, setStep } = props;

  const client = useClient();
  const { form, submitName } = useForm({
    defaultValues: {
      email: "",
    },
    validate: {
      email: validEmail,
    },
    async onSubmit({ values }) {
      const result = await client.subscribeToNewsletterByEmail(
        "changelog",
        values.email,
      );

      return result.mapFailure(getSubmitFailureMessage);
    },
    async onSuccess({ tokenId }, { values }) {
      setStep({ type: "SUBMIT_CODE", email: values.email, tokenId });
    },
  });

  return (
    <Form store={form} resetOnSubmit={false}>
      <Letterhead>
        <LetterheadHeader>
          <LetterheadHeading className={css.card.heading}>
            {title}
          </LetterheadHeading>

          {description}
        </LetterheadHeader>

        <LetterheadTextField
          name={form.names.email}
          label="Your email"
          placeholder={placeholders.email}
          autoComplete="email"
          required
        />

        <LetterheadFormActions>
          <LetterheadSubmitError name={submitName} />
          <LetterheadSubmitButton>Subscribe</LetterheadSubmitButton>
        </LetterheadFormActions>
      </Letterhead>
    </Form>
  );
}

function SubmitCodeStep(props: {
  tokenId: string;
  setStep: SetStep;
  email: string;
}) {
  const { tokenId, email, setStep } = props;
  const client = useClient();

  const { form, submitName } = useForm({
    defaultValues: {
      code: "",
    },
    async onSubmit({ values }) {
      const op = await client.confirmNewsletterSignup(
        "changelog",
        tokenId,
        values.code,
      );

      return op.mapFailure((failure) => {
        return getSubmitFailureMessage(failure, {
          404: "🚫 This code is incorrect or expired. Please try again.",
        });
      });
    },
    onSuccess() {
      setStep({ type: "SUBSCRIBE_SUCCESS" });
    },
  });

  return (
    <Form store={form} resetOnSubmit={false}>
      <Letterhead>
        <LetterheadHeader>
          <LetterheadHeading>Confirm subscription</LetterheadHeading>
          <LetterheadParagraph>
            We've sent a one-time code to <em>{email}</em>. Please, enter the
            code in the field below to confirm your newsletter subscription.
          </LetterheadParagraph>
        </LetterheadHeader>

        <LetterheadTextField
          name={form.names.code}
          label="Code"
          placeholder="E.g. 123123"
          autoComplete="one-time-code"
          required
        />

        <LetterheadFormActions>
          <LetterheadSubmitError name={submitName} />
          <LetterheadSubmitButton>Confirm</LetterheadSubmitButton>
        </LetterheadFormActions>
      </Letterhead>
    </Form>
  );
}

type SubscribeStep =
  | { type: "SUBSCRIBE" }
  | { type: "SUBMIT_CODE"; email: string; tokenId: string }
  | { type: "SUBSCRIBE_SUCCESS" };

type ViewContent = {
  title: ReactNode;
  description: ReactNode;
};

/**
 * Allows users to sign up to the newsletter using an arbitrary email.
 *
 * Signups are verified using an one-time code, which is sent to the supplied
 * email address.
 */
export function SubscribeByEmailCard(props: {
  content: Record<Exclude<SubscribeStep["type"], "SUBMIT_CODE">, ViewContent>;
}) {
  const { content } = props;
  const [step, setStep] = useState<SubscribeStep>({ type: "SUBSCRIBE" });

  switch (step.type) {
    case "SUBSCRIBE": {
      return <SubscribeView {...content[step.type]} setStep={setStep} />;
    }

    case "SUBMIT_CODE": {
      return (
        <SubmitCodeStep
          email={step.email}
          tokenId={step.tokenId}
          setStep={setStep}
        />
      );
    }

    case "SUBSCRIBE_SUCCESS": {
      return <LetterheadInfoCard {...content[step.type]} />;
    }
  }
}
