import React from 'react';
import {
  Form,
  FormGroup,
  TextInput,
  Split,
  SplitItem,
  Popover,
  DatePicker,
  Title,
  Spinner,
  EmptyState,
  EmptyStateIcon,
  EmptyStateBody,
  Button,
  Modal,
  Wizard,
  WizardContextConsumer,
} from '@patternfly/react-core';
import HelpIcon from '@patternfly/react-icons/dist/js/icons/help-icon';
import ExclamationCircleIcon from '@patternfly/react-icons/dist/js/icons/exclamation-circle-icon';
import { capitalize } from '@patternfly/react-core/dist/esm/helpers/util';
import { isValidDate } from '@patternfly/react-core/dist/esm/components/CalendarMonth';
import { useDispatch } from 'react-redux';
import { addNotification } from '@redhat-cloud-services/frontend-components-notifications/redux';
import MUARolesTable from './MUARolesTable';
import PropTypes from 'prop-types';
import apiInstance from '../Helpers/apiInstance';

const nameHelperText =
  'Customers will be able to see this information as part of your request';
const helperTexts = {
  'first name': nameHelperText,
  'last name': nameHelperText,
  'account number':
    'This is the account number that you would like to receive read access to',
  'access duration':
    'This is the time frame you would like to be granted read access to accounts',
  'organization id':
    'This is the org id of the account that you would like to receive read access to',
};
const invalidAccountTitle = 'Invalid Account number';
const getLabelIcon = (field) => (
  <Popover bodyContent={<p>{helperTexts[field]}</p>}>
    <button
      type="button"
      aria-label={`More info for ${field}`}
      onClick={(e) => e.preventDefault()}
      aria-describedby="form-name"
      className="pf-c-form__group-label-help"
    >
      <HelpIcon noVerticalAlign />
    </button>
  </Popover>
);

const today = new Date();
today.setDate(today.getDate() - 1);
const maxStartDate = new Date();
maxStartDate.setDate(maxStartDate.getDate() + 60);
const dateFormat = (date) =>
  date.toLocaleDateString('en-US', {
    year: 'numeric',
    month: '2-digit',
    day: '2-digit',
  });
