import { KintoneRestAPIClient } from '@kintone/rest-api-client';
import invariant from 'tiny-invariant';
import { loadPluginConfigLookup } from '../common/config';
import { filterConfigByPluginContext, getPluginContext } from '../common/context';

interface UpdateRecordAttachmentFieldValueItem {
  fileKey: string;
}
type UpdateRecordAttachmentFieldValue = UpdateRecordAttachmentFieldValueItem[];

((PLUGIN_ID) => {
  kintone.events.on(
    ['app.record.create.show', 'app.record.edit.show', 'mobile.app.record.create.show', 'mobile.app.record.edit.show'],
    function (
      event:
        | kintone.events.AppRecordCreateShowEvent
        | kintone.events.AppRecordEditShowEvent
        | kintone.events.MobileAppRecordCreateShowEvent
        | kintone.events.MobileAppRecordEditShowEvent,
    ) {
      const mappings = loadPluginConfigLookup(PLUGIN_ID);
      mappings?.forEach((mapping) => {
        if (mapping.destAttachmentFieldCode in event.record) {
          (event.record[mapping.destAttachmentFieldCode] as any).disabled = true;
        }
      });
      return event;
    },
  );

  kintone.events.on(
    [
      'app.record.create.submit.success',
      'app.record.edit.submit.success',
      'mobile.app.record.create.submit.success',
      'mobile.app.record.edit.submit.success',
    ],
    async (
      event:
        | kintone.events.AppRecordCreateSubmitSuccessEvent
        | kintone.events.AppRecordEditSubmitSuccessEvent
        | kintone.events.MobileAppRecordCreateSubmitSuccessEvent
        | kintone.events.MobileAppRecordEditSubmitSuccessEvent,
    ) => {
      const context = await getPluginContext(event.appId, false);
      const mappings = filterConfigByPluginContext(loadPluginConfigLookup(PLUGIN_ID), context);
      if (mappings == null) {
        return event;
      }
      const client = new KintoneRestAPIClient();
      await Promise.all<void>(
        mappings.map(async (mapping) => {
          const lookupField = event.record[mapping.lookupFieldCode];
          invariant(
            lookupField?.type === 'NUMBER' || lookupField?.type === 'SINGLE_LINE_TEXT',
            'The field type of lookup field code must be number or single line text field.',
          );
          const lookupFieldProperty = context.lookupFields.find((p) => p.code === mapping.lookupFieldCode);
          invariant(lookupFieldProperty != null, 'The property of lookup field not found.');
          const destAttachmentField = event.record[mapping.destAttachmentFieldCode];
          invariant(
            destAttachmentField?.type === 'FILE',
            'The field type of destination attachment field code must be attachment field.',
          );
          const updateFileKeys: UpdateRecordAttachmentFieldValue = [];
          if (lookupField.value !== '') {
            const res = await client.record
              .getRecords({
                app: lookupFieldProperty.lookup.relatedApp.app,
                query: `${lookupFieldProperty.lookup.relatedKeyField} = "${lookupField.value?.replace(/[\\"]/g, '\\$&')}"`,
                totalCount: true,
              })
              .catch(() => null);
            if (res != null && res.totalCount === '1') {
              const srcRecordId = res.records[0].$id;
              invariant(srcRecordId.type === '__ID__', 'The field of Source record ID not found.');
              const srcAttachmentField = res.records[0][mapping.srcAttachmentFieldCode];
              invariant(
                srcAttachmentField.type === 'FILE',
                'The field type of source attachment field code must be attachment field.',
              );
              destAttachmentField.value.forEach((destFileInfo) => {
                if (
                  srcAttachmentField.value.find(
                    (srcFileInfo) =>
                      destFileInfo.name === srcFileInfo.name &&
                      destFileInfo.size === srcFileInfo.size &&
                      destFileInfo.contentType === srcFileInfo.contentType,
                  ) != null
                ) {
                  updateFileKeys.push({ fileKey: destFileInfo.fileKey });
                }
              });
              await Promise.all<void>(
                srcAttachmentField.value
                  .filter((srcFileInfo) => {
                    return (
                      destAttachmentField.value.find(
                        (destFileInfo) =>
                          destFileInfo.name === srcFileInfo.name &&
                          destFileInfo.size === srcFileInfo.size &&
                          destFileInfo.contentType === srcFileInfo.contentType,
                      ) == null
                    );
                  })
                  .map(async (srcFileInfo) => {
                    return client.file
                      .downloadFile({
                        fileKey: srcFileInfo.fileKey,
                      })
                      .then((fileData) => {
                        return client.file.uploadFile({
                          file: {
                            name: srcFileInfo.name,
                            data: new Blob([fileData], { type: srcFileInfo.contentType }),
                          },
                        });
                      })
                      .then((fileKey) => {
                        updateFileKeys.push(fileKey);
                      });
                  }),
              );
            }
          }
          if (
            destAttachmentField.value.length !== updateFileKeys.length ||
            destAttachmentField.value.every(
              (v) =>
                destAttachmentField.value.filter((e) => e.fileKey === v.fileKey).length !==
                updateFileKeys.filter((e) => e.fileKey === v.fileKey).length,
            )
          ) {
            await client.record.updateRecord({
              app: event.appId,
              id: event.recordId,
              record: {
                [mapping.destAttachmentFieldCode]: {
                  value: updateFileKeys,
                },
              },
            });
          }
        }),
      ).catch((e) => {
        console.error(e);
      });
      await new Promise((resolve) => setTimeout(resolve, 10000));
      return event;
    },
  );
})(kintone.$PLUGIN_ID);
