import React, { useCallback, useEffect, useState } from "react";
import { Link, useHistory } from "react-router-dom";
import {
  InlineMessage,
  Input,
  PillInput,
  TalentButton,
  TalentButtonWithIcon,
  Typography,
} from "@komi-app/components";

import { useTypedSelector } from "redux/rootReducer";
import {
  campaignService,
  emailDomainService,
  getSalesServiceV1,
  services,
} from "services";

import CampaignTemplatePreview from "../CampaignTemplatePreview";
import ScheduleCampaignModal from "../Modals/ScheduleCampaignModal";
import { CampaignTemplateBuilderEditModal } from "../../Templates";
import { ErrorMessage } from "components/ProfileSettings/AudiencesAndCampaigns/EmailDomainSetup/EmailDomainSetupTile/ErrorMessage";
import { useEmailDomainQuery } from "components/ProfileSettings/AudiencesAndCampaigns/EmailDomainSetup/queries";
import When from "@komi-app/when";

import {
  FieldValues,
  Path,
  useController,
  UseControllerProps,
  useForm,
} from "react-hook-form";

import { Segment } from "@komi-app/fans-sdk";
import { CampaignFormDto } from "./types";
import classNames from "classnames";
import "./CampaignForm.scss";
import notification from "utils/notification";
import { format } from "date-fns";
import { CampaignFormCombobox } from "./CampaignFormCombobox";
import { selectTalentProfile } from "redux/User/selector";
import { FLAGS, useFeatureIsOn } from "@komi-app/flags-sdk";
import TemplateWithCursorGalleryModal from "../Modals/TemplateGalleryModal";
import { useSessionFlag } from "hooks/useSessionFlag";
import { useModalState } from "hooks/useModalState";
import { SmartScheduleEducationModal } from "components/SmartScheduleEducationModal";
import { campaignCreateScheduleUrl } from "constants/dca";
import { useQuery } from "@tanstack/react-query";
import { GetQuotaResponse, QUOTAS } from "@komi-app/sales-sdk";
import {
  ButtonType,
  Message,
  MessageSemantic,
  Semantic,
} from "@komi-app/creator-ui";

export interface CampaignFormProps {
  initialValues: CampaignFormDto;
  isLoading: boolean;
  onSubmit: (values: CampaignFormDto) => Promise<void>;
  handleUnsavedChanges: (value: boolean) => void;
  campaignId?: string;
}

