import { Model } from "dva";
import _ from 'lodash';
import { message, Modal } from 'antd';
import { delay } from 'dva/saga';
import { PageState, AreaCode, AreaCodes } from '@/types/common';
import { getAreaCodes, getUserInfo } from '@/services/common';
import { validPhoneInput, validPwdInput, validPhone, validPwd } from '@/utils/validate';
import { checkExistence, CheckResult, signIn, signUp, sendCode, reset } from './service';
import { formatMessage } from 'umi-plugin-locale';
import { dispatch } from '@/utils/utils';

export enum PageStep {
  Check = 'check',
  Login = 'login',
  Register = 'register',
  Reset = 'reset',
  ResetSuccess = 'resetSuccess',
}

export interface CheckModelState {
  pageState: PageState,
  pageStep: PageStep,
  areacodes: AreaCodes,
  areacode: AreaCode | undefined, // 选中的地区
  phone: string;
  password: string;
  pwdConfirm: string;
  inviter: string;
  verifyCode: string;
  submitting: boolean;
  sendingCode: boolean;
  codeTimer: number;
}

const initState: CheckModelState = {
  pageState: PageState.Pending,
  pageStep: PageStep.Check,
  areacodes: [],
  areacode: undefined,
  phone: '',
  password: '',
  pwdConfirm: '',
  inviter: '',
  verifyCode: '',
  submitting: false,
  sendingCode: false,
  codeTimer: -1,
};

