import React from 'react';
import PropTypes from 'prop-types';

import Panel from '../atom/panel';
import InitDialog from '../views/init-dialog';
import CloneDialog from '../views/clone-dialog';
import CredentialDialog from '../views/credential-dialog';
import OpenIssueishDialog from '../views/open-issueish-dialog';
import OpenCommitDialog from '../views/open-commit-dialog';

export default class DialogsController extends React.Component {
  static propTypes = {
    // Model
    request: PropTypes.shape({
      identifier: PropTypes.string.isRequired,
      isProgressing: PropTypes.bool.isRequired,
    }).isRequired,

    // Atom environment
    workspace: PropTypes.object.isRequired,
    commands: PropTypes.object.isRequired,
    config: PropTypes.object.isRequired,
  };

  constructor(props) {
    super(props);

    this.dialogRenderFns = {
      null: () => null,
      init: this.renderInitDialog,
      clone: this.renderCloneDialog,
      credential: this.renderCredentialDialog,
      issueish: this.renderIssueishDialog,
      commit: this.renderCommitDialog,
    };

    this.state = {
      requestInProgress: null,
      requestError: [null, null],
    };
  }

  render() {
    return this.dialogRenderFns[this.props.request.identifier]();
  }

  renderInitDialog = () => (
    <Panel workspace={this.props.workspace} location="modal">
      <InitDialog {...this.getCommonProps()} />
    </Panel>
  )

  renderCloneDialog = () => (
    <Panel workspace={this.props.workspace} location="modal">
      <CloneDialog config={this.props.config} {...this.getCommonProps()} />
    </Panel>
  )

  renderCredentialDialog = () => (
    <Panel workspace={this.props.workspace} location="modal">
      <CredentialDialog {...this.getCommonProps()} />
    </Panel>
  )

  renderIssueishDialog = () => (
    <Panel workspace={this.props.workspace} location="modal">
      <OpenIssueishDialog {...this.getCommonProps()} />
    </Panel>
  )

  renderCommitDialog = () => (
    <Panel workspace={this.props.workspace} location="modal">
      <OpenCommitDialog {...this.getCommonProps()} />
    </Panel>
  )

  getCommonProps() {
    const {request} = this.props;
    const accept = request.isProgressing
      ? async (...args) => {
        this.setState({requestInProgress: request});
        try {
          return await request.accept(...args);
        } catch (error) {
          this.setState({requestError: [request, error]});
          return undefined;
        } finally {
          this.setState({requestInProgress: null});
        }
      } : (...args) => {
        try {
          return request.accept(...args);
        } catch (error) {
          this.setState({requestError: [request, error]});
          return undefined;
        }
      };
    const wrapped = wrapDialogRequest(request, {accept});

    return {
      commands: this.props.commands,
      inProgress: this.state.requestInProgress === request,
      error: this.state.requestError[0] === request ? this.state.requestError[1] : null,
      request: wrapped,
    };
  }
}

class DialogRequest {
  constructor(identifier, params = {}) {
    this.identifier = identifier;
    this.params = params;
    this.isProgressing = false;
    this.accept = () => {};
    this.cancel = () => {};
  }

  onAccept(cb) {
    this.accept = cb;
  }

  onProgressingAccept(cb) {
    this.isProgressing = true;
    this.onAccept(cb);
  }

  onCancel(cb) {
    this.cancel = cb;
  }

  getParams() {
    return this.params;
  }
}

function wrapDialogRequest(original, {accept}) {
  const dup = new DialogRequest(original.identifier, original.getParams());
  dup.isProgressing = original.isProgressing;
  dup.onAccept(accept);
  dup.onCancel(original.cancel);
  return dup;
}

export const dialogRequests = {
  null: {
    identifier: 'null',
    isProgressing: false,
    params: {},
    accept: () => {},
    cancel: () => {},
  },

  init({dirPath}) {
    return new DialogRequest('init', {dirPath});
  },

  clone(opts) {
    return new DialogRequest('clone', {
      sourceURL: '',
      destPath: '',
      ...opts,
    });
  },

  credential(opts) {
    return new DialogRequest('credential', {
      includeUsername: false,
      includeRemember: false,
      prompt: 'Please authenticate',
      ...opts,
    });
  },

  issueish() {
    return new DialogRequest('issueish');
  },

  commit() {
    return new DialogRequest('commit');
  },
};

@@ -2247,32 +2247,60 @@
  this.setState(%7B
+requestError: %5Bnull, null%5D, 
 requestInProgres
@@ -2337,22 +2337,30 @@
         
-return
+const result =
  await r
@@ -2375,32 +2375,109 @@
 ccept(...args);%0A
+          this.setState(%7BrequestInProgress: null%7D);%0A          return result;%0A
         %7D catch 
@@ -2533,32 +2533,57 @@
 %5Brequest, error%5D
+, requestInProgress: null
 %7D);%0A          re
@@ -2611,21 +2611,34 @@
    %7D
- finally
+%0A      %7D : (...args) =%3E
  %7B%0A
-  
     
@@ -2667,62 +2667,30 @@
 uest
-InProgress
+Error
 : 
+%5B
 null
-%7D);%0A        %7D%0A      %7D : (...args) =%3E %7B
+, null%5D%7D);
 %0A   

Manage dialog progress and error state more atomically