import { ConsentValue, Role } from 'loved-bridge/tables';
import type { ReactNode } from 'react';
import { useState } from 'react';
import type { IntlShape } from 'react-intl';
import { defineMessages, FormattedMessage, useIntl } from 'react-intl';
import { addOrUpdateMapperConsent, alertApiErrorMessage } from '../api';
import { autoHeightRef } from '../auto-height';
import { BoolView } from '../BoolView';
import type { FormSubmitHandler } from '../dom-helpers';
import { valueCasts } from '../dom-helpers';
import { Form } from '../dom-helpers';
import type { IMapperBeatmapsetConsent, IMapperConsent } from '../interfaces';
import ListInputCustom from '../ListInputCustom';
import { Modal } from '../Modal';
import { useOsuAuth } from '../osuAuth';
import { hasRole } from '../permissions';
import { UserInline } from '../UserInline';
import { consentMap } from './MapperConsents';

const messages = defineMessages({
  selectConsent: {
    defaultMessage: 'Select a consent option',
    description: '[Mapper consents] Mapper consent editor option',
  },
  submit: {
    defaultMessage: 'Submit',
    description: '[General] Submit button',
  },
  submitting: {
    defaultMessage: 'Submitting...',
    description: '[General] Submit button when in progress',
  },
});

function renderMapperConsentBeatmapsetInput(intl: IntlShape) {
  return (
    consentBeatmapset: IMapperBeatmapsetConsent | null,
    renderRemoveButton: () => ReactNode,
  ) => (
    <div className='box'>
      <table>
        <tbody>
          <tr>
            <td>
              <label htmlFor='beatmapset_id'>
                <FormattedMessage
                  defaultMessage='Beatmapset ID'
                  description='[Mapper consents] Mapper consent editor option'
                />
              </label>
            </td>
            <td>
              <input
                name='beatmapset_id'
                required
                type='number'
                defaultValue={consentBeatmapset?.beatmapset_id}
              />
            </td>
          </tr>
          <tr>
            <td>
              <label htmlFor='consent'>
                <FormattedMessage
                  defaultMessage='Consent'
                  description='[Mapper consents] Mapper consent editor option'
                />
              </label>
            </td>
            <td>
              <select
                name='consent'
                required
                defaultValue={consentBeatmapset == null ? undefined : +consentBeatmapset.consent}
                key={
                  consentBeatmapset == null
                    ? undefined
                    : +consentBeatmapset.consent /* TODO: Workaround for https://github.com/facebook/react/issues/21025 */
                }
              >
                <option hidden value=''>
                  {intl.formatMessage(messages.selectConsent)}
                </option>
                {[true, false].map((consentValue) => (
                  <BoolView key={+consentValue} option value={consentValue} />
                ))}
              </select>
            </td>
          </tr>
          <tr>
            <td>
              <label htmlFor='consent_reason'>
                <FormattedMessage
                  defaultMessage='Reason'
                  description='[Mapper consents] Mapper consent editor option'
                />
              </label>
            </td>
            <td>
              <textarea
                name='consent_reason'
                defaultValue={consentBeatmapset?.consent_reason ?? undefined}
                ref={autoHeightRef}
              />
            </td>
          </tr>
          <tr>
            <td>{renderRemoveButton()}</td>
          </tr>
        </tbody>
      </table>
    </div>
  );
}

type MapperConsentEditorProps =
  | {
      consent: IMapperConsent | undefined;
      editSelf: true;
      onConsentUpdate: (consent: IMapperConsent) => void;
    }
  | {
      consent: IMapperConsent;
      editSelf?: false;
      onConsentUpdate: (consent: IMapperConsent) => void;
    };

