import { useState, useMemo, useCallback } from "react";
import { Prompt } from "react-router-dom";
import { Location } from "history";
import { useQuery } from "@tanstack/react-query";

import config from "config";

import { FLAGS, useFeatureIsOn } from "@komi-app/flags-sdk";
import {
  CampaignStatus,
  CampaignType,
  ScheduleType,
  UNSUBSCRIBE_MESSAGE,
  SMS_URL_REGEX
} from "@komi-app/messaging-sdk";
import {
  SMSCampaignPage,
  FormErrors,
  FormValues,
  useModal,
  Semantic,
  useToast,
} from "@komi-app/creator-ui";

import { getCampaignSDK, services } from "services";
import { SMSCampaign } from "types/campaigns";

import {
  getDefaultMessage,
  validateContentStep,
  validateDetailsStep,
  validateScheduleStep,
} from "./campaignUtils";
import { generateTimeSlots } from "utils/sms";
import { v4 as uuidV4 } from "uuid";
import _ from "lodash";

import { useTypedSelector } from "redux/rootReducer";
import { selectTalentProfile } from "redux/User/selector";

export interface CampaignFormProps {
  existingCampaignData?: SMSCampaign;
  assignedNumber: string;
}

const SMSCampaignForm: React.FC<CampaignFormProps> = ({
  existingCampaignData,
  assignedNumber,
}) => {
  const DEFAULT_FORM_VALUES: SMSCampaign = {
    id: uuidV4(),
    status: CampaignStatus.DRAFT,
    scheduleType: ScheduleType.UTC,
    campaignName: "",
    selectedSegments: [],
    messageBody: "",
    scheduleTimeStamp: undefined,
  };
  const [isSaving, setIsSaving] = useState(false);
  const [activeStepIndex, setActiveStepIndex] = useState<number>(0);
  const [navigatingLocation, setNavigatingLocation] = useState<string>("");

  const { Modal, openModal, closeModal } = useModal();
  const { createToast } = useToast();
  const listingURL = "/admin/audience-campaigns/campaigns";
  const phoneNumberConfigURL = "/admin/settings/audiences-and-campaigns";
  const talent = useTypedSelector(selectTalentProfile);
  const debounceUpdateTimer = 500;
  const talentName = useMemo(() => {
    const firstName = talent?.firstName || "";
    const lastName = talent?.lastName ? ` ${talent.lastName}` : "";
    return `${firstName}${lastName}`;
  }, []);

  const isExtendedTimeSelectionOn = useFeatureIsOn(
    FLAGS.FEAT_CRM_1209_ALLOW_EXTENDED_SMS_TIME_SELECTION
  );

  const timeSelectOptions = useMemo(() => {
    const timeSlots = isExtendedTimeSelectionOn
      ? {}
      : {
        lowerHourBound: 14,
        lowerMinuteBound: 0,
        upperHourBound: 18,
        upperMinuteBound: 30,
      };

    return generateTimeSlots(timeSlots);
  }, [isExtendedTimeSelectionOn]);

  /**
   * Merge default form values with existing campaign values,
   * existingCampaignData overwrites only when it is passed.
   */
  const [formValue, setFormValue] = useState<SMSCampaign>({
    ...DEFAULT_FORM_VALUES,
    ...existingCampaignData,
    messageBody:
      existingCampaignData?.messageBody || getDefaultMessage(talentName),
  });
  const [formErrors, setFormErrors] = useState<FormErrors>({
    campaignName: "",
    selectedSegments: undefined,
    messageBody: "",
    scheduleTimeStamp: undefined,
  });

  /**
   * Handler for when user want to navigate out
   * of the page w/o saving
   * @param nextLocation String
   * @returns
   */
  const handleNavigateWhenSaving = (nextLocation: Location<unknown>) => {
    if (isSaving) {
      setNavigatingLocation(nextLocation.pathname);
      openModal();
    }
    // Allow navigation if there are no unsaved changes
    return !isSaving;
  };

  /**
   * Get the list of errors for the current
   * step.
   * @param formValue FormValues
   * @returns
   */
  const getCurrentStepErrors = async (
    formValue: SMSCampaign
  ): Promise<{ hasFormErrors: boolean; stepErrors: Partial<FormErrors> }> => {
    let stepErrors = {};

    switch (activeStepIndex) {
      case 0:
        stepErrors = await validateDetailsStep(formValue, formValue.id);
        break;
      case 1:
        stepErrors = validateContentStep(formValue);
        break;
      case 2:
        stepErrors = validateScheduleStep(formValue);
        break;
      default:
        break;
    }
    const hasFormErrors = Object.keys(stepErrors).length > 0;
    return { hasFormErrors, stepErrors };
  };

  const onNextClick = async () => {
    const { hasFormErrors, stepErrors } = await getCurrentStepErrors(formValue);
    if (!hasFormErrors) {
      setActiveStepIndex(activeStepIndex + 1);
    } else {
      setFormErrors({ ...formErrors, ...stepErrors });
    }
  };

  const onPreviousClick = async () => {
    const { hasFormErrors, stepErrors } = await getCurrentStepErrors(formValue);
    if (!hasFormErrors) {
      setActiveStepIndex(activeStepIndex - 1);
    } else {
      setFormErrors({ ...formErrors, ...stepErrors });
    }
  };

  const onScheduleClick = async () => {
    try {
      //Do not schedule if there are any errors in the form
      const { hasFormErrors, stepErrors } =
        await getCurrentStepErrors(formValue);
      setFormErrors(stepErrors);
      if (hasFormErrors) return;

      await triggerAutosave({
        ...formValue,
        status: CampaignStatus.SCHEDULED,
      });

      window.location.href =
        listingURL + `?campaignName=${formValue.campaignName}`;
    } catch (error: any) {
      createToast({
        text: "We couldn’t schedule your campaign. Retry later.",
        semantic: "error",
        durationMs: 5000,
      });
    }
  };

  const onFieldUpdate = (update: Partial<FormValues>) => {
    const updatedForm = { ...formValue, ...update };
    resetFieldErrors(update);
    setFormValue(updatedForm);
    debouncedSave(updatedForm);
  };

  const triggerAutosave = async (data: Partial<SMSCampaign>) => {
    //Get the errors related to the current step.
    const { hasFormErrors } = await getCurrentStepErrors({
      ...formValue,
      ...data,
    });

    /**
     * If there were any errors, we do not trigger saving.
     * The error highlight is only done on Next button click.
     */
    if (hasFormErrors) return;
    try {
      setIsSaving(true);
      /**
       * We call the SDK method with the
       * form values.
       */
      await getCampaignSDK().updateCampaign({
        type: CampaignType.SMS,
        id: data.id as string,
        body: data.messageBody,
        name: data.campaignName,
        scheduledTimestamp: data.scheduleTimeStamp,
        scheduleType: data.scheduleType,
        segmentIds: data.selectedSegments,
        status: data.status,
      });
      setIsSaving(false);
    } catch (error: any) {
      //Show error toast if we were unable to save
      createToast({
        text: "We couldn’t save your changes. Retry later.",
        semantic: "error",
        durationMs: 5000,
      });
    }
  };

  //Reset the errors of a set of field. Used when we update
  // a field.
  const resetFieldErrors = (update: Partial<FormValues>) => {
    setFormErrors((prev) => {
      const updatedErrors = { ...prev };
      Object.keys(update).forEach((key) => {
        delete updatedErrors[key as keyof FormErrors];
      });
      return updatedErrors;
    });
  };

  const debouncedSave = useCallback(
    _.debounce(triggerAutosave, debounceUpdateTimer),
    [activeStepIndex]
  );

  /**
   * Fetch segments for the current
   * talent.
   */
  const { data: segmentsByTalent } = useQuery({
    queryKey: ["segmentServiceLegacy-getSegments"],
    queryFn: async () => {
      const segments = await services.segment().getSegments();
      return segments.map(({ segmentId, name }) => ({ id: segmentId, name }));
    },
    refetchOnWindowFocus: false,
  });
  return (
    <>
      <div className="create-sms-campaign">
        <Prompt when={isSaving} message={handleNavigateWhenSaving} />
        <SMSCampaignPage
          smsUrlRegex={SMS_URL_REGEX}
          talentPhoneNumber={assignedNumber}
          isPhoneRegistered={assignedNumber.length > 0}
          isAutoSaving={isSaving}
          segmentsList={segmentsByTalent || []}
          talentName={talentName}
          formValues={formValue}
          formErrors={formErrors}
          activeStepIndex={activeStepIndex}
          registerPhoneNumberURL={phoneNumberConfigURL}
          previousPageUrl={listingURL}
          unsubscribeMessage={UNSUBSCRIBE_MESSAGE}
          shortenedLinkFormat={
            config.service.env === "staging"
              ? "s.km.link/unique"
              : "km.link/unique"
          }
          handleScheduleClick={onScheduleClick}
          onFormUpdate={onFieldUpdate}
          onNextClick={onNextClick}
          onPreviousClick={onPreviousClick}
          timeSelectOptions={timeSelectOptions}
        />
      </div>
      <Modal
        heading="Unsaved changes"
        body="Some of the changes you made haven’t been saved. Are you sure you want to leave?"
        buttons={[
          {
            label: "Cancel",
            semantic: Semantic.SECONDARY,
            onClick: closeModal,
          },
          {
            label: "Ok",
            semantic: Semantic.PRIMARY,
            onClick: () => {
              window.location.href = navigatingLocation;
            },
          },
        ]}
      />
    </>
  );
};

export default SMSCampaignForm;
