import * as React from 'react';
import { Helmet } from 'react-helmet';
import { ActionGroup, Button } from '@patternfly/react-core';

import { SecretModel, ConfigMapModel } from '../../models';
import { IdentityProvider, k8sCreate, K8sResourceKind, OAuthKind } from '../../module/k8s';
import { ButtonBar, ListInput, PromiseComponent, history } from '../utils';
import { addIDP, getOAuthResource, redirectToOAuthPage, mockNames } from './';
import { IDPNameInput } from './idp-name-input';
import { IDPCAFileInput } from './idp-cafile-input';

export class AddGitHubPage extends PromiseComponent<{}, AddGitHubPageState> {
  readonly state: AddGitHubPageState = {
    name: 'github',
    clientID: '',
    clientSecret: '',
    hostname: '',
    organizations: [],
    teams: [],
    caFileContent: '',
    inProgress: false,
    errorMessage: '',
  };

  getOAuthResource(): Promise<OAuthKind> {
    return this.handlePromise(getOAuthResource());
  }

  createClientSecret(): Promise<K8sResourceKind> {
    const { clientSecret } = this.state;
    const secret = {
      apiVersion: 'v1',
      kind: 'Secret',
      metadata: {
        generateName: 'github-client-secret-',
        namespace: 'openshift-config',
      },
      stringData: {
        clientSecret,
      },
    };

    return this.handlePromise(k8sCreate(SecretModel, secret));
  }

  createCAConfigMap(): Promise<K8sResourceKind> {
    const { caFileContent } = this.state;
    if (!caFileContent) {
      return Promise.resolve(null);
    }

    const ca = {
      apiVersion: 'v1',
      kind: 'ConfigMap',
      metadata: {
        generateName: 'github-ca-',
        namespace: 'openshift-config',
      },
      data: {
        'ca.crt': caFileContent,
      },
    };

    return this.handlePromise(k8sCreate(ConfigMapModel, ca));
  }

  addGitHubIDP(
    oauth: OAuthKind,
    clientSecretName: string,
    caName: string,
    dryRun?: boolean,
  ): Promise<K8sResourceKind> {
    const { name, clientID, hostname, organizations, teams } = this.state;
    const idp: IdentityProvider = {
      name,
      type: 'GitHub',
      mappingMethod: 'claim',
      github: {
        clientID,
        clientSecret: {
          name: clientSecretName,
        },
        hostname,
        organizations,
        teams,
      },
    };

    if (caName) {
      idp.github.ca = {
        name: caName,
      };
    }

    return this.handlePromise(addIDP(oauth, idp, dryRun));
  }

  submit: React.FormEventHandler<HTMLFormElement> = (e) => {
    e.preventDefault();
    if (this.state.organizations.length > 0 && this.state.teams.length > 0) {
      this.setState({ errorMessage: 'Specify either organizations or teams, but not both.' });
      return;
    }

    // Clear any previous errors.
    this.setState({ errorMessage: '' });
    this.getOAuthResource().then((oauth: OAuthKind) => {
      const mockCA = this.state.caFileContent ? mockNames.ca : '';
      this.addGitHubIDP(oauth, mockNames.secret, mockCA, true)
        .then(() => {
          const promises = [this.createClientSecret(), this.createCAConfigMap()];

          Promise.all(promises)
            .then(([secret, configMap]) => {
              const caName = configMap ? configMap.metadata.name : '';
              return this.addGitHubIDP(oauth, secret.metadata.name, caName);
            })
            .then(redirectToOAuthPage);
        })
        .catch((err) => {
          this.setState({ errorMessage: err });
        });
    });
  };

  nameChanged: React.ReactEventHandler<HTMLInputElement> = (event) => {
    this.setState({ name: event.currentTarget.value });
  };

  clientIDChanged: React.ReactEventHandler<HTMLInputElement> = (event) => {
    this.setState({ clientID: event.currentTarget.value });
  };

