import React, { useEffect } from 'react';
import { getFormErrorLabelsProps, FormErrorLabelsProps } from './errorHandling';
import * as Yup from 'yup';
import { withProposalFormData,
  ProposalFormExportProps,
  ProposalFormContainerProps,
  ProposalFormInnerProps } from './GenericProposalForm';
import { GenericWorkingGroupProposalForm,
  FormValues as WGFormValues,
  defaultValues as wgFromDefaultValues } from './GenericWorkingGroupProposalForm';
import { FormField, InputFormField, TextareaFormField } from './FormFields';
import { withFormContainer } from './FormContainer';
import { ActivateOpeningAtKey, ActivateOpeningAtDef, StakingAmountLimitModeKeys, IApplicationRationingPolicy, IStakingPolicy } from '@joystream/types/hiring';
import { GenericJoyStreamRoleSchema } from '@joystream/types/hiring/schemas/role.schema.typings';
import { Dropdown, Grid, Message, Checkbox } from 'semantic-ui-react';
import { formatBalance } from '@polkadot/util';
import _ from 'lodash';
import { IWorkingGroupOpeningPolicyCommitment } from '@joystream/types/working-group';
import { IAddOpeningParameters } from '@joystream/types/proposals';
import { WorkingGroupKey, InputValidationLengthConstraint } from '@joystream/types/common';
import { BlockNumber } from '@polkadot/types/interfaces';
import { withCalls } from '@polkadot/react-api';
import { SimplifiedTypeInterface } from '@polkadot/joy-utils/types/common';
import Validation from '../validationSchema';

export type FormValues = WGFormValues & {
  activateAt: ActivateOpeningAtKey;
  activateAtBlock: string;
  maxReviewPeriodLength: string;
  applicationsLimited: boolean;
  maxApplications: string;
  applicationStakeRequired: boolean;
  applicationStakeMode: StakingAmountLimitModeKeys;
  applicationStakeValue: string;
  roleStakeRequired: boolean;
  roleStakeMode: StakingAmountLimitModeKeys;
  roleStakeValue: string;
  terminateRoleUnstakingPeriod: string;
  leaveRoleUnstakingPeriod: string;
  humanReadableText: string;
};

const defaultValues: FormValues = {
  ...wgFromDefaultValues,
  activateAt: 'CurrentBlock',
  activateAtBlock: '',
  maxReviewPeriodLength: (14400 * 30).toString(), // 30 days
  applicationsLimited: false,
  maxApplications: '',
  applicationStakeRequired: false,
  applicationStakeMode: StakingAmountLimitModeKeys.Exact,
  applicationStakeValue: '',
  roleStakeRequired: false,
  roleStakeMode: StakingAmountLimitModeKeys.Exact,
  roleStakeValue: '',
  terminateRoleUnstakingPeriod: (14400 * 7).toString(), // 7 days
  leaveRoleUnstakingPeriod: (14400 * 7).toString(), // 7 days
  humanReadableText: ''
};

const HRTDefault: (memberHandle: string, group: WorkingGroupKey) => GenericJoyStreamRoleSchema =
  (memberHandle, group) => ({
    version: 1,
    headline: `Looking for ${group} Working Group Leader!`,
    job: {
      title: `${group} Working Group Leader`,
      description: `Become ${group} Working Group Leader! This is a great opportunity to support Joystream!`
    },
    application: {
      sections: [
        {
          title: 'About you',
          questions: [
            {
              title: 'Your name',
              type: 'text'
            },
            {
              title: 'What makes you a good fit for the job?',
              type: 'text area'
            }
          ]
        }
      ]
    },
    reward: '100 JOY per block',
    creator: {
      membership: {
        handle: memberHandle
      }
    }
  });

type FormAdditionalProps = Record<any, never>; // Aditional props coming all the way from export component into the inner form.
type ExportComponentProps = ProposalFormExportProps<FormAdditionalProps, FormValues>;
type FormContainerProps = ProposalFormContainerProps<ExportComponentProps> & {
  currentBlock?: BlockNumber;
  HRTConstraint?: InputValidationLengthConstraint;
};
type FormInnerProps = ProposalFormInnerProps<FormContainerProps, FormValues>;

type StakeFieldsProps = Pick<FormInnerProps, 'values' | 'handleChange' | 'setFieldValue'> & {
  errorLabelsProps: FormErrorLabelsProps<FormValues>;
  stakeType: 'role' | 'application';
};

