import React from 'react';

import { produce } from 'immer';
import moize from 'moize';
import { useTranslation } from 'react-i18next';
import invariant from 'tiny-invariant';
import {
  loadPluginConfigLookup,
  PluginConfigLookup,
  PluginConfigLookupItem,
  savePluginConfigLookup,
} from '../common/config';
import { filterConfigByPluginContext, getPluginContext, PluginContext } from '../common/context';
import KintonePluginAlert from '../common/ui/KintonePluginAlert';
import KintonePluginButton from '../common/ui/KintonePluginButton';
import KintonePluginDesc from '../common/ui/KintonePluginDesc';
import KintonePluginLabel from '../common/ui/KintonePluginLabel';
import KintonePluginRequire from '../common/ui/KintonePluginRequire';
import KintonePluginRow from '../common/ui/KintonePluginRow';
import KintonePluginSelect, { KintonePluginSelectOptionData } from '../common/ui/KintonePluginSelect';
import KintonePluginTable from '../common/ui/KintonePluginTable';
import KintonePluginTableTd from '../common/ui/KintonePluginTableTd';
import KintonePluginTableTh from '../common/ui/KintonePluginTableTh';
import KintonePluginTitle from '../common/ui/KintonePluginTitle';

import styles from './Settings.module.css';

const hasDuplicate = <T,>(items: T[], func: (a: T, b: T) => boolean = (a, b) => a === b): boolean => {
  return items.find((item, idx) => items.some((item2, idx2) => func(item, item2) && idx !== idx2)) != null;
};

const hasDuplicateDestAttachmentFields = (rows: PluginConfigLookup): boolean => {
  return hasDuplicate<PluginConfigLookupItem>(rows, (a, b) => a.destAttachmentFieldCode === b.destAttachmentFieldCode);
};

interface FieldSelectTableRowProps {
  context: PluginContext;
  row: PluginConfigLookupItem;
  onUpdate: (item: PluginConfigLookupItem) => void;
  onClickAddRow: () => void;
  onClickRemoveRow: () => void;
}

const FieldSelectTableRow: React.FC<FieldSelectTableRowProps> = (props) => {
  const { context, row, onUpdate, onClickAddRow, onClickRemoveRow } = props;
  const defaultOption: KintonePluginSelectOptionData = React.useMemo(() => {
    return { value: '', label: '--', disabled: true };
  }, []);
  const lookupOptions: KintonePluginSelectOptionData[] = React.useMemo(() => {
    return [
      defaultOption,
      ...context.lookupFields.map((property) => ({
        value: property.code,
        label: `${property.label} (${property.code})`,
      })),
    ];
  }, [context.lookupFields, defaultOption]);

  const srcAppId = context.lookupFields.find((field) => field.code === row.lookupFieldCode)?.lookup.relatedApp.app;
  const srcFileOptions: KintonePluginSelectOptionData[] = React.useMemo(() => {
    return [
      defaultOption,
      ...(srcAppId != null
        ? context.attachmentFields[srcAppId].map((property) => ({
            value: property.code,
            label: `${property.label} (${property.code})`,
          }))
        : []),
    ];
  }, [srcAppId, context.attachmentFields, defaultOption]);

  const destFileOptions: KintonePluginSelectOptionData[] = React.useMemo(() => {
    return [
      defaultOption,
      ...(srcAppId != null
        ? context.attachmentFields[context.appId].map((property) => ({
            value: property.code,
            label: `${property.label} (${property.code})`,
          }))
        : []),
    ];
  }, [context.appId, context.attachmentFields, defaultOption, srcAppId]);

  const handleOnChangeLookup = (code: string) => {
    const draft = {
      lookupFieldCode: code,
      srcAttachmentFieldCode: '',
      destAttachmentFieldCode: '',
    };
    onUpdate(draft);
  };
  const handleOnChangeSrcFile = (code: string) => {
    const draft = {
      lookupFieldCode: row.lookupFieldCode,
      srcAttachmentFieldCode: code,
      destAttachmentFieldCode: '',
    };
    onUpdate(draft);
  };

  const handleOnChangeDestFile = (code: string) => {
    const draft = {
      lookupFieldCode: row.lookupFieldCode,
      srcAttachmentFieldCode: row.srcAttachmentFieldCode,
      destAttachmentFieldCode: code,
    };
    onUpdate(draft);
  };

  const handleOnClickAddRow: React.MouseEventHandler<HTMLButtonElement> = (e) => {
    e.preventDefault();
    onClickAddRow();
  };

  const handleOnClickRemoveRow: React.MouseEventHandler<HTMLButtonElement> = (e) => {
    e.preventDefault();
    onClickRemoveRow();
  };

  return (
    <tr>
      <KintonePluginTableTd variant="control">
        <KintonePluginSelect value={row.lookupFieldCode} onChange={handleOnChangeLookup} options={lookupOptions} />
      </KintonePluginTableTd>
      <KintonePluginTableTd variant="control">
        <KintonePluginSelect
          value={row.srcAttachmentFieldCode}
          onChange={handleOnChangeSrcFile}
          options={srcFileOptions}
        />
      </KintonePluginTableTd>
      <KintonePluginTableTd variant="control">
        <KintonePluginSelect
          value={row.destAttachmentFieldCode}
          onChange={handleOnChangeDestFile}
          options={destFileOptions}
        />
      </KintonePluginTableTd>
      <KintonePluginTableTd variant="operation">
        <KintonePluginButton variant="add-row-image" onClick={handleOnClickAddRow} />
        <KintonePluginButton variant="remove-row-image" onClick={handleOnClickRemoveRow} />
      </KintonePluginTableTd>
    </tr>
  );
};

