import { getFormProps, getInputProps, useForm } from "@conform-to/react";
import { conformZodMessage, parseWithZod } from "@conform-to/zod";
import { ArrowRightIcon, EnvelopeIcon, UserIcon } from "@heroicons/react/20/solid";
import { HandRaisedIcon } from "@heroicons/react/24/solid";
import { RadioGroup } from "@radix-ui/react-radio-group";
import { json, type ActionFunction } from "@remix-run/node";
import { Form, useActionData, useNavigation } from "@remix-run/react";
import { motion } from "framer-motion";
import { forwardRef, useEffect, useState } from "react";
import { z } from "zod";
import { UserGroupIcon } from "~/assets/icons/UserGroupIcon";
import { BackgroundWrapper } from "~/components/BackgroundWrapper";
import { AppContainer, MainCenteredContainer } from "~/components/layout/AppLayout";
import { Button } from "~/components/primitives/Buttons";
import { Fieldset } from "~/components/primitives/Fieldset";
import { FormButtons } from "~/components/primitives/FormButtons";
import { FormError } from "~/components/primitives/FormError";
import { FormTitle } from "~/components/primitives/FormTitle";
import { Input } from "~/components/primitives/Input";
import { InputGroup } from "~/components/primitives/InputGroup";
import { Label } from "~/components/primitives/Label";
import { RadioGroupItem } from "~/components/primitives/RadioButton";
import { Select, SelectItem } from "~/components/primitives/Select";
import { prisma } from "~/db.server";
import { useFeatures } from "~/hooks/useFeatures";
import { useUser } from "~/hooks/useUser";
import { redirectWithSuccessMessage } from "~/models/message.server";
import { updateUser } from "~/models/user.server";
import { requireUserId } from "~/services/session.server";
import { rootPath } from "~/utils/pathBuilder";
import { getVercelInstallParams } from "~/v3/vercel";

const referralSourceOptions = [
  "Search engine",
  "YouTube",
  "Twitter/X",
  "LinkedIn",
  "Word of mouth",
  "AI assistant/LLM",
  "Blog/article",
  "Event",
  "Other",
] as const;

const roleOptions = [
  "Founder",
  "Staff/principal engineer",
  "Senior software engineer",
  "Software engineer",
  "AI/ML engineer",
  "Engineering manager",
  "Product engineer",
  "Non technical builder using AI tools",
  "Student/learner",
  "Other",
] as const;

function shuffleArray<T>(arr: T[]): T[] {
  const shuffled = [...arr];
  for (let i = shuffled.length - 1; i > 0; i--) {
    const j = Math.floor(Math.random() * (i + 1));
    [shuffled[i], shuffled[j]] = [shuffled[j], shuffled[i]];
  }
  return shuffled;
}

function createSchema(
  constraints: {
    isEmailUnique?: (email: string) => Promise<boolean>;
  } = {}
) {
  return z
    .object({
      name: z.string().min(3, "Your name must be at least 3 characters").max(50),
      email: z
        .string()
        .email()
        .superRefine((email, ctx) => {
          if (constraints.isEmailUnique === undefined) {
            ctx.addIssue({
              code: z.ZodIssueCode.custom,
              message: conformZodMessage.VALIDATION_UNDEFINED,
            });
          } else {
            return constraints.isEmailUnique(email).then((isUnique) => {
              if (isUnique) {
                return;
              }

              ctx.addIssue({
                code: z.ZodIssueCode.custom,
                message: "Email is already being used by a different account",
              });
            });
          }
        }),
      confirmEmail: z.string(),
      referralSource: z.string().optional(),
      referralSourceOther: z.string().optional(),
      role: z.string().optional(),
      roleOther: z.string().optional(),
      referralSourcePosition: z.coerce.number().optional(),
      rolePosition: z.coerce.number().optional(),
    })
    .refine((value) => value.email === value.confirmEmail, {
      message: "Emails must match",
      path: ["confirmEmail"],
    });
}

