import { Button, Column, Dialog, InputField, ModalContext } from '@8base/boost';
import { ApolloClient } from 'apollo-client';
import React from 'react';
import { compose, withApollo } from 'react-apollo';

import { getOperationName } from 'apollo-link';
import { ChatContext, IChatContext, withContext } from 'context';
import { DialogForm } from 'shared/components';
import {
  ChannelFormDocument,
  ChannelFormQuery,
  ChannelFormQueryVariables,
  ChannelUpdateDocument,
  ChannelUpdateMutation,
  ChannelUpdateMutationVariables,
  UserChannelsPreviewDocument,
} from 'shared/graphql/__generated__';

// -- TYPES

interface IEditChannelDialogProps {
  setChannel: IChatContext['setChannel'];
  client: ApolloClient<any>;
}

interface IDialogContentProps extends IEditChannelDialogProps {
  channelId: string;
}

interface IDialogContentState {
  data: {
    name: string;
    purpose: string;
  };
  errors: {
    [key in keyof IDialogContentState['data']]?: string;
  };
  submitting: boolean;
  loadingData: boolean;
}

// -- CONSTANTS

export const EDIT_CHANNEL_DIALOG_ID = 'EDIT_CHANNEL_DIALOG_ID';

// -- MAIN

class DialogContent extends React.Component<IDialogContentProps, IDialogContentState> {
  static contextType = ModalContext;

  constructor(props: IDialogContentProps) {
    super(props);

    this.state = {
      data: {
        name: '',
        purpose: '',
      },
      errors: {},
      submitting: false,
      loadingData: false,
    };
  }

  componentDidMount() {
    this.fetchChannel();
  }

  render() {
    const { data, errors, submitting, loadingData } = this.state;

    return (
      <DialogForm onSubmit={this.onSubmit}>
        <Dialog.Header title="Edit Channel" onClose={this.closeDialog} />

        <Dialog.Body scrollable={true}>
          {loadingData ? (
            <div>Loading...</div>
          ) : (
            <Column gap="md">
              <InputField
                label="Name"
                placeholder="Channel's name"
                input={{ name: 'name', value: data.name, onChange: this.onChange }}
                meta={{ error: errors.name, touched: true }}
                stretch={true}
              />

              <InputField
                label="Purpose (optional)"
                placeholder="Channel's purpose"
                input={{ name: 'purpose', value: data.purpose, onChange: this.onChange }}
                stretch={true}
              />
            </Column>
          )}
        </Dialog.Body>

        <Dialog.Footer>
          <Button color="primary" type="submit" loading={submitting}>
            Edit
          </Button>
        </Dialog.Footer>
      </DialogForm>
    );
  }

  private onSubmit = async (e: any) => {
    e.preventDefault();

    if (this.state.submitting) {
      return;
    }

    const errors = this.validateData(this.state.data);

    if (Object.keys(errors).length > 0) {
      this.setState({
        errors,
      });

      return;
    }

    const { name, purpose } = this.state.data;

    this.setState({
      submitting: true,
    });

    await this.props.client.mutate<ChannelUpdateMutation, ChannelUpdateMutationVariables>(
      {
        mutation: ChannelUpdateDocument,
        variables: {
          data: {
            id: this.props.channelId,
            name,
            purpose,
          },
        },
        refetchQueries: [getOperationName(UserChannelsPreviewDocument) as string],
      },
    );

    this.setState({
      submitting: false,
    });

    this.closeDialog();
  };

  private closeDialog = () => {
    this.context.closeModal(EDIT_CHANNEL_DIALOG_ID);
  };

  private onChange = (val: string, e: any) => {
    const targetName = e.target.name;

    this.setState(s => ({
      data: {
        ...s.data,
        [targetName]: val,
      },
      errors: {
        ...s.errors,
        [targetName]: undefined,
      },
    }));
  };

  private validateData(data: IDialogContentState['data']) {
    const errors: any = {};

    if (!data.name) {
      errors.name = 'Required';
    }

    return errors;
  }

  private async fetchChannel() {
    this.setState({
      loadingData: true,
    });

    const { data } = await this.props.client.query<
      ChannelFormQuery,
      ChannelFormQueryVariables
    >({
      query: ChannelFormDocument,
      variables: {
        id: this.props.channelId,
      },
      fetchPolicy: 'network-only',
    });

    const { name, purpose } = data.channel || ({} as NonNullable<typeof data.channel>);

    this.setState({
      data: {
        name: name || '',
        purpose: purpose || '',
      },
      loadingData: false,
    });
  }
}

function EditChannelDialog(props: IEditChannelDialogProps) {
  return (
    <Dialog id={EDIT_CHANNEL_DIALOG_ID} size="md">
      {({ args }: { args: { channelId: string } }) => (
        <DialogContent {...props} channelId={args.channelId} />
      )}
    </Dialog>
  );
}

EditChannelDialog.id = EDIT_CHANNEL_DIALOG_ID;

export default compose(
  withApollo,
  withContext({ context: ChatContext, keys: ['setChannel'] }),
)(EditChannelDialog);
