// Copyright 2021 The casbin Authors. All Rights Reserved.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//      http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

import React from 'react';
import {Link} from "react-router-dom";
import {Form, Input, Checkbox, Button, Row, Col, Result, Modal} from 'antd';
import * as Setting from "../Setting";
import * as AuthBackend from "./AuthBackend";
import i18next from "i18next";
import * as Util from "./Util";
import {authConfig} from "./Auth";
import * as ApplicationBackend from "../backend/ApplicationBackend";
import {CountDownInput} from "../common/CountDownInput";
import SelectRegionBox from "../SelectRegionBox";
import CustomGithubCorner from "../CustomGithubCorner";

const formItemLayout = {
  labelCol: {
    xs: {
      span: 24,
    },
    sm: {
      span: 8,
    },
  },
  wrapperCol: {
    xs: {
      span: 24,
    },
    sm: {
      span: 18,
    },
  },
};

const tailFormItemLayout = {
  wrapperCol: {
    xs: {
      span: 24,
      offset: 0,
    },
    sm: {
      span: 16,
      offset: 8,
    },
  },
};

class SignupPage extends React.Component {
  constructor(props) {
    super(props);
    this.state = {
      classes: props,
      applicationName: props.match?.params.applicationName !== undefined ? props.match.params.applicationName : authConfig.appName,
      application: null,
      email: "",
      phone: "",
      emailCode: "",
      phoneCode: "",
      validEmail: false,
      validPhone: false,
      region: "",
      isTermsOfUseVisible: false,
      termsOfUseContent: "",
    };

    this.form = React.createRef();
  }

  UNSAFE_componentWillMount() {
    if (this.state.applicationName !== undefined) {
      this.getApplication();
    } else {
      Util.showMessage("error", `Unknown application name: ${this.state.applicationName}`);
    }
  }

  getApplication() {
    if (this.state.applicationName === undefined) {
      return;
    }

    ApplicationBackend.getApplication("admin", this.state.applicationName)
      .then((application) => {
        this.setState({
          application: application,
        });
        this.getTermsofuseContent(application.termsOfUse);
      });
  }

  getResultPath(application) {
    if (authConfig.appName === application.name) {
      return "/result";
    } else {
      if (Setting.hasPromptPage(application)) {
        return `/prompt/${application.name}`;
      } else {
        return `/result/${application.name}`;
      }
    }
  }

  getApplicationObj() {
    if (this.props.application !== undefined) {
      return this.props.application;
    } else {
      return this.state.application;
    }
  }

  getTermsofuseContent(url) {
    fetch(url, {
      method: "GET",
    }).then(r => {
      r.text().then(res => {
        this.setState({termsOfUseContent: res})
      })
    })
  }

  onUpdateAccount(account) {
    this.props.onUpdateAccount(account);
  }

  onFinish(values) {
    const application = this.getApplicationObj();
    values.phonePrefix = application.organizationObj.phonePrefix;
    AuthBackend.signup(values)
      .then((res) => {
        if (res.status === 'ok') {
          if (Setting.hasPromptPage(application)) {
            AuthBackend.getAccount("")
              .then((res) => {
                let account = null;
                if (res.status === "ok") {
                  account = res.data;
                  account.organization = res.data2;

                  this.onUpdateAccount(account);
                  Setting.goToLinkSoft(this, this.getResultPath(application));
                } else {
                  Setting.showMessage("error", `Failed to sign in: ${res.msg}`);
                }
              });
          } else {
            Setting.goToLinkSoft(this, this.getResultPath(application));
          }
        } else {
          Setting.showMessage("error", i18next.t(`signup:${res.msg}`));
        }
      });
  }

  onFinishFailed(values, errorFields, outOfDate) {
    this.form.current.scrollToField(errorFields[0].name);
  }