export const action: ActionFunction = async ({ request }) => {
  const userId = await requireUserId(request);
  const formData = await request.formData();

  const formSchema = createSchema({
    isEmailUnique: async (email) => {
      const existingUser = await prisma.user.findFirst({
        where: {
          email,
        },
      });

      if (!existingUser) {
        return true;
      }

      if (existingUser.id === userId) {
        return true;
      }

      return false;
    },
  });

  const submission = await parseWithZod(formData, { schema: formSchema, async: true });

  if (submission.status !== "success") {
    return json(submission.reply());
  }

  try {
    const onboardingData: Record<string, string | undefined> = {};

    if (submission.value.referralSource) {
      onboardingData.referralSource = submission.value.referralSource;
      if (submission.value.referralSourcePosition) {
        onboardingData.referralSourcePosition = String(submission.value.referralSourcePosition);
      }
      if (submission.value.referralSource === "Other" && submission.value.referralSourceOther) {
        onboardingData.referralSourceOther = submission.value.referralSourceOther;
      }
    }

    if (submission.value.role) {
      onboardingData.role = submission.value.role;
      if (submission.value.rolePosition) {
        onboardingData.rolePosition = String(submission.value.rolePosition);
      }
      if (submission.value.role === "Other" && submission.value.roleOther) {
        onboardingData.roleOther = submission.value.roleOther;
      }
    }

    const referralSourceForLegacy =
      submission.value.referralSource === "Other" && submission.value.referralSourceOther
        ? `Other: ${submission.value.referralSourceOther}`
        : submission.value.referralSource;

    await updateUser({
      id: userId,
      name: submission.value.name,
      email: submission.value.email,
      referralSource: referralSourceForLegacy,
      onboardingData: Object.keys(onboardingData).length > 0 ? onboardingData : undefined,
    });

    const vercelParams = getVercelInstallParams(request);
    let redirectUrl = rootPath();

    if (vercelParams) {
      const params = new URLSearchParams({
        code: vercelParams.code,
        configurationId: vercelParams.configurationId,
        integration: "vercel",
      });
      if (vercelParams.next) {
        params.set("next", vercelParams.next);
      }
      redirectUrl = `/orgs/new?${params.toString()}`;
    }

    return redirectWithSuccessMessage(redirectUrl, request, "Your details have been updated.");
  } catch (error: any) {
    return json({ errors: { body: error.message } }, { status: 400 });
  }
};

const HandIcon = forwardRef<HTMLDivElement, {}>(({}, ref) => {
  return (
    <div ref={ref}>
      <HandRaisedIcon className="h-7 w-7 text-amber-400" />
    </div>
  );
});
const MotionHand = motion(HandIcon);