const dateParse = (date) => {
  const split = date.split('/');
  if (split.length !== 3) {
    return new Date();
  }
  let month = split[0].padStart(2, '0');
  let day = split[1].padStart(2, '0');
  let year = split[2].padStart(4, '0');
  return new Date(`${year}-${month}-${day}T00:00:00`);
};
const RequestDetailsForm = ({
  user = {},
  targetAccount,
  setTargetAccount,
  targetOrg,
  setTargetOrg,
  start,
  setStart,
  end,
  setEnd,
  disableAccount,
  disableOrgId,
  isLoading,
  error,
}) => {
  let [startDate, setStartDate] = React.useState();
  const [validatedAccount, setValidatedAccount] = React.useState(
    error ? 'error' : 'default'
  );

  const [validatedOrgId, setValidatedOrgId] = React.useState(
    error ? 'error' : 'default'
  );

  // https://github.com/RedHatInsights/insights-rbac/blob/master/rbac/api/cross_access/model.py#L49
  const startValidator = (date) => {
    if (isValidDate(date)) {
      if (date < today) {
        setEnd('');
        return 'Start date must be today or later';
      }
      if (date > maxStartDate) {
        setEnd('');
        return 'Start date must be within 60 days of today';
      }
    }

    return '';
  };

  const endValidator = (date) => {
    if (isValidDate(startDate)) {
      if (startDate > date) {
        return 'End date must be after from date';
      }
    }

    const maxToDate = new Date(startDate);
    maxToDate.setFullYear(maxToDate.getFullYear() + 1);
    if (date > maxToDate) {
      return 'Access duration may not be longer than one year';
    }

    return '';
  };

  const onStartChange = (str, date) => {
    setStartDate(new Date(date));
    setStart(str);
    if (isValidDate(date) && !startValidator(date)) {
      date.setDate(date.getDate() + 7);
      setEnd(dateFormat(date));
    } else {
      setEnd('');
    }
  };

  const onEndChange = (str, date) => {
    if (endValidator(date)) {
      setEnd('');
    } else {
      setEnd(str);
    }
  };

  return (
    <Form onSubmit={(ev) => ev.preventDefault()} isDisabled={isLoading}>
      <Title headingLevel="h2">Request details</Title>
      <Split hasGutter>
        <SplitItem isFilled>
          <FormGroup label="First name" labelIcon={getLabelIcon('first name')}>
            <TextInput id="first-name" value={user.first_name} isDisabled />
          </FormGroup>
        </SplitItem>
        <SplitItem isFilled>
          <FormGroup label="Last name" labelIcon={getLabelIcon('last name')}>
            <TextInput id="last-name" value={user.last_name} isDisabled />
          </FormGroup>
        </SplitItem>
      </Split>
      <FormGroup
        label="Account number"
        isRequired
        labelIcon={getLabelIcon('account number')}
        helperText="Enter the account number you would like access to"
        helperTextInvalid="Please enter a valid account number"
        validated={validatedAccount}
      >
        <TextInput
          id="account-number"
          value={targetAccount}
          onChange={(val) => {
            setTargetAccount(val);
            setValidatedAccount('default');
          }}
          isRequired
          placeholder="Example, 8675309"
          validated={validatedAccount}
          isDisabled={disableAccount}
        />
      </FormGroup>
      <FormGroup
        label="Organization id"
        isRequired
        labelIcon={getLabelIcon('organization id')}
        helperText="Enter the organization id you would like access to"
        helperTextInvalid="Please enter a valid organization id"
        validated={validatedOrgId}
      >
        <TextInput
          id="org-id"
          value={targetOrg}
          onChange={(val) => {
            setTargetOrg(val);
            setValidatedOrgId('default');
          }}
          isRequired
          placeholder="Example, 1234567"
          validated={validatedOrgId}
          isDisabled={disableOrgId}
        />
      </FormGroup>
      <FormGroup
        label="Access duration"
        isRequired
        labelIcon={getLabelIcon('access duration')}
      >
        <Split>
          <SplitItem>
            <DatePicker
              width="300px"
              aria-label="Start date"
              value={start}
              dateFormat={dateFormat}
              dateParse={dateParse}
              placeholder="mm/dd/yyyy"
              onChange={onStartChange}
              validators={[startValidator]}
            />
          </SplitItem>
          <SplitItem style={{ padding: '6px 12px 0 12px' }}>to</SplitItem>
          <SplitItem>
            <DatePicker
              width="300px"
              aria-label="End date"
              value={end}
              dateFormat={dateFormat}
              dateParse={dateParse}
              placeholder="mm/dd/yyyy"
              onChange={onEndChange}
              validators={[endValidator]}
              rangeStart={start}
            />
          </SplitItem>
        </Split>
      </FormGroup>
    </Form>
  );
};

RequestDetailsForm.propTypes = {
  user: PropTypes.any,
  targetAccount: PropTypes.any,
  setTargetAccount: PropTypes.any,
  targetOrg: PropTypes.any,
  setTargetOrg: PropTypes.any,
  start: PropTypes.any,
  setStart: PropTypes.any,
  end: PropTypes.any,
  setEnd: PropTypes.any,
  disableAccount: PropTypes.any,
  disableOrgId: PropTypes.any,
  isLoading: PropTypes.any,
  error: PropTypes.any,
};