const model: Model = {
  namespace: 'check',

  state: initState,

  effects: {
    *back(_, { select, put }) {
      const { pageStep: prev } = yield select((state: any) => state.check);
      let pageStep = PageStep.Check;
      switch (prev) {
        case PageStep.Login:
        case PageStep.Register:
        case PageStep.ResetSuccess:
          pageStep = PageStep.Check;
          break;
        case PageStep.Reset:
          pageStep = PageStep.Login;
          break;
      }
      yield put({
        type: 'onUpdateState',
        pageStep,
        password: '',
        pwdConfirm: '',
        verifyCode: '',
      });
    },

    *load(_, { call, put, all, select }) {
      const { location } = yield select((state: any) => state.router);
      let inviter = '';
      if (location.query && location.query.inviteCode) {
        inviter = location.query.inviteCode;
      }
      yield put({ type: 'onUpdateState', pageState: PageState.Pending, inviter });

      try {
        const areacodes = yield call(getAreaCodes);
        yield all([
          put({ type: 'onUpdatePageState', payload: PageState.Success }),
          put({ type: 'onUpdateAreaCodes', payload: areacodes }),
        ]);
      } catch(e) {
        yield put({ type: 'onUpdatePageState', payload: PageState.Failure });
      }
    },

    *timer({ payload }: any, { put }) {
      yield put({ type: 'onUpdateState', codeTimer: payload });
      yield delay(1000);
      const next = payload - 1;
      if (next >= 0) {
        yield put({ type: 'timer', payload: next });
      }
    },

    *submitCheck(_, { put, call, select }) {
      const { areacode, phone } = yield select((state: any) => state.check);
      if (!areacode) return message.warn(formatMessage({ id: 'check.msgArea' }));
      if (!validPhone(phone)) return message.warn(formatMessage({ id: 'check.msgPhone' }));

      yield put({ type: 'onUpdateState', submitting: true });
      try {
        const checkResult: CheckResult = yield call(checkExistence, areacode, phone);
        yield put({
          type: 'onUpdateState',
          pageStep: {
            [CheckResult.Login]: PageStep.Login,
            [CheckResult.Register]: PageStep.Register,
          }[checkResult],
        });
      } catch(e) {
        message.error(e.message);
      } finally {
        yield put({ type: 'onUpdateState', submitting: false });
      }
    },

    *submitLogin(_, { put, call, select, all }) {
      const { areacode, phone, password } = yield select((state: any) => state.check);
      if (!areacode) return message.warn(formatMessage({ id: 'check.msgArea' }));
      if (!validPhone(phone)) return message.warn(formatMessage({ id: 'check.msgPhone' }));
      if (!validPwd(password)) return message.warn(formatMessage({ id: 'check.msgPwd' }));

      yield put({ type: 'onUpdateState', submitting: true });
      try {
        const token = yield call(signIn, areacode, phone, password);
        yield put({ type: 'user/updateToken', payload: token });
        yield delay(10);
        const user = yield call(getUserInfo, true);
        yield all([
          put({ type: 'user/updateUser', payload: user }),
          put({ type: 'onUpdateState', ...initState }),
        ]);
      } catch(e) {
        message.error(e.message);
        yield all([
          put({ type: 'user/logout' }),
          put({ type: 'onUpdateState', submitting: false }),
        ]);
      }
    },

    *submitRegister(_, { put, select, call, all }) {
      const { areacode, phone, password, verifyCode, inviter, pwdConfirm } = yield select((state: any) => state.check);
      if (!areacode) return message.warn(formatMessage({ id: 'check.msgArea' }));
      if (!validPhone(phone)) return message.warn(formatMessage({ id: 'check.msgPhone' }));
      if (!verifyCode) return message.warn(formatMessage({ id: 'check.msgCode' }));
      if (!validPwd(password)) return message.warn(formatMessage({ id: 'check.msgPwd' }));
      if (password !== pwdConfirm) return message.warn(formatMessage({ id: 'check.msgConfirm' }));
      if (!inviter) return message.warn(formatMessage({ id: 'check.msgInviter' }));

      yield put({ type: 'onUpdateState', submitting: true });
      try {
        const token = yield call(signUp, areacode, phone, password, verifyCode, inviter);
        yield put({ type: 'user/updateToken', payload: token });
        yield delay(10);
        const user = yield call(getUserInfo, true);
        yield all([
          put({ type: 'user/updateUser', payload: user }),
          put({ type: 'onUpdateState', ...initState }),
        ]);
      } catch(e) {
        message.error(e.message);
        yield all([
          put({ type: 'user/logout' }),
          put({ type: 'onUpdateState', submitting: false }),
        ]);
      }
    },

    *submitReset(_, { put, select, call }) {
      const { areacode, phone, password, verifyCode, pwdConfirm } = yield select((state: any) => state.check);
      if (!areacode) return message.warn(formatMessage({ id: 'check.msgArea' }));
      if (!validPhone(phone)) return message.warn(formatMessage({ id: 'check.msgPhone' }));
      if (!verifyCode) return message.warn(formatMessage({ id: 'check.msgCode' }));
      if (!validPwd(password)) return message.warn(formatMessage({ id: 'check.msgPwd' }));
      if (password !== pwdConfirm) return message.warn(formatMessage({ id: 'check.msgConfirm' }));

      yield put({ type: 'onUpdateState', submitting: true });
      try {
        yield call(reset, areacode, phone, password, verifyCode);
        Modal.success({
          okText: formatMessage({ id: 'common.iknow' }),
          title: formatMessage({ id: 'common.prompt' }),
          content: formatMessage({ id: 'check.resetSuccess' }),
          onOk: () => dispatch({ type: 'check/back' }),
        });
      } catch(e) {
        message.error(e.message);
      } finally {
        yield put({ type: 'onUpdateState', submitting: false });
      }
    },

    *sendCode({ payload }, { select, put, call }) {
      const { areacode, phone } = yield select((state: any) => state.check);
      if (!areacode) return message.warn(formatMessage({ id: 'check.msgArea' }));
      if (!validPhone(phone)) return message.warn(formatMessage({ id: 'check.msgPhone' }));

      yield put({ type: 'onUpdateState', sendingCode: true });
      try {
        yield call(sendCode, areacode, phone, payload);
        yield put({ type: 'timer', payload: 60 });
      } catch(e) {
        message.error(e.message);
      } finally {
        yield put({ type: 'onUpdateState', sendingCode: false });
      }
    },
  },

  reducers: {
    onUpdatePageState(state, { payload: pageState } : any) {
      return {
        ...state,
        pageState,
      };
    },

    onUpdateAreaCodes(state, { payload: areacodes } : any) {
      return {
        ...state,
        areacodes,
        areacode: areacodes[0],
      };
    },

    onUpdatePhone(state, { payload } : any) {
      const phone = validPhoneInput(payload) ? payload : state.phone;
      return {
        ...state,
        phone,
      };
    },

    onUpdatePassword(state, { payload } : any) {
      const password = validPwdInput(payload) ? payload : state.password;
      return {
        ...state,
        password,
      };
    },

    onUpdateState(state, newState) {
      return {
        ...state,
        ...newState,
      };
    },
  },
};

export default model;