const StakeFields: React.FunctionComponent<StakeFieldsProps> = ({
  values,
  errorLabelsProps,
  handleChange,
  stakeType,
  setFieldValue
}) => {
  return (
    <>
      <FormField label={`${_.startCase(stakeType)} stake` }>
        <Checkbox
          toggle
          onChange={(e, data) => { setFieldValue(`${stakeType}StakeRequired`, data.checked); }}
          label={ `Require ${stakeType} stake` }
          checked={ stakeType === 'role' ? values.roleStakeRequired : values.applicationStakeRequired }/>
      </FormField>
      { (stakeType === 'role' ? values.roleStakeRequired : values.applicationStakeRequired) && (<>
        <FormField label='Stake mode'>
          <Dropdown
            onChange={handleChange}
            name={ `${stakeType}StakeMode` }
            selection
            options={[StakingAmountLimitModeKeys.Exact, StakingAmountLimitModeKeys.AtLeast].map((mode) => ({ text: mode, value: mode }))}
            value={ stakeType === 'role' ? values.roleStakeMode : values.applicationStakeMode }
          />
        </FormField>
        <InputFormField
          label='Stake value'
          unit={formatBalance.getDefaults().unit}
          onChange={handleChange}
          name={ `${stakeType}StakeValue` }
          error={ stakeType === 'role' ? errorLabelsProps.roleStakeValue : errorLabelsProps.applicationStakeValue}
          value={ stakeType === 'role' ? values.roleStakeValue : values.applicationStakeValue}
          placeholder={'ie. 100'}
        />
      </>) }
    </>
  );
};

const valuesToAddOpeningParams = (values: FormValues): SimplifiedTypeInterface<IAddOpeningParameters> => {
  const commitment: SimplifiedTypeInterface<IWorkingGroupOpeningPolicyCommitment> = {
    max_review_period_length: parseInt(values.maxReviewPeriodLength)
  };

  if (parseInt(values.terminateRoleUnstakingPeriod) > 0) {
    commitment.terminate_role_stake_unstaking_period = parseInt(values.terminateRoleUnstakingPeriod);
  }

  if (parseInt(values.leaveRoleUnstakingPeriod) > 0) {
    commitment.exit_role_stake_unstaking_period = parseInt(values.leaveRoleUnstakingPeriod);
  }

  if (values.applicationsLimited) {
    const rationingPolicy: SimplifiedTypeInterface<IApplicationRationingPolicy> = {
      max_active_applicants: parseInt(values.maxApplications)
    };

    commitment.application_rationing_policy = rationingPolicy;
  }

  if (values.applicationStakeRequired) {
    const applicationStakingPolicy: SimplifiedTypeInterface<IStakingPolicy> = {
      amount: parseInt(values.applicationStakeValue),
      amount_mode: values.applicationStakeMode
    };

    commitment.application_staking_policy = applicationStakingPolicy;
  }

  if (values.roleStakeRequired) {
    const roleStakingPolicy: SimplifiedTypeInterface<IStakingPolicy> = {
      amount: parseInt(values.roleStakeValue),
      amount_mode: values.roleStakeMode
    };

    commitment.role_staking_policy = roleStakingPolicy;
  }

  return {
    activate_at: { [values.activateAt]: values.activateAt === 'ExactBlock' ? parseInt(values.activateAtBlock) : null },
    commitment: commitment,
    human_readable_text: values.humanReadableText,
    working_group: values.workingGroup
  };
};