export default function MapperConsentEditor({
  consent,
  editSelf,
  onConsentUpdate,
}: MapperConsentEditorProps) {
  const authUser = useOsuAuth().user!;
  const intl = useIntl();
  const [busy, setBusy] = useState(false);
  const [modalOpen, setModalOpen] = useState(false);

  const onSubmit: FormSubmitHandler = (_, then, controls) => {
    let consentValue: ConsentValue | null = null;
    let consentReason: string | null = null;
    const consentBeatmapsets: Partial<IMapperBeatmapsetConsent>[] = [];

    const controlsCount = controls.length;
    const controlCasts = {
      beatmapset_id: valueCasts.int,
      consent: valueCasts.bool,
      consent_reason: valueCasts.string,
    } as any; // TODO: typing
    let currentBeatmapset = -1;
    let i = 0;

    for (; i < controlsCount && (controls[i] as any).name !== 'beatmapset_id'; i++) {
      const control = controls[i] as any; // TODO: typing

      if (control.name === 'consent') {
        consentValue = valueCasts.int(control.value);
      } else if (control.name === 'consent_reason') {
        consentReason = valueCasts.string(control.value);
      }
    }

    for (; i < controlsCount; i++) {
      const control = controls[i] as any; // TODO: typing

      if (!['beatmapset_id', 'consent', 'consent_reason'].includes(control.name)) {
        continue;
      }

      // Assume the controls are in correct order
      if (control.name === 'beatmapset_id') {
        currentBeatmapset++;
        consentBeatmapsets.push({});
      }

      consentBeatmapsets[currentBeatmapset][control.name as keyof IMapperBeatmapsetConsent] =
        controlCasts[control.name](control.value);
    }

    return addOrUpdateMapperConsent(
      {
        consent: consentValue,
        consent_reason: consentReason,
        user_id: editSelf ? authUser.id : consent!.user_id,
      },
      consentBeatmapsets as Pick<
        IMapperBeatmapsetConsent,
        'beatmapset_id' | 'consent' | 'consent_reason'
      >[],
    )
      .then((response) => onConsentUpdate(response.body))
      .then(then)
      .catch(alertApiErrorMessage)
      .finally(() => setModalOpen(false));
  };

  return (
    <>
      {editSelf ? (
        <button type='button' onClick={() => setModalOpen(true)}>
          <FormattedMessage
            defaultMessage='Edit my consent'
            description='[Mapper consents] Button to edit the mapper consent status of the current user'
          />
        </button>
      ) : (
        <td>
          <button type='button' onClick={() => setModalOpen(true)} className='fake-a'>
            Edit
          </button>
        </td>
      )}
      <Modal close={() => setModalOpen(false)} open={modalOpen}>
        <FormattedMessage
          defaultMessage='Editing {user}'
          description='[Mapper consents] Title of mapper consent editor modal'
          tagName='h2'
          values={{ user: <UserInline user={editSelf ? authUser : consent!.mapper} /> }}
        />
        <Form busyState={[busy, setBusy]} onSubmit={onSubmit}>
          <table>
            <tbody>
              <tr>
                <td>
                  <label htmlFor='consent'>
                    <FormattedMessage
                      defaultMessage='Consent'
                      description='[Mapper consents] Mapper consent editor option'
                    />
                  </label>
                </td>
                <td>
                  <select
                    name='consent'
                    required
                    // eslint-disable-next-line eqeqeq
                    defaultValue={consent?.consent === null ? 'null' : consent?.consent}
                    key={
                      consent?.consent /* TODO: Workaround for https://github.com/facebook/react/issues/21025 */
                    }
                  >
                    <option hidden value=''>
                      {intl.formatMessage(messages.selectConsent)}
                    </option>
                    {[ConsentValue.yes, ConsentValue.no].map((consentValue) => (
                      <option
                        key={consentValue}
                        className={consentMap[consentValue][1]}
                        value={consentValue}
                      >
                        {intl.formatMessage(consentMap[consentValue][0])}
                      </option>
                    ))}
                    {hasRole(authUser, Role.captain) &&
                      ['null' as const, ConsentValue.unreachable].map((consentValue) => (
                        <option
                          key={consentValue}
                          className={consentMap[consentValue][1]}
                          value={consentValue}
                        >
                          {intl.formatMessage(consentMap[consentValue][0])}
                        </option>
                      ))}
                  </select>
                </td>
              </tr>
              <tr>
                <td>
                  <label htmlFor='consent_reason'>
                    <FormattedMessage
                      defaultMessage='Reason'
                      description='[Mapper consents] Mapper consent editor option'
                    />
                  </label>
                </td>
                <td>
                  <textarea
                    name='consent_reason'
                    defaultValue={consent?.consent_reason ?? undefined}
                    ref={autoHeightRef}
                  />
                </td>
              </tr>
            </tbody>
          </table>
          <FormattedMessage
            defaultMessage='Beatmapsets'
            description='[Mapper consents] Sub-header for mapper consent editor'
            tagName='h3'
          />
          <ListInputCustom
            items={consent?.beatmapset_consents ?? []}
            renderItemInput={renderMapperConsentBeatmapsetInput(intl)}
          />
          <button type='submit' className='modal-submit-button'>
            {intl.formatMessage(busy ? messages.submitting : messages.submit)}
          </button>
        </Form>
      </Modal>
    </>
  );
}