  renderFormItem(application, signupItem) {
    if (!signupItem.visible) {
      return null;
    }

    const required = signupItem.required;

    if (signupItem.name === "Username") {
      return (
        <Form.Item
          name="username"
          key="username"
          label={i18next.t("signup:Username")}
          rules={[
            {
              required: required,
              message: i18next.t("forget:Please input your username!"),
              whitespace: true,
            },
          ]}
        >
          <Input />
        </Form.Item>
      )
    } else if (signupItem.name === "Display name") {
      return (
        <Form.Item
          name="name"
          key="name"
          label={signupItem.rule === "Personal" ? i18next.t("general:Personal name") : i18next.t("general:Display name")}
          rules={[
            {
              required: required,
              message: signupItem.rule === "Personal" ? i18next.t("signup:Please input your personal name!") : i18next.t("signup:Please input your display name!"),
              whitespace: true,
            },
          ]}
        >
          <Input />
        </Form.Item>
      )
    } else if (signupItem.name === "Affiliation") {
      return (
        <Form.Item
          name="affiliation"
          key="affiliation"
          label={i18next.t("user:Affiliation")}
          rules={[
            {
              required: required,
              message: i18next.t("signup:Please input your affiliation!"),
              whitespace: true,
            },
          ]}
        >
          <Input />
        </Form.Item>
      )
    } else if (signupItem.name === "ID card") {
      return (
        <Form.Item
          name="idCard"
          key="idCard"
          label={i18next.t("user:ID card")}
          rules={[
            {
              required: required,
              message: i18next.t("signup:Please input your ID card number!"),
              whitespace: true,
            },
            {
              required: required,
              pattern: new RegExp(/^[1-9]\d{5}(18|19|20)\d{2}((0[1-9])|(10|11|12))(([0-2][1-9])|10|20|30|31)\d{3}[0-9X]$/, "g"),
              message: i18next.t("signup:Please input the correct ID card number!"),
            },
          ]}
        >
          <Input />
        </Form.Item>
      )
    } else if (signupItem.name === "Country/Region") {
      return (
        <Form.Item
            name="country_region"
            key="region"
            label={i18next.t("user:Country/Region")}
            rules={[
                {
                    required: required,
                    message: i18next.t("signup:Please select your country/region!"),
                },
            ]}
        >
          <SelectRegionBox onChange={(value) => {this.setState({region: value})}} />
        </Form.Item>
      )
    } else if (signupItem.name === "Email") {
      return (
        <React.Fragment>
          <Form.Item
            name="email"
            key="email"
            label={i18next.t("general:Email")}
            rules={[
              {
                required: required,
                message: i18next.t("signup:Please input your Email!"),
              },
              {
                validator: (_, value) => {
                  if (this.state.email !== "" && !Setting.isValidEmail(this.state.email)) {
                    this.setState({validEmail: false});
                    return Promise.reject(i18next.t("signup:The input is not valid Email!"));
                  }

                  this.setState({validEmail: true});
                  return Promise.resolve();
                }
              }
            ]}
          >
            <Input onChange={e => this.setState({email: e.target.value})} />
          </Form.Item>
          <Form.Item
            name="emailCode"
            key="emailCode"
            label={i18next.t("code:Email code")}
            rules={[{
              required: required,
              message: i18next.t("code:Please input your verification code!"),
            }]}
          >
            <CountDownInput
              disabled={!this.state.validEmail}
              onButtonClickArgs={[this.state.email, "email", Setting.getApplicationOrgName(application)]}
            />
          </Form.Item>
        </React.Fragment>
      )
    } else if (signupItem.name === "Phone") {
      return (
        <React.Fragment>
          <Form.Item
            name="phone"
            key="phone"
            label={i18next.t("general:Phone")}
            rules={[
              {
                required: required,
                message: i18next.t("signup:Please input your phone number!"),
              },
              {
                validator: (_, value) =>{
                  if (this.state.phone !== "" && !Setting.isValidPhone(this.state.phone)) {
                    this.setState({validPhone: false});
                    return Promise.reject(i18next.t("signup:The input is not valid Phone!"));
                  }

                  this.setState({validPhone: true});
                  return Promise.resolve();
                }
              }
            ]}
          >
            <Input
              style={{
                width: '100%',
              }}
              addonBefore={`+${this.state.application?.organizationObj.phonePrefix}`}
              onChange={e => this.setState({phone: e.target.value})}
            />
          </Form.Item>
          <Form.Item
            name="phoneCode"
            key="phoneCode"
            label={i18next.t("code:Phone code")}
            rules={[
              {
                required: required,
                message: i18next.t("code:Please input your phone verification code!"),
              },
            ]}
          >
            <CountDownInput
              disabled={!this.state.validPhone}
              onButtonClickArgs={[this.state.phone, "phone", Setting.getApplicationOrgName(application)]}
            />
          </Form.Item>
        </React.Fragment>
      )
    } else if (signupItem.name === "Password") {
      return (
        <Form.Item
          name="password"
          key="password"
          label={i18next.t("general:Password")}
          rules={[
            {
              required: required,
              min: 6,
              message: i18next.t("login:Please input your password, at least 6 characters!"),
            },
          ]}
          hasFeedback
        >
          <Input.Password />
        </Form.Item>
      )
    } else if (signupItem.name === "Confirm password") {
      return (
        <Form.Item
          name="confirm"
          key="confirm"
          label={i18next.t("signup:Confirm")}
          dependencies={['password']}
          hasFeedback
          rules={[
            {
              required: required,
              message: i18next.t("signup:Please confirm your password!"),
            },
            ({ getFieldValue }) => ({
              validator(rule, value) {
                if (!value || getFieldValue('password') === value) {
                  return Promise.resolve();
                }

                return Promise.reject(i18next.t("signup:Your confirmed password is inconsistent with the password!"));
              },
            }),
          ]}
        >
          <Input.Password />
        </Form.Item>
      )
    } else if (signupItem.name === "Agreement") {
      return (
        <Form.Item
          name="agreement"
          key="agreement"
          valuePropName="checked"
          rules={[
            {
              required: required,
              message: i18next.t("signup:Please accept the agreement!"),
            },
          ]}
          {...tailFormItemLayout}
        >
          <Checkbox>
            {i18next.t("signup:Accept")}&nbsp;
            <Link onClick={() => {
              this.setState({
                isTermsOfUseVisible: true,
              });
            }}>
              {i18next.t("signup:Terms of Use")}
            </Link>
          </Checkbox>
        </Form.Item>
      )
    }
  }