const CampaignForm = ({
  initialValues,
  isLoading,
  onSubmit,
  campaignId,
  handleUnsavedChanges,
}: CampaignFormProps) => {
  const isSmartScheduleCampaignEnabled = useFeatureIsOn(
    FLAGS.SMART_SCHEDULE_TALENT
  );
  const {
    isOpen: isEducationModalOpen,
    open: openEducationModal,
    close: closeEducationModal,
  } = useModalState();
  const [hasSeenEducationModal, markEducationModalAsSeen] = useSessionFlag(
    "shown_smart_schedule_education_modal",
    //NOTE: Update the default to false after fixing state issue
    true
  );
  const router = useHistory();
  const navigateToSmartSchedulePage = () => {
    const formValues = getValues();
    handleUnsavedChanges(false);
    // Forward the campaign form details to /email-campaign-schedule page

    router.push(campaignCreateScheduleUrl, { formValues });
  };

  const customDomainQuery = useEmailDomainQuery();

  const [segments, setSegments] = useState<Segment[]>([]);
  const [testEmails, setTestEmails] = useState<string[]>([]);
  const [modalOpen, setModalOpen] = useState(false);
  const [isTemplateEdited, setIsTemplateEdited] = useState(false);

  const talentProfile = useTypedSelector(selectTalentProfile);
  const [nameAlreadyExists, setNameAlreadyExists] = useState(false);
  useEffect(() => {
    const getSegments = async () => {
      const fetchedSegments = await services.segment().getSegments();
      setSegments(fetchedSegments);
    };

    getSegments();
  }, []);

  const { setValue, control, watch, trigger, getValues } =
    useForm<CampaignFormDto>({
      defaultValues: initialValues,
      mode: "all",
    });

  const templateId = watch("templateId");
  const subject = watch("subject");
  const name = watch("name");
  const schedule = watch("schedule");
  const selectedSegments = watch("segmentIds");

  const sendTestEmail = async () => {
    if (!templateId) return;
    await emailDomainService.sendTestEmail({
      templateId,
      emails: testEmails,
      subject: `Test email: ${subject}`,
    });
  };

  const confirmNameExistsAndSave = async () => {
    if (!name) {
      trigger("name");
      return;
    }
    const nameAlreadyExists = await campaignService.checkNameExists(
      talentProfile?.id as string,
      campaignId || "",
      name
    );
    if (nameAlreadyExists) setNameAlreadyExists(true);
    else {
      onSubmit(getValues());
      handleUnsavedChanges(false);
    }
  };

  const validateName = async (): Promise<boolean> => {
    if (!name) {
      trigger("name");
      return false;
    }
    const nameAlreadyExists = await campaignService.checkNameExists(
      talentProfile?.id as string,
      campaignId || "",
      name
    );
    if (nameAlreadyExists) {
      setNameAlreadyExists(true);
      return false;
    } else {
      return true;
    }
  };

  const validateForm = async (): Promise<boolean> => {
    const isNameValid = await validateName();
    const isSubjectValid = await trigger("subject");
    const isTemplateValid = await trigger("templateId");
    const isSegmentValid = await trigger("segmentIds");

    return isNameValid && isSubjectValid && isTemplateValid && isSegmentValid;
  };

  const isQuotaLimitingEnabled = useFeatureIsOn(
    FLAGS.CRM_1381_FRONTEND_QUOTA_SCHEDULE_BLOCK
  );

  const { data: segmentsFanCount, isLoading: isSegmentsFanCountLoading } =
    useQuery({
      queryFn: () => services.segment().countMultiSegments(selectedSegments),
      queryKey: ["get-segments-fan-count", selectedSegments],
      enabled: isQuotaLimitingEnabled && selectedSegments.length > 0,
    });

  const quotaReducer = (data: GetQuotaResponse) => {
    const currentUsage = data?.usage?.quotaUsed || 0;
    const quotaLimit = data?.quota.quotaValue;
    const hasReachedQuota = currentUsage >= quotaLimit;

    return {
      hasReachedQuota,
      actionExceedsQuota:
        data.quota.quotaKey === QUOTAS.EMAIL_SENDS
          ? currentUsage + (segmentsFanCount || 0) > quotaLimit
          : hasReachedQuota,
      limit: quotaLimit,
    };
  };

  // TODO: Replace funcs with salesServiceV1.hasAvailableQuota once we have multi-facing auth implemented
  const { data: contactsQuota, isLoading: isContactsQuotaLoading } = useQuery({
    queryFn: () => getSalesServiceV1().getQuota(QUOTAS.CONTACTS),
    queryKey: ["get-quota", QUOTAS.CONTACTS],
    enabled: isQuotaLimitingEnabled,
    select: quotaReducer,
  });
  const { data: emailsQuota, isLoading: isEmailsQuotaLoading } = useQuery({
    queryFn: () => getSalesServiceV1().getQuota(QUOTAS.EMAIL_SENDS),
    queryKey: ["get-quota", QUOTAS.EMAIL_SENDS],
    enabled: isQuotaLimitingEnabled,
    select: quotaReducer,
  });

  const limitMessage = React.useMemo(() => {
    const limits = [];
    if (contactsQuota?.hasReachedQuota) {
      limits.push(`${contactsQuota?.limit} contact limit`);
    }
    if (emailsQuota?.hasReachedQuota || emailsQuota?.actionExceedsQuota) {
      limits.push(`${emailsQuota?.limit} email send limit`);
    }
    if (limits.length === 0) return "limit";

    return limits.join(" and ");
  }, [contactsQuota, emailsQuota]);

  const hasQuotaLimitation = React.useMemo(() => {
    return (
      (isQuotaLimitingEnabled && contactsQuota?.hasReachedQuota) ||
      emailsQuota?.hasReachedQuota ||
      emailsQuota?.actionExceedsQuota
    );
  }, [
    isQuotaLimitingEnabled,
    contactsQuota?.hasReachedQuota,
    emailsQuota?.hasReachedQuota,
    emailsQuota?.actionExceedsQuota,
  ]);

  /**
   * Checks if the schedule button should be disabled
   * Check if the form is Loading
   * Also checks if the email domain is verified
   */
  const isSubmitEnabled = React.useMemo(() => {
    // keeping this separate for legibility
    if (isQuotaLimitingEnabled) {
      return (
        !isContactsQuotaLoading &&
        !hasQuotaLimitation &&
        !isEmailsQuotaLoading &&
        (!isSegmentsFanCountLoading || selectedSegments.length === 0) &&
        !isLoading &&
        customDomainQuery?.data?.status === "verified"
      );
    }
    return !isLoading && customDomainQuery?.data?.status === "verified";
  }, [
    isLoading,
    customDomainQuery?.data?.status,
    isQuotaLimitingEnabled,
    isContactsQuotaLoading,
    isEmailsQuotaLoading,
    hasQuotaLimitation,
    isSegmentsFanCountLoading,
    selectedSegments,
  ]);

  const onScheduleClick = async () => {
    if (isSmartScheduleCampaignEnabled) {
      const isFormValid = await validateForm();
      if (!isFormValid) {
        return;
      }

      if (hasSeenEducationModal) {
        //TODO : perform the campaign name exists check before redirecting to next page
        navigateToSmartSchedulePage();
      } else {
        openEducationModal();
      }
    } else {
      const isFormValid = await validateForm();
      if (isFormValid) setModalOpen(true);
    }
  };

  /**
   * Check if the user has configured domains.
   * If yes, check if the domain is verified.
   * If not, show the error message.
   */
  const getDomainConfigurationMessage = () => {
    if (customDomainQuery.data) {
      switch (customDomainQuery.data.status) {
        case "issue":
          return <ErrorMessage state={customDomainQuery.data} />;
        case "empty":
        case "pending":
          return (
            <InlineMessage
              type="error"
              message={
                <>
                  Connect your email domain in order to be able to send
                  campaigns to your audience.
                  <Link to="/admin/settings/audiences-and-campaigns">
                    &nbsp;Go to Settings
                  </Link>
                </>
              }
            />
          );
        default:
          return null;
      }
    } else {
      return null;
    }
  };

  const handleFormCheck = useCallback(() => {
    const currentFormValues: CampaignFormDto = getValues();
    const hasUnsavedChanges: boolean =
      currentFormValues.name !== initialValues.name ||
      currentFormValues.subject !== initialValues.subject ||
      isTemplateEdited ||
      currentFormValues.segmentIds.length > 0;
    handleUnsavedChanges(hasUnsavedChanges);
  }, [templateId, initialValues, isTemplateEdited]);

  useEffect(() => {
    if (isTemplateEdited) {
      handleFormCheck();
    }
  }, [isTemplateEdited]);

  return (
    <>
      {getDomainConfigurationMessage()}

      <form onChange={handleFormCheck}>
        <div className="details">
          <Typography.Heading level={4}>Details</Typography.Heading>
          <div className="details-content">
            <ControlledInput
              controllerProps={{
                control: control,
                name: "name",
                rules: { required: "Enter campaign name" },
              }}
              inputProps={{
                "data-testid": "campaign-title-input",
                label: "Campaign name",
                placeholder: "Enter campaign name",
              }}
              fieldError={
                nameAlreadyExists
                  ? "A campaign with the same name already exists"
                  : ""
              }
              onValueUpdate={() => {
                setNameAlreadyExists(false);
              }}
            />

            <ControlledInput
              controllerProps={{
                control: control,
                name: "subject",
                rules: { required: "Enter subject" },
              }}
              inputProps={{
                "data-testid": "campaign-subject-input",
                label: "Subject",
                placeholder: "Enter subject",
              }}
            />
            <CampaignFormCombobox
              controllerProps={{
                control: control,
                name: "segmentIds",
                rules: {
                  required: "Select segment",
                },
              }}
              label="Send to"
              onChangeHandler={handleFormCheck}
              options={segments.map((segment) => ({
                label: segment.name,
                value: segment.segmentId,
              }))}
            />
          </div>
        </div>

        <div className="email-content">
          <Typography.Heading className="email-content-heading" level={4}>
            Email Content
          </Typography.Heading>
          <ControlledCampaignTemplatePreview
            controllerProps={{
              control,
              name: "templateId",
              rules: { required: "Select template" },
            }}
            onOpen={() => {
              setIsTemplateEdited(true);
            }}
          />

          {templateId && (
            <div className="action-buttons">
              <CampaignTemplateBuilderEditModal
                templateId={templateId}
                onOpen={() => {
                  setIsTemplateEdited(true);
                }}
              />
              <TemplateWithCursorGalleryModal
                disabled={false}
                onTemplateSelected={(template) => {
                  setValue("templateId", template.templateId);
                }}
                onCancel={() => {}}
                buttonType="text"
                onOpen={() => {
                  setIsTemplateEdited(true);
                }}
              />
            </div>
          )}
        </div>

        {templateId && (
          <div className="send-test-email">
            <Typography.Heading level={4}>Send Test Email</Typography.Heading>
            <Typography.Body color="gray-700">
              Sending a test email to your recipients to see a preview of what
              your email will look like
            </Typography.Body>
            <PillInput
              placeholder="Email address"
              validationErrorMessage="Enter a valid email"
              validationRegexType="email"
              emptyErrorMessage="Enter an email"
              title="Recipients"
              onValuesChanged={setTestEmails}
            />
            <div className="action-buttons">
              <TalentButtonWithIcon
                type="text"
                label="Send"
                prefixIcon="send"
                onClick={sendTestEmail}
                disabled={
                  !templateId ||
                  !subject ||
                  !name ||
                  !testEmails.length ||
                  (customDomainQuery.data &&
                    customDomainQuery.data.status !== "verified")
                }
              />
            </div>
          </div>
        )}

        <div className="footer">
          <When
            value={hasQuotaLimitation}
            then={
              <Message
                semantic={MessageSemantic.ERROR}
                text={`You've reached your ${limitMessage}. Upgrade your plan to schedule campaigns.`}
                buttonProps={{
                  label: "Contact Us",
                  href: "mailto:support@komi.io",
                  type: ButtonType.LINK,
                  semantic: Semantic.SECONDARY,
                  size: "xsmall",
                }}
              />
            }
          />
          <div className="footer-buttons">
            <TalentButton
              variant="secondary"
              type="button"
              disabled={isLoading}
              onClick={confirmNameExistsAndSave}
            >
              SAVE & EXIT
            </TalentButton>
            <TalentButton
              type="button"
              disabled={!isSubmitEnabled}
              onClick={onScheduleClick}
            >
              SCHEDULE
            </TalentButton>
            <ScheduleCampaignModal
              onScheduleSubmitted={async (schedule) => {
                setValue("schedule", schedule);
                const newValues = getValues();
                await onSubmit(newValues);
                if (newValues.schedule.startTimestamp) {
                  notification.success({
                    message: `${newValues.name} scheduled for ${format(
                      newValues.schedule.startTimestamp,
                      "d LLLL, yyyy 'at' p"
                    )}`,
                  });
                }
              }}
              existingSchedule={schedule}
              modalOpen={modalOpen}
              setModalOpen={setModalOpen}
            />
            <SmartScheduleEducationModal
              visible={isEducationModalOpen}
              onSchedule={() => {
                markEducationModalAsSeen(true);
                navigateToSmartSchedulePage();
              }}
              onDismiss={() => {
                closeEducationModal();
              }}
            />
          </div>
        </div>
      </form>
    </>
  );
};

