"use client";

import { AnimatePresence, motion } from "framer-motion";
import { useEffect, useState } from "react";
import { useFormContext } from "react-hook-form";

import LicenseRequired from "@calcom/features/ee/common/components/LicenseRequired";
import { useLocale } from "@calcom/lib/hooks/useLocale";
import { trpc } from "@calcom/trpc/react";
import classNames from "@calcom/ui/classNames";
import { Form } from "@calcom/ui/components/form";
import { showToast } from "@calcom/ui/components/toast";

import type { SingleFormComponentProps } from "../types/shared";
import type { RoutingFormWithResponseCount } from "../types/types";
import type { NewFormDialogState } from "./FormActions";
import { FormActionsProvider } from "./FormActions";
import { InfoLostWarningDialog } from "./InfoLostWarningDialog";
import { Header } from "./_components/Header";
import { TestFormRenderer, type UptoDateForm } from "./_components/TestForm";

const BREAKPOINTS = {
  sm: 640,
  md: 768,
  lg: 1024,
  xl: 1280,
  "2xl": 1536,
} as const;

type Breakpoint = keyof typeof BREAKPOINTS;
type BreakpointState = Record<Breakpoint, boolean>;

function useBreakPoints() {
  const [breakpoints, setBreakpoints] = useState<BreakpointState>({
    sm: false,
    md: false,
    lg: false,
    xl: false,
    "2xl": false,
  });

  useEffect(() => {
    const updateBreakpoints = () => {
      const width = window.innerWidth;
      setBreakpoints({
        sm: width >= BREAKPOINTS.sm,
        md: width >= BREAKPOINTS.md,
        lg: width >= BREAKPOINTS.lg,
        xl: width >= BREAKPOINTS.xl,
        "2xl": width >= BREAKPOINTS["2xl"],
      });
    };

    // Initial check
    updateBreakpoints();

    // Add resize listener
    window.addEventListener("resize", updateBreakpoints);
    return () => window.removeEventListener("resize", updateBreakpoints);
  }, []);

  return {
    ...breakpoints,
    // Convenience properties
    isMobile: !breakpoints.md,
    isTablet: breakpoints.md && !breakpoints.lg,
    isDesktop: breakpoints.lg,
  };
}

/**
 * It has the the ongoing changes in the form along with enrichedWithUserProfileForm specific data.
 * So, it can be used to test the form in the test preview dialog without saving the changes even.
 */