const cachedPluginContext = moize.promise(getPluginContext);

interface SettingsProps {
  pluginId: string;
}

const Settings: React.FC<SettingsProps> = (props) => {
  const { pluginId } = props;
  const { t } = useTranslation();
  const appId = kintone.app.getId();
  invariant(appId, 'The app ID is not available. Please ensure you are on a Kintone app page.');
  const context = React.use(cachedPluginContext(appId, true));

  const [mappings, setMappings] = React.useState<PluginConfigLookup>(
    () =>
      filterConfigByPluginContext(loadPluginConfigLookup(pluginId), context) ?? [
        { lookupFieldCode: '', srcAttachmentFieldCode: '', destAttachmentFieldCode: '' },
      ],
  );

  const [error, setError] = React.useState<string>('');

  const handleOnUpdateFieldSelect = (idx: number, item: PluginConfigLookupItem) => {
    setError('');
    setMappings(
      produce((draft) => {
        draft[idx] = item;
      }),
    );
  };

  const handleOnAddFieldSelect = (idx: number) => {
    setError('');
    if (mappings.length < context.attachmentFields[context.appId].length) {
      setMappings(
        produce((draft) => {
          draft.splice(idx + 1, 0, {
            lookupFieldCode: '',
            srcAttachmentFieldCode: '',
            destAttachmentFieldCode: '',
          });
        }),
      );
    }
  };

  const handleOnRemoveFieldSelect = (idx: number) => {
    setError('');
    if (mappings.length === 1) {
      setMappings(
        produce((draft) => {
          draft[idx] = {
            lookupFieldCode: '',
            srcAttachmentFieldCode: '',
            destAttachmentFieldCode: '',
          };
        }),
      );
    } else if (mappings.length > 1) {
      setMappings(
        produce((draft) => {
          draft.splice(idx, 1);
        }),
      );
    }
  };

  const handleOnSubmit = (e: React.FormEvent<HTMLFormElement>) => {
    e.preventDefault();
    if (mappings.find((item) => item.destAttachmentFieldCode === '') != null) {
      setError(t('settings.lookup.errors.incomplete-field-mapping-found'));
    } else if (hasDuplicateDestAttachmentFields(mappings)) {
      setError(t('settings.lookup.errors.same-destination-attachment-field-found'));
    } else {
      setError('');
      savePluginConfigLookup(mappings, () => {
        alert(t('on-saved'));
        window.location.href = `../../flow?app=${appId}`;
      });
    }
  };

  const handleOnClickCancel = () => {
    setError('');
    window.location.href = `../../${appId}/plugin/`;
  };

  return (
    <section className="settings">
      <KintonePluginLabel>{t('title')}</KintonePluginLabel>
      {error !== '' && (
        <KintonePluginRow>
          <KintonePluginAlert>{error}</KintonePluginAlert>
        </KintonePluginRow>
      )}
      <form onSubmit={handleOnSubmit}>
        <KintonePluginRow>
          <KintonePluginTitle>
            {t('settings.lookup.title')}
            <KintonePluginRequire>*</KintonePluginRequire>
          </KintonePluginTitle>
          <KintonePluginDesc>{t('settings.lookup.description')}</KintonePluginDesc>
          {context.lookupFields.length === 0 ? (
            <KintonePluginAlert>{t('settings.lookup.errors.no-lookup-field-found')}</KintonePluginAlert>
          ) : (
            <KintonePluginTable>
              <thead>
                <tr>
                  <KintonePluginTableTh variant="title">
                    {t('settings.lookup.messages.lookup-field')}
                  </KintonePluginTableTh>
                  <KintonePluginTableTh variant="title">
                    {t('settings.lookup.messages.source-attachment-field')}
                  </KintonePluginTableTh>
                  <KintonePluginTableTh variant="title">
                    {t('settings.lookup.messages.destination-attachment-field')}
                  </KintonePluginTableTh>
                  <KintonePluginTableTh variant="blankspace" />
                </tr>
              </thead>
              <tbody>
                {mappings.map((row, idx) => (
                  <FieldSelectTableRow
                    key={`${row.lookupFieldCode}-${row.srcAttachmentFieldCode}-${row.destAttachmentFieldCode}-${idx}`}
                    context={context}
                    row={row}
                    onUpdate={(draft) => {
                      handleOnUpdateFieldSelect(idx, draft);
                    }}
                    onClickAddRow={() => {
                      handleOnAddFieldSelect(idx);
                    }}
                    onClickRemoveRow={() => {
                      handleOnRemoveFieldSelect(idx);
                    }}
                  />
                ))}
              </tbody>
            </KintonePluginTable>
          )}
        </KintonePluginRow>
        <KintonePluginRow className={styles.buttons}>
          <KintonePluginButton variant="dialog-cancel" type="button" onClick={handleOnClickCancel}>
            {t('buttons.cancel')}
          </KintonePluginButton>
          <KintonePluginButton variant="dialog-ok" type="submit">
            {t('buttons.save')}
          </KintonePluginButton>
        </KintonePluginRow>
      </form>
    </section>
  );
};
export default Settings;