const AddWorkingGroupOpeningForm: React.FunctionComponent<FormInnerProps> = (props) => {
  const { handleChange, errors, touched, values, setFieldValue, myMembership } = props;

  useEffect(() => {
    if (myMembership && !touched.humanReadableText) {
      setFieldValue(
        'humanReadableText',
        JSON.stringify(HRTDefault(myMembership.handle.toString(), values.workingGroup), undefined, 4)
      );
    }
    // eslint-disable-next-line react-hooks/exhaustive-deps
  }, [values.workingGroup, myMembership, touched.humanReadableText]);
  const errorLabelsProps = getFormErrorLabelsProps<FormValues>(errors, touched);

  return (
    <GenericWorkingGroupProposalForm
      {...props}
      txMethod='createAddWorkingGroupLeaderOpeningProposal'
      proposalType='AddWorkingGroupLeaderOpening'
      submitParams={[valuesToAddOpeningParams(values)]}
    >
      <Grid columns='4' doubling stackable verticalAlign='bottom'>
        <Grid.Row>
          <Grid.Column>
            <FormField label='Activate opening at'>
              <Dropdown
                onChange={handleChange}
                name='activateAt'
                selection
                options={Object.keys(ActivateOpeningAtDef).map((wgKey) => ({ text: wgKey, value: wgKey }))}
                value={values.activateAt}
              />
            </FormField>
          </Grid.Column>
          <Grid.Column>
            { values.activateAt === 'ExactBlock' && (
              <InputFormField
                onChange={handleChange}
                name='activateAtBlock'
                error={errorLabelsProps.activateAtBlock}
                value={values.activateAtBlock}
                placeholder={'Provide the block number'}
              />
            ) }
          </Grid.Column>
        </Grid.Row>
      </Grid>
      { values.activateAt === 'ExactBlock' && (
        <Message info>
          In case <b>ExactBlock</b> is specified, the opening will remain in <i>Waiting to Begin</i> stage (which means it will be visible,
          but no applicants will be able to apply yet) until current block number will equal the specified number.
        </Message>
      ) }
      <Grid columns='4' doubling stackable verticalAlign='bottom'>
        <Grid.Row>
          <Grid.Column>
            <InputFormField
              label='Max. review period'
              onChange={handleChange}
              name='maxReviewPeriodLength'
              error={errorLabelsProps.maxReviewPeriodLength}
              value={values.maxReviewPeriodLength}
              placeholder={'ie. 72000'}
              unit='blocks'
            />
          </Grid.Column>
        </Grid.Row>
      </Grid>
      <Grid columns='4' doubling stackable verticalAlign='bottom'>
        <Grid.Row>
          <Grid.Column>
            <FormField label='Applications limit'>
              <Checkbox
                toggle
                onChange={(e, data) => { setFieldValue('applicationsLimited', data.checked); }}
                label='Limit applications'
                checked={values.applicationsLimited}/>
            </FormField>
            { values.applicationsLimited && (
              <InputFormField
                onChange={handleChange}
                name='maxApplications'
                error={errorLabelsProps.maxApplications}
                value={values.maxApplications}
                placeholder={'Max. number of applications'}
              />
            ) }
          </Grid.Column>
        </Grid.Row>
      </Grid>
      <Grid columns='2' stackable style={{ marginBottom: 0 }}>
        <Grid.Row>
          <Grid.Column>
            <StakeFields stakeType='application' {...{ errorLabelsProps, values, handleChange, setFieldValue }}/>
          </Grid.Column>
          <Grid.Column>
            <StakeFields stakeType='role' {...{ errorLabelsProps, values, handleChange, setFieldValue }}/>
          </Grid.Column>
        </Grid.Row>
      </Grid>
      <Grid columns='2' stackable style={{ marginBottom: 0 }}>
        <Grid.Row>
          <Grid.Column>
            <InputFormField
              onChange={handleChange}
              name='terminateRoleUnstakingPeriod'
              error={errorLabelsProps.terminateRoleUnstakingPeriod}
              value={values.terminateRoleUnstakingPeriod}
              label={'Terminate role unstaking period'}
              placeholder={'ie. 14400'}
              unit='blocks'
              help={
                'In case leader role or application is terminated - this will be the unstaking period for the role stake (in blocks).'
              }
            />
          </Grid.Column>
          <Grid.Column>
            <InputFormField
              onChange={handleChange}
              name='leaveRoleUnstakingPeriod'
              error={errorLabelsProps.leaveRoleUnstakingPeriod}
              value={values.leaveRoleUnstakingPeriod}
              label={'Leave role unstaking period'}
              placeholder={'ie. 14400'}
              unit='blocks'
              help={
                'In case leader leaves/exits his role - this will be the unstaking period for his role stake (in blocks). ' +
                'It also applies when user is withdrawing an active leader application.'
              }
            />
          </Grid.Column>
        </Grid.Row>
      </Grid>
      <TextareaFormField
        label='Opening schema (human_readable_text)'
        help='JSON schema that describes some characteristics of the opening presented in the UI (headers, content, application form etc.)'
        onChange={handleChange}
        name='humanReadableText'
        placeholder='Paste the JSON schema here...'
        error={errorLabelsProps.humanReadableText}
        value={values.humanReadableText}
        rows={20}
      />
    </GenericWorkingGroupProposalForm>
  );
};

const FormContainer = withFormContainer<FormContainerProps, FormValues>({
  mapPropsToValues: (props: FormContainerProps) => ({
    ...defaultValues,
    ...(props.initialData || {})
  }),
  validationSchema: (props: FormContainerProps) => Yup.object().shape({
    ...Validation.All(),
    ...Validation.AddWorkingGroupLeaderOpening(
      props.currentBlock?.toNumber() || 0,
      props.HRTConstraint
    )
  }),
  handleSubmit: () => null,
  displayName: 'AddWorkingGroupOpeningForm'
})(AddWorkingGroupOpeningForm);

export default withCalls<ExportComponentProps>(
  ['derive.chain.bestNumber', { propName: 'currentBlock' }],
  ['query.storageWorkingGroup.openingHumanReadableText', { propName: 'HRTConstraint' }]
)(
  withProposalFormData<FormContainerProps, ExportComponentProps>(FormContainer)
);