// Can't use CSS with @redhat-cloud-services/frontend-components-config because it's scoped to <main> content
// rather than Modal content...
const spaceUnderStyle = { paddingBottom: '16px' };
const ReviewStep = ({
  targetAccount,
  start,
  end,
  roles,
  isLoading,
  error,
  onClose,
}) => {
  let content = null;
  if (isLoading) {
    content = (
      <EmptyState>
        <EmptyStateIcon icon={() => <Spinner size="lg" />} />
        <Title headingLevel="h2" size="lg">
          Submitting access request
        </Title>
        <Button variant="link" onClick={onClose}>
          Close
        </Button>
      </EmptyState>
    );
  } else if (error) {
    const context = React.useContext(WizardContextConsumer);
    content = (
      <EmptyState>
        <EmptyStateIcon icon={ExclamationCircleIcon} color="#C9190B" />
        <Title headingLevel="h2" size="lg">
          {error.title}
        </Title>
        <EmptyStateBody>{error.description}</EmptyStateBody>
        {error.title === invalidAccountTitle && (
          <Button variant="primary" onClick={() => context.goToStepById(1)}>
            Return to Step 1
          </Button>
        )}
      </EmptyState>
    );
  } else {
    content = (
      <React.Fragment>
        <Title headingLevel="h2" style={spaceUnderStyle}>
          Review details
        </Title>
        <table>
          <tr>
            <td style={spaceUnderStyle}>
              <b>Account number</b>
            </td>
            <td style={spaceUnderStyle}>{targetAccount}</td>
          </tr>
          <tr>
            <td style={{ paddingRight: '32px' }}>
              <b>Access duration</b>
            </td>
            <td></td>
          </tr>
          <tr>
            <td>From</td>
            <td>{start}</td>
          </tr>
          <tr>
            <td style={spaceUnderStyle}>To</td>
            <td style={spaceUnderStyle}>{end}</td>
          </tr>
          <tr>
            <td>
              <b>Roles</b>
            </td>
            <td>{roles[0]}</td>
          </tr>
          {roles.slice(1).map((role) => (
            <tr key={role}>
              <td></td>
              <td>{role}</td>
            </tr>
          ))}
        </table>
      </React.Fragment>
    );
  }

  return <React.Fragment>{content}</React.Fragment>;
};

ReviewStep.propTypes = {
  targetAccount: PropTypes.any,
  start: PropTypes.any,
  end: PropTypes.any,
  roles: PropTypes.any,
  isLoading: PropTypes.any,
  error: PropTypes.any,
  onClose: PropTypes.any,
};