export default function Page() {
  const user = useUser();
  const lastSubmission = useActionData();
  const [enteredEmail, setEnteredEmail] = useState<string>(user.email ?? "");
  const { isManagedCloud } = useFeatures();
  const navigation = useNavigation();
  const isSubmitting = navigation.state === "submitting" || navigation.state === "loading";
  const [selectedReferralSource, setSelectedReferralSource] = useState<string | undefined>();
  const [selectedRole, setSelectedRole] = useState<string>("");

  const [shuffledReferralSources, setShuffledReferralSources] = useState<string[]>([
    ...referralSourceOptions,
  ]);
  const [shuffledRoles, setShuffledRoles] = useState<string[]>([...roleOptions]);

  useEffect(() => {
    const nonOtherReferral = referralSourceOptions.filter((r) => r !== "Other");
    setShuffledReferralSources([...shuffleArray(nonOtherReferral), "Other"]);

    const nonOtherRoles = roleOptions.filter((r) => r !== "Other");
    setShuffledRoles([...shuffleArray(nonOtherRoles), "Other"]);
  }, []);

  const [form, { name, email, confirmEmail }] = useForm({
    id: "confirm-basic-details",
    lastResult: lastSubmission as any,
    onValidate({ formData }) {
      return parseWithZod(formData, { schema: createSchema() });
    },
    shouldRevalidate: "onSubmit",
  });

  const shouldShowConfirm = user.email !== enteredEmail || user.email === "";

  return (
    <AppContainer className="bg-background-deep">
      <BackgroundWrapper>
        <MainCenteredContainer
          variant="onboarding"
          className="max-w-116 rounded-lg border border-grid-bright bg-background-dimmed p-5 shadow-lg"
        >
          <Form method="post" {...getFormProps(form)}>
            <FormTitle
              title="Welcome to Trigger.dev"
              LeadingIcon={
                <MotionHand
                  style={{
                    originY: 0.75,
                  }}
                  initial={{
                    rotate: 0,
                  }}
                  animate={{
                    rotate: [0, -20, 0, 20, 0, -20, 0, 20, 0],
                  }}
                  transition={{
                    delay: 1,
                    duration: 1,
                    repeatDelay: 5,
                    repeat: Infinity,
                    ease: "linear",
                  }}
                />
              }
              description="We just need you to confirm a couple of details."
            />
            <Fieldset>
              <InputGroup>
                <Label htmlFor={name.id}>
                  Full name <span className="text-text-bright">*</span>
                </Label>
                <Input
                  {...getInputProps(name, { type: "text" })}
                  defaultValue={user.name ?? ""}
                  placeholder="Your full name"
                  icon={UserIcon}
                  autoFocus
                />
                <FormError id={name.errorId}>{name.errors}</FormError>
              </InputGroup>
              <InputGroup>
                <Label htmlFor={email.id}>
                  Email <span className="text-text-bright">*</span>
                </Label>
                <Input
                  {...getInputProps(email, { type: "email" })}
                  defaultValue={enteredEmail}
                  onChange={(e) => {
                    setEnteredEmail(e.target.value);
                  }}
                  placeholder="Your email address"
                  icon={EnvelopeIcon}
                  spellCheck={false}
                />
                <FormError id={email.errorId}>{email.errors}</FormError>
              </InputGroup>

              {shouldShowConfirm ? (
                <InputGroup>
                  <Label htmlFor={confirmEmail.id}>Confirm email</Label>
                  <Input
                    {...getInputProps(confirmEmail, { type: "email" })}
                    placeholder="Your email, again"
                    icon={EnvelopeIcon}
                    spellCheck={false}
                  />
                  <FormError id={confirmEmail.errorId}>{confirmEmail.errors}</FormError>
                </InputGroup>
              ) : (
                <>
                  <input {...getInputProps(confirmEmail, { type: "hidden" })} value={user.email} />
                </>
              )}

              {isManagedCloud && (
                <>
                  <div className="border-t border-grid-bright" />
                  <InputGroup>
                    <Label className="mb-0.5" id="referral-label">
                      How did you hear about us?
                    </Label>
                    <input
                      type="hidden"
                      name="referralSource"
                      value={selectedReferralSource ?? ""}
                    />
                    <input
                      type="hidden"
                      name="referralSourcePosition"
                      value={
                        selectedReferralSource
                          ? shuffledReferralSources.indexOf(selectedReferralSource) + 1
                          : ""
                      }
                    />
                    <RadioGroup
                      value={selectedReferralSource}
                      onValueChange={setSelectedReferralSource}
                      className="flex flex-wrap gap-2"
                      aria-labelledby="referral-label"
                    >
                      {shuffledReferralSources.map((option) => (
                        <RadioGroupItem
                          key={option}
                          id={`referral-${option}`}
                          label={option}
                          value={option}
                          variant="button/small"
                        />
                      ))}
                    </RadioGroup>
                    {selectedReferralSource === "Other" && (
                      <div className="mt-2">
                        <Input
                          name="referralSourceOther"
                          type="text"
                          placeholder="What was the source?"
                          spellCheck={false}
                        />
                      </div>
                    )}
                  </InputGroup>

                  <InputGroup className="mt-1">
                    <Label id="role-label">What role fits you best?</Label>
                    <input type="hidden" name="role" value={selectedRole} />
                    <input
                      type="hidden"
                      name="rolePosition"
                      value={selectedRole ? shuffledRoles.indexOf(selectedRole) + 1 : ""}
                    />
                    <Select<string, string>
                      value={selectedRole}
                      setValue={setSelectedRole}
                      placeholder="Select an option"
                      aria-labelledby="role-label"
                      variant="secondary/small"
                      dropdownIcon
                      icon={<UserGroupIcon className="mr-1 size-4.5 text-text-dimmed" />}
                      items={shuffledRoles}
                      className="h-8 min-w-0 border-0 bg-background-hover pl-2 text-sm text-text-dimmed ring-border-bright transition hover:bg-secondary hover:text-text-dimmed hover:ring-1"
                      text={(v) => (v ? <span className="text-text-bright">{v}</span> : undefined)}
                    >
                      {(items) =>
                        items.map((item) => (
                          <SelectItem key={item} value={item}>
                            <span className="text-text-bright">{item}</span>
                          </SelectItem>
                        ))
                      }
                    </Select>
                    {selectedRole === "Other" && (
                      <div>
                        <Input
                          name="roleOther"
                          type="text"
                          placeholder="What's your role?"
                          spellCheck={false}
                        />
                      </div>
                    )}
                  </InputGroup>
                </>
              )}

              <FormButtons
                confirmButton={
                  <Button
                    type="submit"
                    variant={"primary/small"}
                    TrailingIcon={ArrowRightIcon}
                    isLoading={isSubmitting}
                  >
                    Continue
                  </Button>
                }
              />
            </Fieldset>
          </Form>
        </MainCenteredContainer>
      </BackgroundWrapper>
    </AppContainer>
  );
}
