import {FormRenderer} from 'app/client/components/FormRenderer';
import {handleSubmit, TypedFormData} from 'app/client/lib/formUtils';
import {makeT} from 'app/client/lib/localization';
import {FormModel, FormModelImpl} from 'app/client/models/FormModel';
import {buildFormFooter} from 'app/client/ui/FormContainer';
import {FormErrorPage} from 'app/client/ui/FormErrorPage';
import {FormSuccessPage} from 'app/client/ui/FormSuccessPage';
import {colors} from 'app/client/ui2018/cssVars';
import {ApiError} from 'app/common/ApiError';
import {getPageTitleSuffix} from 'app/common/gristUrls';
import {getGristConfig} from 'app/common/urlUtils';
import {Disposable, dom, makeTestId, Observable, styled, subscribe} from 'grainjs';

const t = makeT('FormPage');

const testId = makeTestId('test-form-');

export class FormPage extends Disposable {
  private readonly _model: FormModel = new FormModelImpl();
  private readonly _error = Observable.create<string|null>(this, null);

  constructor() {
    super();
    this._model.fetchForm().catch(reportError);

    this.autoDispose(subscribe(this._model.form, (_use, form) => {
      if (!form) { return; }

      document.title = `${form.formTitle}${getPageTitleSuffix(getGristConfig())}`;
    }));
  }

  public buildDom() {
    return cssPageContainer(
      dom.domComputed(use => {
        const error = use(this._model.error);
        if (error) { return dom.create(FormErrorPage, error); }

        const submitted = use(this._model.submitted);
        if (submitted) { return dom.create(FormSuccessPage, this._model); }

        return this._buildFormPageDom();
      }),
    );
  }

  private _buildFormPageDom() {
    return dom.domComputed(use => {
      const form = use(this._model.form);
      const rootLayoutNode = use(this._model.formLayout);
      if (!form || !rootLayoutNode) { return null; }

      const formRenderer = FormRenderer.new(rootLayoutNode, {
        fields: form.formFieldsById,
        rootLayoutNode,
        disabled: this._model.submitting,
        error: this._error,
      });

      return dom('div',
        cssForm(
          cssFormBody(
            cssFormContent(
              dom.autoDispose(formRenderer),
              formRenderer.render(),
              handleSubmit(this._model.submitting,
                (_formData, formElement) => this._handleFormSubmit(formElement),
                () => this._handleFormSubmitSuccess(),
                (e) => this._handleFormError(e),
              ),
            ),
          ),
          cssFormFooter(
            buildFormFooter(),
          ),
        ),
        testId('page'),
      );
    });
  }

  private async _handleFormSubmit(formElement: HTMLFormElement) {
    await this._model.submitForm(new TypedFormData(formElement));
  }

  private async _handleFormSubmitSuccess() {
    const formLayout = this._model.formLayout.get();
    if (!formLayout) { throw new Error('formLayout is not defined'); }

    const {successURL} = formLayout;
    if (successURL) {
      try {
        const url = new URL(successURL);
        window.location.href = url.href;
        return;
      } catch {
        // If the URL is invalid, just ignore it.
      }
    }

    this._model.submitted.set(true);
  }

  private _handleFormError(e: unknown) {
    this._error.set(t('There was an error submitting your form. Please try again.'));
    if (!(e instanceof ApiError) || e.status >= 500) {
      // If it doesn't look like a user error (i.e. a 4XX HTTP response), report it.
      reportError(e as Error|string);
    }
  }
}

const cssPageContainer = styled('div', `
  height: 100%;
  width: 100%;
  padding: 20px;
  overflow: auto;
`);

const cssForm = styled('div', `
  display: flex;
  flex-direction: column;
  align-items: center;
  background-color: white;
  border-radius: 3px;
  max-width: 600px;
  margin: 0px auto;
`);

const cssFormBody = styled('div', `
  width: 100%;
`);

// TODO: break up and move to `FormRendererCss.ts`.
const cssFormContent = styled('form', `
  color: ${colors.dark};
  font-size: 15px;
  line-height: 1.42857143;

  & h1,
  & h2,
  & h3,
  & h4,
  & h5,
  & h6 {
    margin: 8px 0px 12px 0px;
    font-weight: normal;
  }
  & h1 {
    font-size: 24px;
  }
  & h2 {
    font-size: 22px;
  }
  & h3 {
    font-size: 16px;
  }
  & h4 {
    font-size: 13px;
  }
  & h5 {
    font-size: 11px;
  }
  & h6 {
    font-size: 10px;
  }
  & p {
    margin: 0px;
  }
  & strong {
    font-weight: 600;
  }
  & hr {
    border: 0px;
    border-top: 1px solid ${colors.darkGrey};
    margin: 4px 0px;
  }
`);

const cssFormFooter = styled('div', `
  padding: 8px 16px;
  width: 100%;
`);