function SingleForm({ form, appUrl, Page, enrichedWithUserProfileForm }: SingleFormComponentProps) {
  const utils = trpc.useUtils();
  const { t } = useLocale();
  const [newFormDialogState, setNewFormDialogState] = useState<NewFormDialogState>(null);
  const [isTestPreviewOpen, setIsTestPreviewOpen] = useState(false);
  const [skipFirstUpdate, setSkipFirstUpdate] = useState(true);
  const [showInfoLostDialog, setShowInfoLostDialog] = useState(false);
  const hookForm = useFormContext<RoutingFormWithResponseCount>();
  const { isDesktop } = useBreakPoints();

  useEffect(() => {
    //  The first time a tab is opened, the hookForm copies the form data (saved version, from the backend),
    // and then it is considered the source of truth.

    // There are two events we need to overwrite the hookForm data with the form data coming from the server.

    // 1 - When we change the edited form.

    // 2 - When the form is saved elsewhere (such as in another browser tab)

    // In the second case. We skipped the first execution of useEffect to differentiate a tab change from a form change,
    // because each time a tab changes, a new component is created and another useEffect is executed.
    // An update from the form always occurs after the first useEffect execution.
    if (Object.keys(hookForm.getValues()).length === 0 || hookForm.getValues().id !== form.id) {
      hookForm.reset(form);
    }

    if (skipFirstUpdate) {
      setSkipFirstUpdate(false);
    } else {
      hookForm.reset(form);
    }
    // eslint-disable-next-line react-hooks/exhaustive-deps
  }, [form]);
  const mutation = trpc.viewer.appRoutingForms.formMutation.useMutation({
    onSuccess() {
      showToast(t("form_updated_successfully"), "success");
    },
    onError(e) {
      if (e.message) {
        showToast(e.message, "error");
        return;
      }
      showToast(`Something went wrong`, "error");
    },
    onSettled() {
      utils.viewer.appRoutingForms.formQuery.invalidate({ id: form.id });
    },
  });
  const uptoDateForm = {
    ...hookForm.getValues(),
    routes: hookForm.watch("routes"),
    user: enrichedWithUserProfileForm.user,
    team: enrichedWithUserProfileForm.team,
    nonOrgUsername: enrichedWithUserProfileForm.nonOrgUsername,
    nonOrgTeamslug: enrichedWithUserProfileForm.nonOrgTeamslug,
    userOrigin: enrichedWithUserProfileForm.userOrigin,
    teamOrigin: enrichedWithUserProfileForm.teamOrigin,
  } as UptoDateForm;

  const handleSubmit = (data: RoutingFormWithResponseCount) => {
    mutation.mutate({
      ...data,
    });
  };

  return (
    <>
      <Form form={hookForm} handleSubmit={handleSubmit}>
        <FormActionsProvider
          appUrl={appUrl}
          newFormDialogState={newFormDialogState}
          setNewFormDialogState={setNewFormDialogState}>
          <div className="flex h-full min-h-screen w-full flex-col">
            <Header
              routingForm={form}
              isSaving={mutation.isPending}
              appUrl={appUrl}
              setShowInfoLostDialog={setShowInfoLostDialog}
              setIsTestPreviewOpen={setIsTestPreviewOpen}
              isTestPreviewOpen={isTestPreviewOpen}
            />
            <div
              className={classNames(
                "bg-default flex-1",
                isDesktop && "grid gap-8",
                isDesktop && isTestPreviewOpen && "grid-cols-[1fr,400px]",
                isDesktop && !isTestPreviewOpen && "grid-cols-1",
                !isDesktop && "flex flex-col"
              )}>
              {isDesktop ? (
                <motion.div
                  layout
                  className="mx-auto w-full max-w-4xl px-2 lg:px-4 xl:px-0"
                  transition={{ duration: 0.3, ease: "easeInOut" }}>
                  <Page hookForm={hookForm} form={form} appUrl={appUrl} />
                </motion.div>
              ) : (
                <div className="mx-auto w-full max-w-4xl px-2">
                  <Page hookForm={hookForm} form={form} appUrl={appUrl} />
                </div>
              )}
              <AnimatePresence>
                {isTestPreviewOpen && isDesktop ? (
                  <motion.div
                    initial={{ opacity: 0, x: 20 }}
                    animate={{ opacity: 1, x: 0 }}
                    exit={{ opacity: 0, x: 20 }}
                    transition={{ duration: 0.3, ease: "easeInOut" }}>
                    <TestFormRenderer
                      isMobile={!isDesktop}
                      testForm={uptoDateForm}
                      isTestPreviewOpen={isTestPreviewOpen}
                      setIsTestPreviewOpen={setIsTestPreviewOpen}
                    />
                  </motion.div>
                ) : isTestPreviewOpen ? (
                  <div>
                    <TestFormRenderer
                      isMobile={!isDesktop}
                      testForm={uptoDateForm}
                      isTestPreviewOpen={isTestPreviewOpen}
                      setIsTestPreviewOpen={setIsTestPreviewOpen}
                    />
                  </div>
                ) : null}
              </AnimatePresence>
            </div>
          </div>
        </FormActionsProvider>
        {showInfoLostDialog && (
          <InfoLostWarningDialog
            handleSubmit={() => {
              mutation.mutate({
                ...hookForm.getValues(),
              });
            }}
            goToRoute={`${appUrl}/route-builder/${form?.id}`}
            isOpenInfoLostDialog={showInfoLostDialog}
            setIsOpenInfoLostDialog={setShowInfoLostDialog}
          />
        )}
      </Form>
    </>
  );
}

export default function SingleFormWrapper({ form: _form, ...props }: SingleFormComponentProps) {
  const { data: form, isPending } = trpc.viewer.appRoutingForms.formQuery.useQuery(
    { id: _form.id },
    {
      initialData: _form,
      trpc: {},
    }
  );
  const { t } = useLocale();

  if (isPending) {
    // It shouldn't be possible because we are passing the data from SSR to it as initialData. So, no need for skeleton here
    return null;
  }

  if (!form) {
    throw new Error(t("something_went_wrong"));
  }
  return (
    <LicenseRequired>
      <SingleForm form={form} {...props} />
    </LicenseRequired>
  );
}
