import React, { useCallback, useEffect, useMemo, useState } from 'react';
import { useSelector } from 'react-redux';

import {
  useCampaignStatuses,
  useCurrencies,
  useGenders,
  useGoals,
  usePerformanceObjectives,
  useTaxonomy,
  useTerritories,
} from 'common/hooks';
import {
  AGE_STEPS,
  CAMPAIGN_CREATIVE_LINKS_LIMIT,
  CAMPAIGN_LINKFIRE_LINKS_LIMIT,
  MAX_AGE,
  MIN_AGE,
  SimpleOption,
  SimpleSectionOption,
  Widget,
} from 'common/constants';
import { bem } from 'utils/bem';
import { currency, maxLength, required, requiredArray, requiredCurrencyFormik } from 'utils/validation';
import { Field } from 'common/components/form/formik-field';
import { SPaper } from 'common/s-components/s-paper';
import { CampaignSources, Id, Optional } from 'backend-api/models';
import { SBody } from './s-body';
import {
  getCampaignAvailableEndDates,
  getCampaignAvailableStartDates,
  getCampaignEditableFields,
  getCampaignsTypesOptions,
  prepareCampaignBudgetValue,
} from '../../transducers';
import {
  activeCampaignSelector,
  assignedProjectSelector,
  isLoadingSelector,
  phasesSelector,
  processingSelector,
} from 'campaign/selectors';
import Title from '../campaign-details/title';
import {
  generateCampaignName,
  getCampaignStatusColor,
  getDefaultExpandedPlatforms,
  getPlacementsOptions,
  getPlatformsOptions,
  getSelectedPlacementsForSelectedPlatforms,
  getSelectOptions,
  isCampaignExternal,
} from 'common/transducers';
import { CampaignInput } from './types';
import { FormFooter } from 'common/components/form-footer';
import { goBack, navigateTo } from 'utils/navigation';
import { RemoveButton } from 'campaign/components/campaign-details/remove-button';
import { Cell } from 'common/components/form/cell';
import { PurchaseOrderLink } from 'common/components/form/links/components';
import { CAMPAIGN_TYPES_WITH_SPEND_FIELD, GRATIS_OPTIONS, NOT_AVAILABLE_BUDGET_TOOLTIP } from 'campaign/constants';
import { FixedSizeList, ListChildComponentProps } from 'react-window';
import { MenuProps } from 'react-select';
import { MAX_MENU_HEIGHT } from 'common/components/form/select';
import { PROVIDER_ITEM_HEIGHT } from './constants';
import { isSubstring } from 'utils/string';
import { useFormikContext } from 'formik';
import { GratisType } from 'campaign/types';
import { PlatformDetails } from 'campaign/components/platform-details';
import { paths } from 'app/routing/paths';
import { FieldComposition } from 'common/components/form/limited-length-text-field';
import { useCampaignProviders } from 'common/hooks/use-campaign-providers';
import { UUID } from 'io-ts-types/lib/UUID';

interface Props {
  campaignUuid: UUID;
  projectId: Id;
  currencyCode?: string;
}

const classes = bem('campaign-edit-form');

