import { CustomModal } from 'dashboard/core/components/modal';
import { ModalContext } from 'dashboard/core/contexts/modals';
import { useEventTypes } from 'dashboard/core/utils/event-stream';
import {
  grantIAMRolePublishPermissions,
  grantIAMRoleSubscriberPermissions,
} from 'dashboard/core/utils/iam_role';
import { ALL_ENVIRONMENTS } from 'dashboard/definitions/environments';
import { code } from 'dashboard/generated/controlplane';
import * as _ from 'lodash';
import * as React from 'react';
import { useContext, useState } from 'react';
import {
  Button,
  ButtonType,
  Color,
  Column,
  CoreLink,
  CoreText,
  Display,
  FormGroup,
  FormGroupOrientation,
  Grid,
  Input,
  InputType,
  JustifyContent,
  Layout,
  LoadingSpinner,
  Select,
} from 'twitch-core-ui';

import twirp = code.justin.tv.eventbus.controlplane;
interface Props {
  service?: twirp.IService;
  onSuccess: (publisherGrant: twirp.IAuthorizedFieldPublisherGrant) => void;
  prefilledARN?: string;
  prefilledEventType?: string;
  prefilledEnvironment?: string;
  prefilledMessageName?: string;
  prefilledFieldName?: string;
  subscriberGrant?: boolean;
  authorizedFields?: twirp.IAuthorizedField[];
}

