import { useState } from 'react';

import type {
  AlbumLookupConfig,
  LookupServiceLinks,
} from '~/lib/songwhipApi/types';
import type { FormApi, FormOnSubmit } from '~/src/components/Form';
import type { TimePickerOption } from '~/src/components/TimePicker';
import type { RefObject } from 'react';
import type { MetadataChanges, OnSubmit } from './types';

import Box from '~/src/components/Box';
import DateInput from '~/src/components/DateInput';
import Form, { FORM_SECTION_SPACING } from '~/src/components/Form';
import InputLabel from '~/src/components/InputLabel';
import Text from '~/src/components/Text';
import TextInput from '~/src/components/TextInput';
import { isTime, TimePicker } from '~/src/components/TimePicker';
import { TimezonePicker } from '~/src/components/TimezonePicker';
import useTheme from '~/src/hooks/useTheme';
import { useI18n } from '~/src/lib/i18n';
import { formatDateIso } from '~/src/lib/utils/formatDate';
import { CollapsableSection } from './CollapsableSection';
import { CustomServiceLinks } from './CustomServiceLinks';
import { ReleaseIdentifierInput } from './ReleaseIdentifierInput';
import {
  cleanFormValue,
  hasServiceLinksChanged,
  hasValueChanged,
  parseServiceLinksForm,
} from './utils';

interface FieldConfig<V> {
  initialValue?: V;
  required?: boolean;
  isDisabled?: boolean;
  errors?: {
    empty?: string;
    invalid?: string;
  };
}

interface FormValues extends Record<string, string> {
  albumName: string;
  upc: string;
  isrc: string;
  url: string;
  releaseDate: string;
  releaseTime: string;
  releaseTimezone: string;
}

export interface PrereleaseMetadataFormProps {
  apiRef?: RefObject<FormApi | null>;

  isDisabled?: boolean;
  isAdvancedSettingsEnabled?: boolean;
  isReleaseTimezoneEnabled?: boolean;

  config?: {
    albumName?: FieldConfig<string>;
    upc?: FieldConfig<string | null>;
    isrc?: FieldConfig<string | null>;
    url?: FieldConfig<string | null>;
    releaseDate?: FieldConfig<string>;
    releaseTimezone?: FieldConfig<string>;
    serviceLinks?: FieldConfig<LookupServiceLinks | null>;
  };

  onSubmit: OnSubmit;
  onChange?(params: { isValid: boolean }): void;
}