  renderModal() {
    return (
      <Modal
        title={i18next.t("signup:Terms of Use")}
        visible={this.state.isTermsOfUseVisible}
        width={"55vw"}
        closable={false}
        okText={i18next.t("signup:Accept")}
        cancelText={i18next.t("signup:Decline")}
        onOk={() => {
          this.form.current.setFieldsValue({agreement: true})
          this.setState({
            isTermsOfUseVisible: false,
          });
        }}
        onCancel={() => {
          this.form.current.setFieldsValue({agreement: false})
          this.setState({
            isTermsOfUseVisible: false,
          });
          this.props.history.goBack();
        }}
      >
        <iframe title={"terms"} style={{border: 0, width: "100%", height: "60vh"}} srcDoc={this.state.termsOfUseContent}/>
      </Modal>
    )
  }

  renderForm(application) {
    if (!application.enableSignUp) {
      return (
        <Result
          status="error"
          title="Sign Up Error"
          subTitle={"The application does not allow to sign up new account"}
          extra={[
            <Button type="primary" key="signin" onClick={() => {
              Setting.goToLogin(this, application);
            }}>
              Sign In
            </Button>
          ]}
        >
        </Result>
      )
    }
    return (
      <Form
        {...formItemLayout}
        ref={this.form}
        name="signup"
        onFinish={(values) => this.onFinish(values)}
        onFinishFailed={(errorInfo) => this.onFinishFailed(errorInfo.values, errorInfo.errorFields, errorInfo.outOfDate)}
        initialValues={{
          application: application.name,
          organization: application.organization,
        }}
        style={{width: !Setting.isMobile() ? "400px" : "250px"}}
        size="large"
      >
        <Form.Item
          style={{height: 0, visibility: "hidden"}}
          name="application"
          rules={[
            {
              required: true,
              message: 'Please input your application!',
            },
          ]}
        >
        </Form.Item>
        <Form.Item
          style={{height: 0, visibility: "hidden"}}
          name="organization"
          rules={[
            {
              required: true,
              message: 'Please input your organization!',
            },
          ]}
        >
        </Form.Item>
        {
          application.signupItems?.map(signupItem => this.renderFormItem(application, signupItem))
        }
        <Form.Item {...tailFormItemLayout}>
          <Button type="primary" htmlType="submit">
            {i18next.t("account:Sign Up")}
          </Button>
          &nbsp;&nbsp;{i18next.t("signup:Have account?")}&nbsp;
          <a onClick={() => {
            Setting.goToLogin(this, application);
          }}>
            {i18next.t("signup:sign in now")}
          </a>
        </Form.Item>
      </Form>
    )
  }

  render() {
    const application = this.getApplicationObj();
    if (application === null) {
      return null;
    }

    if (application.signupHtml !== "") {
      return (
        <div dangerouslySetInnerHTML={{ __html: application.signupHtml}} />
      )
    }

    return (
      <div>
        <CustomGithubCorner />
        &nbsp;
        <Row>
          <Col span={24} style={{display: "flex", justifyContent:  "center"}} >
            <div style={{marginTop: "10px", textAlign: "center"}}>
              {
                Setting.renderHelmet(application)
              }
              {
                Setting.renderLogo(application)
              }
              {
                this.renderForm(application)
              }
            </div>
          </Col>
        </Row>
        {
          this.renderModal()
        }
      </div>
    )
  }
}

export default SignupPage;