  clientSecretChanged: React.ReactEventHandler<HTMLInputElement> = (event) => {
    this.setState({ clientSecret: event.currentTarget.value });
  };

  hostnameChanged: React.ReactEventHandler<HTMLInputElement> = (event) => {
    this.setState({ hostname: event.currentTarget.value });
  };

  organizationsChanged = (organizations: string[]) => {
    this.setState({ organizations });
  };

  teamsChanged = (teams: string[]) => {
    this.setState({ teams });
  };

  caFileChanged = (caFileContent: string) => {
    this.setState({ caFileContent });
  };

  render() {
    const { name, clientID, clientSecret, hostname, caFileContent } = this.state;
    const title = 'Add Identity Provider: GitHub';
    return (
      <div className="co-m-pane__body">
        <Helmet>
          <title>{title}</title>
        </Helmet>
        <form onSubmit={this.submit} name="form" className="co-m-pane__body-group co-m-pane__form">
          <h1 className="co-m-pane__heading">{title}</h1>
          <p className="co-m-pane__explanation">
            You can use the GitHub integration to connect to either GitHub or GitHub Enterprise. For
            GitHub Enterprise, you must provide the hostname of your instance and can optionally
            provide a CA certificate bundle to use in requests to the server.
          </p>
          <IDPNameInput value={name} onChange={this.nameChanged} />
          <div className="form-group">
            <label className="control-label co-required" htmlFor="client-id">
              Client ID
            </label>
            <input
              className="pf-c-form-control"
              type="text"
              onChange={this.clientIDChanged}
              value={clientID}
              id="client-id"
              required
            />
          </div>
          <div className="form-group">
            <label className="control-label co-required" htmlFor="client-secret">
              Client Secret
            </label>
            <input
              className="pf-c-form-control"
              type="password"
              onChange={this.clientSecretChanged}
              value={clientSecret}
              id="client-secret"
              required
            />
          </div>
          <div className="form-group">
            <label className="control-label" htmlFor="hostname">
              Hostname
            </label>
            <input
              className="pf-c-form-control"
              type="text"
              onChange={this.hostnameChanged}
              value={hostname}
              id="hostname"
              aria-describedby="idp-hostname-help"
            />
            <p className="help-block" id="idp-hostname-help">
              Optional domain for use with a hosted instance of GitHub Enterprise.
            </p>
          </div>
          <IDPCAFileInput value={caFileContent} onChange={this.caFileChanged} />
          <div className="co-form-section__separator" />
          <h3>Organizations</h3>
          <p className="co-help-text">
            Optionally list organizations. If specified, only GitHub users that are members of at
            least one of the listed organizations will be allowed to log in. Cannot be used in
            combination with <strong>teams</strong>.
          </p>
          <ListInput
            label="Organization"
            onChange={this.organizationsChanged}
            helpText="Restricts which organizations are allowed to log in."
          />
          <div className="co-form-section__separator" />
          <h3>Teams</h3>
          <p className="co-help-text">
            Optionally list teams. If specified, only GitHub users that are members of at least one
            of the listed teams will be allowed to log in. Cannot be used in combination with{' '}
            <strong>organizations</strong>.
          </p>
          <ListInput
            label="Team"
            onChange={this.teamsChanged}
            helpText="Restricts which teams are allowed to log in. The format is <org>/<team>."
          />
          <ButtonBar errorMessage={this.state.errorMessage} inProgress={this.state.inProgress}>
            <ActionGroup className="pf-c-form">
              <Button type="submit" variant="primary" data-test-id="add-idp">
                Add
              </Button>
              <Button type="button" variant="secondary" onClick={history.goBack}>
                Cancel
              </Button>
            </ActionGroup>
          </ButtonBar>
        </form>
      </div>
    );
  }
}

export type AddGitHubPageState = {
  name: string;
  clientID: string;
  clientSecret: string;
  hostname: string;
  organizations: string[];
  teams: string[];
  caFileContent: string;
  inProgress: boolean;
  errorMessage: string;
};