export const PrereleaseMetadataForm = ({
  apiRef,
  isDisabled,
  isAdvancedSettingsEnabled = true,
  isReleaseTimezoneEnabled = false,
  onSubmit,
  onChange,
  config: {
    albumName,
    releaseDate,
    releaseTimezone,
    upc,
    isrc,
    url,
    serviceLinks,
  } = {},
}: PrereleaseMetadataFormProps) => {
  const { t, tx } = useI18n('prerelease');
  const theme = useTheme();

  const initialAlbumName = albumName?.initialValue;
  const initialUpc = upc?.initialValue;
  const initialIsrc = isrc?.initialValue;
  const initialServiceLinks = serviceLinks?.initialValue;
  const initialReleaseDateValues = toReleaseDateValues(
    releaseDate?.initialValue,
    releaseTimezone?.initialValue
  );

  // store current selected values in order to manage dependent fields state
  const [values, setValues] = useState<{
    releaseDate?: string;
  }>({
    releaseDate: initialReleaseDateValues.date,
  });

  const [errors, setErrors] = useState<{
    releaseDateEmpty?: string;
    releaseIdentifierEmpty?: string;
  }>({
    releaseDateEmpty: !initialReleaseDateValues.date
      ? releaseDate?.errors?.empty
      : undefined,

    releaseIdentifierEmpty:
      !upc?.initialValue && !isrc?.initialValue && !url?.initialValue
        ? (upc?.errors?.empty ?? isrc?.errors?.empty ?? url?.errors?.empty)
        : undefined,
  });

  const handleOnSubmit: FormOnSubmit<FormValues> = async ({
    values: {
      albumName: albumNameValue,
      upc: upcValue,
      isrc: isrcValue,
      url: urlValue,
      releaseDate: releaseDateValue,
      // theses fields behind ff so needs default values
      releaseTime: releaseTimeValue = initialReleaseDateValues.time,
      releaseTimezone: releaseTimezoneValue = initialReleaseDateValues.timezone,
      ...restValues
    },
  }) => {
    const metadataChanges: MetadataChanges = {};
    const lookupConfigChanges: AlbumLookupConfig = {};

    let newServiceLinks = initialServiceLinks ?? {};

    const newAlbumName = cleanFormValue(albumNameValue);
    const newUpc = cleanFormValue(upcValue);
    const newIsrc = cleanFormValue(isrcValue);
    const newUrl = cleanFormValue(urlValue);
    const newReleaseTimezone = releaseTimezoneValue;

    const newReleaseDate = releaseDateValue
      ? formatDateIso(`${releaseDateValue}T${releaseTimeValue}Z`)
      : undefined;

    if (hasValueChanged(initialAlbumName, newAlbumName)) {
      metadataChanges.name = newAlbumName;
    }

    if (hasValueChanged(initialReleaseDateValues?.date, newReleaseDate)) {
      metadataChanges.releaseDate = newReleaseDate || null;
    }

    if (
      hasValueChanged(initialReleaseDateValues?.timezone, newReleaseTimezone)
    ) {
      metadataChanges.releaseTimezone = newReleaseTimezone || null;
    }

    if (hasValueChanged(initialUpc, newUpc)) {
      metadataChanges.upc = newUpc || null;
      lookupConfigChanges.upc = newUpc || null; // Use null to remove the value from the config.
    }

    if (hasValueChanged(initialIsrc, newIsrc)) {
      // Temporary assign isrc to the albums.upc field, since it is a required / unique database field.
      // We should either drop the unique constraint or remove the field.
      if (newIsrc) metadataChanges.upc = newIsrc;
      lookupConfigChanges.isrc = newIsrc || null; // Use null to remove the value from the config.
    }

    if (hasValueChanged(url?.initialValue, newUrl)) {
      if (newUrl) metadataChanges.upc = null;
      lookupConfigChanges.url = newUrl || null;
    }

    newServiceLinks = parseServiceLinksForm(restValues);

    if (hasServiceLinksChanged(initialServiceLinks ?? {}, newServiceLinks)) {
      lookupConfigChanges.links = newServiceLinks;
    }

    await onSubmit({
      albumName: newAlbumName,
      releaseDate: newReleaseDate,
      releaseTimezone: newReleaseTimezone,
      upc: newUpc,
      isrc: newIsrc,
      url: newUrl,
      serviceLinks: newServiceLinks,
      metadataChanges: Object.keys(metadataChanges).length
        ? metadataChanges
        : undefined,
      lookupConfigChanges: Object.keys(lookupConfigChanges).length
        ? lookupConfigChanges
        : undefined,
    });
  };

  return (
    <Form
      apiRef={apiRef}
      trackingId="prereleaseMetadataForm"
      testId="prereleaseMetadataForm"
      onChange={onChange}
      isDisabled={isDisabled}
      onSubmit={handleOnSubmit}
      flexColumn
      gap={FORM_SECTION_SPACING}
    >
      <Box>
        <InputLabel
          value={t('labels.releaseName')}
          description={tx('releaseNameInputHelp')}
        >
          <TextInput
            autoFocus
            testId="albumNameInput"
            maxLength={255}
            height="5rem"
            isDisabled={albumName?.isDisabled}
            required={albumName?.required ?? true}
            name="albumName"
            placeholder={t('releaseNamePlaceholder')}
            defaultValue={initialAlbumName}
          />
        </InputLabel>
      </Box>
      <Box>
        <InputLabel
          value={t('labels.releaseDate')}
          description={t('releaseDateInputHelp')}
        >
          {errors.releaseDateEmpty && (
            <Text size="1.3rem" color={theme.colorDanger} margin="0 0 0.7rem">
              {errors.releaseDateEmpty}
            </Text>
          )}
          <DateInput
            testId="releaseDateInput"
            height="5rem"
            min={formatDateIso(new Date(), { stripTime: true })}
            name="releaseDate"
            isDisabled={releaseDate?.isDisabled}
            defaultValue={initialReleaseDateValues.date}
            placeholder={t('releaseDatePlaceholder')}
            required={releaseDate?.required ?? false}
            borderColor={
              errors.releaseDateEmpty ? theme.colorDanger : undefined
            }
            onChange={({ value }) => {
              setValues((prev) => ({ ...prev, releaseDate: value }));
              setErrors((prev) => ({ ...prev, releaseDateEmpty: undefined }));
            }}
          />
          {isReleaseTimezoneEnabled && (
            <Box flexRow gap="1rem" margin="1rem 0 0">
              <TimePicker
                testId="releaseTimeSelect"
                name="releaseTime"
                defaultValue={initialReleaseDateValues.time}
                step={30}
                isDisabled={releaseDate?.isDisabled || !values.releaseDate}
              />
              <Box flexGrow>
                <TimezonePicker
                  testId="releaseTimezoneSelect"
                  name="releaseTimezone"
                  defaultValue={initialReleaseDateValues.timezone}
                  date={values.releaseDate}
                  isDisabled={releaseDate?.isDisabled || !values.releaseDate}
                />
              </Box>
            </Box>
          )}
        </InputLabel>
      </Box>
      <ReleaseIdentifierInput
        initialUpc={upc?.initialValue}
        initialIsrc={isrc?.initialValue}
        initialUrl={url?.initialValue}
        required={upc?.required ?? isrc?.required ?? url?.required ?? false}
        error={errors.releaseIdentifierEmpty}
        onChange={() => {
          setErrors((prev) => ({ ...prev, releaseIdentifierEmpty: undefined }));
        }}
      />
      {isAdvancedSettingsEnabled && (
        <CollapsableSection
          title={t('labels.advancedSettings')}
          testId="advancedSettings"
        >
          <CustomServiceLinks links={initialServiceLinks ?? {}} />
        </CollapsableSection>
      )}
    </Form>
  );
};

const toReleaseDateValues = (
  value: string = '',
  timezone: string = 'America/New_York'
): { date?: string; time: TimePickerOption['id']; timezone: string } => {
  const date = value ? new Date(value).toISOString() : undefined;
  const time = date?.substring(11, 16);

  return { date, time: isTime(time) ? time : '00:00', timezone };
};