export const Body = React.memo(({ projectId, campaignUuid, currencyCode }: Props) => {
  const { values: formValues, submitForm, isValid, setFieldValue, setFieldTouched, dirty } = useFormikContext<
    CampaignInput
  >();

  const [providersSearch, setProvidersSearch] = useState('');
  const [destinationsWarning, setDestinationsWarning] = useState<Optional<string>>(undefined);

  const values = useMemo(() => formValues || {}, [formValues]);

  const assignedProject = useSelector(assignedProjectSelector);
  const isBeingProcessed = useSelector(processingSelector);
  const isLoading = useSelector(isLoadingSelector);
  const phases = useSelector(phasesSelector);

  const taxonomy = useTaxonomy();
  const goals = useGoals();
  const genders = useGenders();
  const campaignProviders = useCampaignProviders(campaignUuid);
  const territories = useTerritories();
  const campaignStatuses = useCampaignStatuses();
  useCurrencies();

  const platformsOptions = useMemo(
    () => (taxonomy?.platforms ? getPlatformsOptions(taxonomy.platforms, values.platforms || []) : []),
    [values.platforms, taxonomy?.platforms]
  );

  const defaultExpandedPlatformsIds = useMemo(() => getDefaultExpandedPlatforms(platformsOptions), [platformsOptions]);

  const campaign = useSelector(activeCampaignSelector);
  const campaignPhase = useMemo(() => phases.find(phase => phase.id === campaign?.phases?.[0]), [campaign, phases]);

  const campaignTypesOptions = getCampaignsTypesOptions(taxonomy?.categories);

  const metrics = usePerformanceObjectives();
  const kpiOptions = useMemo(() => getSelectOptions(metrics), [metrics]);

  const objectivesByGoals: Optional<SimpleSectionOption[]> = goals?.map(item => {
    return { label: item.name, options: item.objectives };
  });

  const [campaignNameDirty, setCampaignNameDirty] = useState(false);
  const [campaignNameManuallyChange, setCampaignNameManuallyChange] = useState(false);
  const [autogeneratedName, setAutogeneratedName] = useState('');

  const editableFields = getCampaignEditableFields(campaign);

  useEffect(() => {
    const typeIdValue = values.type || -1;
    const platformIdsValue = values.platforms || [];

    const generatedName = generateCampaignName(typeIdValue, platformIdsValue, taxonomy);

    setAutogeneratedName(generatedName || '');
    if (campaignNameDirty || generatedName === values.name || !editableFields.name) return;
    if (campaign?.name === generatedName) {
      setCampaignNameDirty(false);
    } else if (campaign && !dirty) {
      setCampaignNameDirty(true);
      return;
    }

    setCampaignNameManuallyChange(false);
    setFieldValue('name', generatedName);
  }, [
    values,
    taxonomy,
    campaign?.name,
    campaignUuid,
    campaignNameDirty,
    campaign,
    dirty,
    editableFields.name,
    setFieldValue,
  ]);

  const availableStartDates = useMemo(
    () => getCampaignAvailableStartDates(values.endDate, assignedProject, campaignPhase),
    [values.endDate, assignedProject, campaignPhase]
  );
  const availableEndDates = useMemo(
    () => getCampaignAvailableEndDates(values.startDate, assignedProject, campaignPhase),
    [values.startDate, assignedProject, campaignPhase]
  );

  const handleDestinationsLinkError = useCallback(
    (errorString: string) => {
      setFieldTouched('destinations', true);
      setDestinationsWarning(errorString);
    },
    [setFieldTouched]
  );

  const handleDestinationsLinkResetError = useCallback(() => {
    setDestinationsWarning(undefined);
  }, []);

  const isExternalCampaign = isCampaignExternal(campaign);

  const campaignNamePlaceholderPersistent =
    (!values.platforms || values.platforms.length == 0) &&
    !campaignNameManuallyChange &&
    !campaignNameDirty &&
    editableFields.name;

  const campaignNamePlaceholder = formValues?.name
    ? `${formValues?.name} | Select Platform`
    : 'Select Campaign Type | Select Platform';

  const onCampaignNameInputChange = useCallback(
    event => {
      if (!campaignNameManuallyChange) {
        setCampaignNameManuallyChange(true);
      }
      if (!event.target.value) {
        setCampaignNameManuallyChange(false);
      }
    },
    [campaignNameManuallyChange]
  );

  const normalizeCampaignName = useCallback((value: string, prevValue: string): string => {
    if (value && value !== prevValue) {
      setCampaignNameDirty(true);
    } else if (!value) {
      setCampaignNameDirty(false);
    }
    return value;
  }, []);

  const onResetNameClick = useCallback(() => {
    setCampaignNameDirty(false);
    setCampaignNameManuallyChange(false);
  }, []);

  const onCancelEditing = useCallback(() => {
    goBack(() => {
      navigateTo(campaignUuid ? paths.campaign(projectId, campaignUuid) : paths.projectDetails(projectId));
    });
  }, [campaignUuid, projectId]);

  const shouldHideCampaignBudget = campaign?.source === CampaignSources.FACEBOOK;

  const renderProviderOptions = useCallback((children: React.ReactNode) => {
    return (props: ListChildComponentProps): React.ReactElement => {
      const { index, style } = props;
      return <div style={style}>{children?.[index]}</div>;
    };
  }, []);

  const renderProviderMenuList = useCallback(
    ({ options, children }: MenuProps<SimpleOption>) => (
      <FixedSizeList width="100%" height={MAX_MENU_HEIGHT} itemCount={options.length} itemSize={PROVIDER_ITEM_HEIGHT}>
        {renderProviderOptions(children)}
      </FixedSizeList>
    ),
    [renderProviderOptions]
  );

  const providerFilterComponents = useMemo(
    () => ({
      MenuList: renderProviderMenuList,
    }),
    [renderProviderMenuList]
  );

  const filteredProviders = useMemo(() => campaignProviders?.filter(({ name }) => isSubstring(name, providersSearch)), [
    providersSearch,
    campaignProviders,
  ]);

  const formatPlannedBudgetValue = useCallback(
    value => {
      if (!campaign || !shouldHideCampaignBudget) {
        return value;
      }
      return prepareCampaignBudgetValue(campaign.source, campaign.plannedBudget);
    },
    [campaign, shouldHideCampaignBudget]
  );

  const normalizePlannedBudgetValue = useCallback(
    value => {
      if (shouldHideCampaignBudget) {
        return campaign?.plannedBudget;
      }
      return value;
    },
    [campaign?.plannedBudget, shouldHideCampaignBudget]
  );

  const placementOptions = useMemo(
    () => getPlacementsOptions(taxonomy?.placements, values.platforms, taxonomy?.platforms),
    [taxonomy?.placements, values.platforms, taxonomy?.platforms]
  );

  useEffect(() => {
    const selectedPlacements = getSelectedPlacementsForSelectedPlatforms(
      taxonomy?.platforms,
      taxonomy?.placements,
      values.platforms,
      values.placements
    );

    setFieldValue('placements', selectedPlacements);
    // eslint-disable-next-line react-hooks/exhaustive-deps
  }, [taxonomy?.placements, taxonomy?.platforms, setFieldValue, values.platforms]);

  const badgeSelectorColor = useMemo(() => getCampaignStatusColor(values.status), [values.status]);

  const isSpendFieldVisible =
    !isExternalCampaign || (values.type && CAMPAIGN_TYPES_WITH_SPEND_FIELD.includes(values.type));

  const isSpendFieldDisabled =
    !editableFields.spend || (values.type && CAMPAIGN_TYPES_WITH_SPEND_FIELD.includes(values.type));

  return (
    <SBody expanded={false} data-selector="campaign-edit-view">
      <div className={classes('name-container')}>
        <Field
          name="name"
          type={Widget.FlatInput}
          dataSelector="name"
          iconRight="edit"
          validate={maxLength(200)}
          maxLength={150}
          bgStyle="grey"
          defaultValue={autogeneratedName}
          placeholderPersistent={campaignNamePlaceholderPersistent}
          onInputChange={onCampaignNameInputChange}
          onResetClick={onResetNameClick}
          placeholder={campaignNamePlaceholderPersistent ? campaignNamePlaceholder : undefined}
          disabled={!editableFields.name}
          normalize={normalizeCampaignName}
          resetShown={campaignNameManuallyChange}
          fieldTooltipContent={'Click to edit campaign name'}
          resetTooltipContent={`Reset to ${autogeneratedName}`}
          className={classes('name-field')}
        />
        <Field
          name="status"
          color={badgeSelectorColor}
          type={Widget.BadgeSelector}
          className={classes('status-selector')}
          options={campaignStatuses}
          dataSelector="campaign-status"
        />
        {campaign && campaign.externalName && campaign.externalId && (
          <PlatformDetails
            source={campaign?.source}
            name={campaign.externalName}
            id={campaign.externalId}
            className={classes('platform-details')}
          />
        )}
      </div>
      <SPaper pt="18px" pb="2px" mt="13px" color="white">
        <div className={classes('campaign-main-info')}>
          <Field
            name="startDate"
            dataSelector="campaign-start-date-field"
            label="Start Date"
            type={Widget.Date}
            bgStyle="grey"
            validate={required}
            format={null}
            availableDates={availableStartDates}
            disabled={!editableFields.startDate}
            isRequired={editableFields.startDate}
            labelTooltipContent={
              <span>
                First day of this <br /> campaign’s flight.
              </span>
            }
          />
          <Field
            name="endDate"
            dataSelector="campaign-end-date-field"
            label="End Date"
            type={Widget.Date}
            bgStyle="grey"
            validate={required}
            format={null}
            availableDates={availableEndDates}
            disabled={!editableFields.endDate}
            calendarInputDataSelector={'campaign-end-date-input-field'}
            isRequired={editableFields.endDate}
            calendarInputPlaceholder="End Date"
            labelTooltipContent={
              <span>
                Last day of this <br /> campaign’s flight.
              </span>
            }
          />
          <div className={classes('divider')} />
          <Field
            dataSelector="campaign-budget-field"
            name="plannedBudget"
            type={shouldHideCampaignBudget ? Widget.Text : Widget.Currency}
            bgStyle="grey"
            label="Budget"
            currencyCode={currencyCode}
            validate={requiredCurrencyFormik}
            disabled={!editableFields.budget || shouldHideCampaignBudget}
            isRequired={editableFields.budget}
            allowNegative={false}
            shouldShowFieldTooltip={shouldHideCampaignBudget}
            fieldTooltipContent={NOT_AVAILABLE_BUDGET_TOOLTIP}
            format={formatPlannedBudgetValue}
            normalize={normalizePlannedBudgetValue}
            labelTooltipContent={
              <span>
                The amount of money <br /> allocated to pay for this <br /> campaign.
              </span>
            }
          />
          {isSpendFieldVisible && (
            <Field
              dataSelector="campaign-spend-field"
              name="budgetSpend"
              type={Widget.Currency}
              bgStyle="grey"
              label="Spend (Optional)"
              currencyCode={currencyCode}
              validate={currency}
              disabled={isSpendFieldDisabled}
              labelTooltipContent={
                <span>
                  The amount of money <br /> that has been spent of <br /> the allocated budget.
                </span>
              }
            />
          )}
          <Field
            dataSelector={`campaign-gratis-toggle`}
            name="gratis"
            bgStyle="grey"
            type={Widget.Toggle}
            label="gratis"
            defaultValue={GratisType.Paid}
            options={GRATIS_OPTIONS}
          />
        </div>
      </SPaper>
      <Title text="Campaign Details" className={classes('section-title')} />
      <SPaper pt="18px" mb="10px" color="white">
        <div className={classes('campaign-main-info')}>
          <Field
            name="type"
            dataSelector="campaign-type"
            type={Widget.Select}
            label="Campaign Type"
            bgStyle="grey"
            options={campaignTypesOptions}
            disabled={false}
            isSearchable
            isRequired={true}
            labelTooltipContent={
              <span>
                The kind of marketing activity <br /> this campaign represents.
              </span>
            }
          />
          <Field
            isOnlyId
            name="platforms"
            dataSelector="campaign-platforms"
            type={Widget.ActionableSelect}
            isMulti
            label="Platforms"
            validate={requiredArray}
            bgStyle="grey"
            options={platformsOptions}
            isRequired
            tree
            defaultExpandedKeys={defaultExpandedPlatformsIds}
            disabled={!editableFields.platforms}
            isSearchable
            useFlatControl
            visibleValues={3}
            labelTooltipContent={
              <span>
                The media platforms this <br /> campaign is running on or <br /> through.
              </span>
            }
          />
          <Field
            dataSelector="campaign-objective-field"
            name="objective"
            label="Objective"
            type={Widget.Select}
            bgStyle="grey"
            options={objectivesByGoals}
            disabled={!editableFields.objective}
            isSearchable
            isRequired={editableFields.objective}
            labelTooltipContent={
              <span>
                The principle goal metric <br /> (KPI) for this campaign.
              </span>
            }
          />
          <Field
            isOnlyId
            name="placements"
            dataSelector="campaign-placements"
            type={Widget.ActionableSelect}
            isMulti
            label="Placements"
            bgStyle="grey"
            options={placementOptions}
            maxLength={5}
            isSearchable
          />
          <Field
            name="kpi"
            dataSelector="campaign-kpi-field"
            type={Widget.Select}
            label="KPI"
            bgStyle="grey"
            options={kpiOptions}
            isSearchable
            isRequired
            ignoreAccents={false}
          />
          <Field
            name="provider"
            dataSelector="campaign-provider-field"
            type={Widget.Select}
            label="Provider"
            bgStyle="grey"
            options={campaignProviders}
            isSearchable
            disabled={!editableFields.provider}
            isRequired={editableFields.provider}
            components={filteredProviders && filteredProviders.length < 10 ? undefined : providerFilterComponents}
            labelTooltipContent={
              <span>
                The company managing this <br /> campaign, including external <br /> vendors.
              </span>
            }
            onInputChange={setProvidersSearch}
            ignoreAccents={false}
          />
        </div>
      </SPaper>
      <Title text="Target Audience" className={classes('section-title')} />
      <SPaper pt="18px" mb="10px" color="white">
        <div className={classes('campaign-main-info')}>
          <Field
            dataSelector="campaign-gender-field"
            name="genders"
            label="Gender"
            type={Widget.MultiSelect}
            bgStyle="grey"
            options={genders}
            disabled={!editableFields.genders}
            isRequired={editableFields.genders}
            labelTooltipContent={
              <span>
                The genders of <br /> the target audience.
              </span>
            }
          />
          <Field
            dataSelector="campaign-age-field"
            name="audienceAge"
            label="Audience Age"
            type={Widget.RangePicker}
            min={MIN_AGE}
            max={MAX_AGE}
            points={AGE_STEPS}
            disabled={!editableFields.audienceAge}
            labelTooltipContent={
              <span>
                The age band of <br /> the target audience.
              </span>
            }
          />
          <Field
            dataSelector="campaign-countries-field"
            name="territory"
            label="Countries"
            type={Widget.ActionableSelect}
            isMulti
            tree
            options={territories}
            bgStyle="grey"
            defaultExpandedKeys={['0']}
            isSearchable
            disabled={!editableFields.countries}
            isRequired={editableFields.countries}
            labelTooltipContent={
              <span>
                The countries that are <br /> targeted by this campaign.
              </span>
            }
          />
        </div>
        <Field
          dataSelector="campaign-details-field"
          name="details"
          type={Widget.LimitedTextField}
          label="Details"
          placeholder="Write target audience details here…"
          maxLength={280}
          bgStyle="grey"
          inputClassName={classes('copy-input')}
          wide
          disabled={isLoading}
          composition={FieldComposition.HORIZONTAL}
        />
      </SPaper>
      <Title text="Links" className={classes('section-title')} />
      <SPaper pt="18px" mb="10px" color="white">
        <>
          <Field
            name="linkfireLinks"
            label="linkfire links"
            placeholder="Paste in or search for Linkfire URL"
            wide
            maxLinksCount={CAMPAIGN_LINKFIRE_LINKS_LIMIT}
            type={Widget.LinkfireSearch}
            disabled={isLoading}
            maxTooltipText="Max 5 Linkfire links are supported per campaign. Remove one of them if you need to add the new one."
          />
          <Field
            dataSelector="destinations-field"
            name="destinations"
            type={Widget.Links}
            label="Destination Links"
            placeholder="Paste in a URL here to add a destination link for this Campaign."
            onError={handleDestinationsLinkError}
            resetError={handleDestinationsLinkResetError}
            maxLength={20}
            warning={destinationsWarning}
            limitText={'Remove a destination link to add a new one.'}
            bgStyle="grey"
            wide
            disabled={!editableFields.destinations}
            labelTooltipContent={
              <span>
                Displays destination links <br /> for this campaign.
              </span>
            }
          />
          {campaign?.purchaseOrder && (
            <Cell className={classes('purchase-order-link-cell')} label="Purchase Order">
              <PurchaseOrderLink projectId={projectId} purchaseOrder={campaign.purchaseOrder} />
            </Cell>
          )}
        </>
      </SPaper>
      <Title text="Ad Creative" className={classes('section-title')} />
      <SPaper pt="18px" mb="10px" color="white">
        <Field
          name="creativeLinks"
          label="Creative Link"
          wide
          maxLength={CAMPAIGN_CREATIVE_LINKS_LIMIT}
          type={Widget.Links}
          bgStyle="grey"
          disabled={isLoading}
          placeholder="Paste in a link to your ad creative."
          limitText="Max 5 Creative links are supported per campaign. Remove one of them if you need to add the new one."
          allowInvalid
        />
        <Field
          dataSelector="campaign-ad-copy-field"
          name="copy"
          type={Widget.LimitedTextField}
          label="Copy"
          placeholder="Write or paste ad copy here..."
          maxLength={150}
          bgStyle="grey"
          inputClassName={classes('copy-input')}
          wide
          disabled={isLoading}
          composition={FieldComposition.HORIZONTAL}
        />
      </SPaper>
      <Title
        text="Notes"
        tooltipContent={
          <span>
            Freeform notes about <br /> this campaign. Supports <br /> copy / paste, links, and <br /> formatting.
          </span>
        }
        className={classes('section-title')}
      />
      <Field
        dataSelector="campaign-notes-field"
        name="notes"
        type={Widget.Notes}
        label=""
        placeholder="Add a campaign note…"
        maxLength={5000}
        className={classes('notes-paper')}
        bgStyle="white"
        wide
        disabled={!editableFields.notes}
      />
      <FormFooter
        onSubmit={submitForm}
        onCancel={onCancelEditing}
        isSaveDisabled={!isValid}
        isBeingProcessed={isBeingProcessed}
        isRemovable={!!campaignUuid}
        customRemoveButton={<RemoveButton campaignUuid={campaignUuid} projectId={projectId} />}
      />
    </SBody>
  );
});