const EditRequestModal = ({ requestId, variant, onClose }) => {
  const isEdit = variant === 'edit';
  const [isLoading, setIsLoading] = React.useState(true);
  const [error, setError] = React.useState();
  const [user, setUser] = React.useState();
  const [targetAccount, setTargetAccount] = React.useState();
  const [targetOrg, setTargetOrg] = React.useState();
  const [start, setStart] = React.useState();
  const [end, setEnd] = React.useState();
  const [roles, setRoles] = React.useState([]);
  const [warnClose, setWarnClose] = React.useState(false);
  const [step, setStep] = React.useState(1);
  const dispatch = useDispatch();
  const isDirty = Boolean(targetAccount || start || end || roles.length > 0);

  // We need to be logged in (and see the username) which is an async request.
  // If we're editing we also need to fetch the roles
  React.useEffect(() => {
    const userPromise = window.insights.chrome.auth.getUser();
    const detailsPromise = isEdit
      ? apiInstance.get(
          `${API_BASE}/cross-account-requests/${requestId}/?query_by=user_id`,
          {
            headers: { Accept: 'application/json' },
          }
        )
      : new Promise((res) => res(true));

    Promise.all([userPromise, detailsPromise])
      .then(([user, details]) => {
        if (user && user.identity && user.identity.user) {
          setUser(user.identity.user);
        } else {
          throw Error("Couldn't get current user. Make sure you're logged in");
        }
        if (isEdit) {
          if (details.errors) {
            throw Error(details.errors.map((e) => e.detail).join('\n'));
          }
          if (details && details.target_account) {
            setTargetAccount(details.target_account);
            setStart(details.start_date);
            setEnd(details.end_date);
            setRoles(details.roles.map((role) => role.display_name));
            setTargetOrg(details.target_org);
          } else {
            throw Error(`Could not fetch details for request ${requestId}`);
          }
        }
        setIsLoading(false);
      })
      .catch((err) => {
        dispatch(
          addNotification({
            variant: 'danger',
            title: 'Could not load access request',
            description: err.message,
          })
        );
      });
  }, []);

  const onSave = () => {
    setIsLoading(true);
    // https://cloud.redhat.com/docs/api-docs/rbac#operations-CrossAccountRequest-createCrossAccountRequests
    const body = {
      target_account: targetAccount,
      start_date: start,
      end_date: end,
      target_org: targetOrg,
      roles,
    };
    apiInstance[isEdit ? 'put' : 'post'](
      `${API_BASE}/cross-account-requests/${isEdit ? `/${requestId}/` : ''}`,
      body,
      {
        headers: {
          'Content-Type': 'application/json',
          Accept: 'application/json',
        },
      }
    )
      .then((res) => {
        if (res.errors && res.errors.length > 0) {
          throw Error(res.errors[0].detail);
        }
        dispatch(
          addNotification({
            variant: 'success',
            title: `${isEdit ? 'Edited' : 'Created'} access request`,
            description: res.request_id,
          })
        );
        onClose(true);
      })
      .catch((err) => {
        const isInvalidAccount = /Account .* does not exist/.test(err.message);
        setError({
          title: isInvalidAccount
            ? invalidAccountTitle
            : `Could not ${variant} access request`,
          description: isInvalidAccount
            ? 'Please return to Step 1: Request details and input a new account number for your request.'
            : err.message,
        });
        setIsLoading(false);
      });
  };

  const step1Complete = [targetAccount, start, end].every(Boolean);
  const step2Complete = roles.length > 0;
  const steps = [
    {
      id: 1,
      name: 'Request details',
      component: (
        <RequestDetailsForm
          user={user}
          targetAccount={targetAccount}
          setTargetAccount={setTargetAccount}
          targetOrg={targetOrg}
          setTargetOrg={setTargetOrg}
          start={start}
          setStart={setStart}
          end={end}
          setEnd={setEnd}
          disableAccount={isEdit}
          disableOrgId={isEdit}
          isLoading={isLoading}
          error={error}
        />
      ),
      enableNext: step1Complete,
    },
    {
      id: 2,
      name: 'Select roles',
      component: <MUARolesTable roles={roles} setRoles={setRoles} />,
      enableNext: step2Complete,
      canJumpTo: step1Complete,
    },
    {
      id: 3,
      name: 'Review details',
      component: (
        <ReviewStep
          targetAccount={targetAccount}
          start={start}
          end={end}
          roles={roles}
          isLoading={isLoading}
          error={error}
          setError={setError}
          onClose={() => onClose(false)}
        />
      ),
      canJumpTo: step1Complete && step2Complete,
      enableNext: !isLoading,
      nextButtonText: 'Finish',
    },
  ];

  const titleId = `${variant}-request`;
  const descriptionId = `${variant} request`;
  return (
    <React.Fragment>
      <Modal
        variant="large"
        style={{ height: '900px' }}
        showClose={false}
        hasNoBodyWrapper
        isOpen={!warnClose}
        aria-describedby={descriptionId}
        aria-labelledby={titleId}
      >
        <Wizard
          titleId={titleId}
          descriptionId={descriptionId}
          title={capitalize(variant) + ' request'}
          steps={steps}
          onClose={() => (isDirty ? setWarnClose(true) : onClose(false))}
          onSave={onSave}
          startAtStep={step}
          onNext={({ id }) => {
            setError();
            setStep(id);
          }}
          onBack={({ id }) => {
            setError();
            setStep(id);
          }}
          onGoToStep={({ id }) => {
            setError();
            setStep(id);
          }}
        />
      </Modal>
      {warnClose && (
        <Modal
          title="Exit request creation?"
          variant="small"
          titleIconVariant="warning"
          isOpen
          onClose={() => setWarnClose(false)}
          actions={[
            <Button
              key="confirm"
              variant="primary"
              onClick={() => onClose(false)}
            >
              Exit
            </Button>,
            <Button
              key="cancel"
              variant="link"
              onClick={() => setWarnClose(false)}
            >
              Stay
            </Button>,
          ]}
        >
          All inputs will be discarded.
        </Modal>
      )}
    </React.Fragment>
  );
};

EditRequestModal.propTypes = {
  requestId: PropTypes.string,
  variant: PropTypes.any,
  onClose: PropTypes.func,
};

export default EditRequestModal;