export default CampaignForm;

type DataAttributes = Partial<Record<`data-testid`, string>>;
function ControlledInput<
  TFieldValues extends FieldValues = FieldValues,
  TName extends Path<TFieldValues> = Path<TFieldValues>,
>({
  controllerProps,
  inputProps,
  fieldError,
  onValueUpdate,
}: {
  controllerProps: UseControllerProps<TFieldValues, TName>;
  inputProps: React.ComponentProps<typeof Input> & DataAttributes;
  fieldError?: string;
  onValueUpdate?: (data: string) => void;
}) {
  const {
    field: { onChange, ...field },
    fieldState: { error },
  } = useController(controllerProps);
  return (
    <Input
      error={error?.message || fieldError}
      onValueChange={(data: string) => {
        onChange(data);
        onValueUpdate && onValueUpdate(data);
      }}
      {...inputProps}
      {...field}
    />
  );
}
function ControlledCampaignTemplatePreview<
  TFieldValues extends FieldValues = FieldValues,
  TName extends Path<TFieldValues> = Path<TFieldValues>,
>({
  controllerProps,
  onOpen,
}: {
  controllerProps: UseControllerProps<TFieldValues, TName>;
  onOpen: () => void;
}) {
  const {
    field,
    fieldState: { error },
  } = useController(controllerProps);

  return (
    <div>
      <div
        className={classNames("email-preview", {
          "email-preview--error": error,
        })}
      >
        <CampaignTemplatePreview
          templateId={field.value}
          onCancel={() => field.onBlur()}
          onTemplateSelected={(templateId: string) => {
            field.onBlur();
            field.onChange(templateId);
          }}
          onOpen={onOpen}
        />
      </div>
      {error && (
        <Typography.Body level={2} color="feedback-error">
          {error.message}
        </Typography.Body>
      )}
    </div>
  );
}