export const GrantPermissionsModal = ({
  onSuccess,
  service,
  prefilledARN,
  prefilledEventType,
  prefilledEnvironment,
  prefilledMessageName,
  prefilledFieldName,
  subscriberGrant,
  authorizedFields,
}: Props) => {
  const enableSubGrantFields = subscriberGrant ? true : false;

  const [arn, setArn] = useState('');
  const [eventType, setEventType] = useState('');
  const [submitError, setErrorText] = useState('');
  const [environment, setEnvironment] = useState('');
  const [messageName, setMessageName] = useState('');
  const [fieldName, setFieldName] = useState('');

  const { hideModal } = useContext(ModalContext);

  const arnEditDisabled = !_.isNil(prefilledARN);
  const etEditDisabled = !_.isNil(prefilledEventType);
  const envEditDisabled = !_.isNil(prefilledEnvironment);
  const mnEditDisabled = !_.isNil(prefilledMessageName);
  const fnEditDisabled = !_.isNil(prefilledFieldName);

  const authorizedFieldOptionMapping = new Map<string, string[]>();
  if (authorizedFields) {
    _.each(authorizedFields, (af) => {
      if (!authorizedFieldOptionMapping.get(af.messageName!)) {
        authorizedFieldOptionMapping.set(af.messageName!, [af.fieldName!]);
      } else {
        authorizedFieldOptionMapping.set(af.messageName!, [
          ...authorizedFieldOptionMapping.get(af.messageName!)!,
          af.fieldName!,
        ]);
      }
    });
  }

  const authorizedFieldMessageNameOpts = _.uniq(
    _.map(authorizedFields, (af) => af.messageName!),
  );

  const authorizedFieldFieldNameOpts =
    messageName === ''
      ? new Array<string>()
      : authorizedFieldOptionMapping.get(messageName)!;

  if (prefilledARN && arn !== prefilledARN) {
    setArn(prefilledARN);
  }
  if (prefilledEventType && eventType !== prefilledEventType) {
    setEventType(prefilledEventType);
  }
  if (prefilledEnvironment && environment !== prefilledEnvironment) {
    setEnvironment(prefilledEnvironment);
  }
  if (prefilledMessageName && messageName !== prefilledMessageName) {
    setMessageName(prefilledMessageName);
  }
  if (prefilledFieldName && fieldName !== prefilledFieldName) {
    setFieldName(prefilledFieldName);
  }

  const {
    eventTypes,
    loading,
    error: eventRetrievalError,
  }: {
    eventTypes: twirp.IEventType[];
    error: Error | null;
    loading: boolean;
  } = (() => {
    if (!_.isNil(service)) {
      return useEventTypes(service.ldapGroup!);
    }
    return {
      eventTypes: [
        {
          name: eventType,
        },
      ],
      loading: false,
      error: null,
    };
  })();

  const handleIAMRoleChange = (
    e: React.FormEvent<HTMLSelectElement | HTMLInputElement>,
  ) => {
    setArn(e.currentTarget.value);
  };
  const handleEventTypeChange = (e: React.FormEvent<HTMLSelectElement>) => {
    setEventType(e.currentTarget.value);
  };
  const handleEnvironmentChange = (e: React.FormEvent<HTMLSelectElement>) => {
    setEnvironment(e.currentTarget.value);
  };
  const handleMessageNameChange = (e: React.FormEvent<HTMLSelectElement>) => {
    setMessageName(e.currentTarget.value);
  };
  const handleFieldNameChange = (e: React.FormEvent<HTMLSelectElement>) => {
    setFieldName(e.currentTarget.value);
  };

  const submitGrant = () => {
    if (subscriberGrant) {
      grantIAMRoleSubscriberPermissions(
        eventType,
        arn,
        environment,
        messageName,
        fieldName,
        (sg: twirp.IAuthorizedFieldSubscriberGrant) => {
          onSuccess(sg);
          hideModal();
        },
        (e: string) => {
          setErrorText(e);
        },
      );
    } else {
      grantIAMRolePublishPermissions(
        eventType,
        arn,
        environment,
        (publisherGrant: twirp.IAuthorizedFieldPublisherGrant) => {
          onSuccess(publisherGrant);
          hideModal();
        },
        (e: string) => {
          setErrorText(e);
        },
      );
    }
  };

  const submitDisabled =
    arn === '' ||
    eventType === '' ||
    environment === '' ||
    (messageName === '' && enableSubGrantFields) ||
    (fieldName === '' && enableSubGrantFields);

  return (
    <CustomModal
      title={
        subscriberGrant
          ? 'Grant IAM Role Subscriber Permission'
          : 'Grant IAM Role Publish Permission'
      }
      isOpen={true}
      onClose={hideModal}
    >
      {loading ? (
        <LoadingSpinner />
      ) : (
        <Grid>
          <Column cols={12}>
            <Layout padding={1}>
              <FormGroup
                label="ARN"
                id="arn"
                orientation={FormGroupOrientation.Horizontal}
              >
                {service ? (
                  <Select
                    value={arn}
                    onChange={handleIAMRoleChange}
                    id="arn"
                    disabled={arnEditDisabled}
                  >
                    <option key="" value="" />
                    {_.map(service.iamRoles, (iamRole, i) => {
                      return <option key={i}>{iamRole.arn}</option>;
                    })}
                  </Select>
                ) : (
                  <Input
                    type={InputType.Text}
                    onChange={handleIAMRoleChange}
                    value={arn}
                    disabled={arnEditDisabled}
                  />
                )}
              </FormGroup>
            </Layout>
          </Column>
          <Column cols={12}>
            <Layout padding={1}>
              <FormGroup
                label="Event Type"
                id="eventType"
                orientation={FormGroupOrientation.Horizontal}
              >
                <Select
                  value={eventType}
                  onChange={handleEventTypeChange}
                  disabled={etEditDisabled}
                  id="eventType"
                >
                  <option key="" value="" />
                  {_.map(eventTypes, (et, i) => {
                    return <option key={i}>{et.name}</option>;
                  })}
                </Select>
              </FormGroup>
            </Layout>
          </Column>
          <Column cols={12}>
            <Layout padding={1}>
              <FormGroup
                label="Environment"
                id="environment"
                orientation={FormGroupOrientation.Horizontal}
              >
                <Select
                  value={environment}
                  onChange={handleEnvironmentChange}
                  disabled={envEditDisabled}
                  id="environment"
                >
                  <option key="" value="" />
                  {_.map(ALL_ENVIRONMENTS, (env, i) => {
                    return <option key={i}>{env}</option>;
                  })}
                </Select>
              </FormGroup>
            </Layout>
          </Column>

          {subscriberGrant && (
            <>
              <Column cols={12}>
                <Layout padding={1}>
                  <FormGroup
                    label="Message Name"
                    id="messageName"
                    orientation={FormGroupOrientation.Horizontal}
                  >
                    <Select
                      value={messageName}
                      onChange={handleMessageNameChange}
                      disabled={mnEditDisabled}
                      id="messageName"
                    >
                      <option key="" value="" />
                      {_.map(authorizedFieldMessageNameOpts, (m, i) => {
                        return <option key={i}>{m}</option>;
                      })}
                    </Select>
                  </FormGroup>
                </Layout>
              </Column>

              <Column cols={12}>
                <Layout padding={1}>
                  <FormGroup
                    label="Field Name"
                    id="fieldName"
                    orientation={FormGroupOrientation.Horizontal}
                  >
                    <Select
                      value={fieldName}
                      onChange={handleFieldNameChange}
                      disabled={fnEditDisabled}
                      id="fieldName"
                    >
                      <option key="" value="" />
                      {_.map(authorizedFieldFieldNameOpts, (f, i) => {
                        return <option key={i}>{f}</option>;
                      })}
                    </Select>
                  </FormGroup>
                </Layout>
              </Column>
            </>
          )}

          <Column cols={12}>
            <CoreText>
              Don't see your event? Check the{' '}
              <CoreLink linkTo="https://git.xarth.tv/pages/eventbus/docs/schema/event_type_ownership/">
                eventbus documentation
              </CoreLink>{' '}
              for how to claim ownership of an event
            </CoreText>
          </Column>
          {submitError && (
            <Column cols={12}>
              <CoreText color={Color.Error}>{submitError}</CoreText>
            </Column>
          )}
          {eventRetrievalError && (
            <Column cols={12}>
              <CoreText color={Color.Error}>
                Error retrieving events - please retry. {eventRetrievalError}
              </CoreText>
            </Column>
          )}
          <Column cols={12}>
            <Layout
              padding={1}
              display={Display.Flex}
              justifyContent={JustifyContent.End}
            >
              <Button
                type={ButtonType.Success}
                onClick={submitGrant}
                disabled={submitDisabled!}
              >
                Grant
              </Button>
            </Layout>
          </Column>
        </Grid>
      )}
    </CustomModal>
  );
};
