import { Subject, Subscription } from 'rxjs';
import { Provide } from 'vue-property-decorator';
import { ModelTool, Verify, AppErrorCode, EntityFieldErrorCode, FormControlInterface } from '@ibizstudio/runtime';
import { MainControlBase } from './main-control-base';
import { IPSDEEditFormItem, IPSDEForm } from '@ibizstudio/runtime';

/**
 * 表单部件基类
 *
 * @export
 * @class FormControlBase
 * @extends {MainControlBase}
 */
export class FormControlBase extends MainControlBase implements FormControlInterface {
  /**
   * 表单数据对象
   *
   * @type {*}
   * @memberof FormControlBase
   */
  data: any = {};

  /**
   * 表单的模型对象
   *
   * @type {*}
   * @memberof FormControlBase
   */
  declare controlInstance: IPSDEForm;

  /**
   * 表单服务对象
   *
   * @type {*}
   * @memberof ControlBase
   */
  declare service: any;

  /**
   * 部件行为--loaddraft
   *
   * @type {*}
   * @memberof FormControlBase
   */
  loaddraftAction: any;

  /**
   * 部件行为--load
   *
   * @type {*}
   * @memberof FormControlBase
   */
  loadAction: any;

  /**
   * 表单状态
   *
   * @type {Subject<any>}
   * @memberof FormControlBase
   */
  @Provide('formState')
  formState: Subject<any> = new Subject();

  /**
   * 忽略表单项值变化
   *
   * @type {boolean}
   * @memberof FormControlBase
   */
  ignorefieldvaluechange: boolean = false;

  /**
   * 数据变化
   *
   * @public
   * @type {Subject<any>}
   * @memberof FormControlBase
   */
  dataChang!: Subject<any>;

  /**
   * 视图状态事件
   *
   * @public
   * @type {(Subscription | undefined)}
   * @memberof FormControlBase
   */
  dataChangEvent: Subscription | undefined;

  /**
   * 原始数据
   *
   * @public
   * @type {*}
   * @memberof FormControlBase
   */
  oldData: any;

  /**
   * 详情模型集合
   *
   * @type {*}
   * @memberof FormControlBase
   */
  detailsModel: any;

  /**
   * 值规则
   *
   * @type {*}
   * @memberof FormControlBase
   */
  rules: any = {};

  /**
   * 转化代码项值映射
   *
   * @type {Map<string,IParams>}
   * @memberof FormControlBase
   */
  codeItemTextMap: Map<string, IParams> = new Map();

  /**
   * 获取多项数据
   *
   * @returns {any[]}
   * @memberof FormControlBase
   */
  getDatas(): any[] {
    return [this.getData()];
  }

  /**
   * 获取单项数据
   *
   * @returns {*}
   * @memberof FormControlBase
   */
  getData(): any {
    const data: any = {};
    for (const key in this.data) {
      if (Object.prototype.hasOwnProperty.call(this.data, key)) {
        const item = this.detailsModel[key];
        if (item?.ignoreInput === 4 && item.disabled) {
          continue;
        }
        const val = this.data[key];
        data[key] = val;
      }
    }
    if (this.codeItemTextMap.size > 0) {
      this.codeItemTextMap.forEach((value, key) => {
        const { val } = value;
        data[key] = val;
      });
    }
    return data;
  }

  /**
   * 重置表单项值
   *
   * @public
   * @param {{ name: string, newVal: any, oldVal: any }} { name, newVal, oldVal } 名称,新值，旧值
   * @memberof FormControlBase
   */
  resetFormData({ name, newVal, oldVal }: { name: string; newVal: any; oldVal: any }): void {}

  /**
   * 表单逻辑
   *
   * @public
   * @param {{ name: string, newVal: any, oldVal: any }} { name, newVal, oldVal } 名称,新值，旧值
   * @memberof FormControlBase
   */
  async formLogic({ name, newVal, oldVal }: { name: string; newVal: any; oldVal: any }) {}

  /**
   * 表单值变化
   *
   * @public
   * @param {{ name: string, newVal: any, oldVal: any }} { name, newVal, oldVal } 名称,新值，旧值
   * @returns {void}
   * @memberof FormControlBase
   */
  formDataChange({ name, newVal, oldVal }: { name: string; newVal: any; oldVal: any }): void {}

  /**
   * 表单加载完成
   *
   * @public
   * @param {*} [data={}] 表单数据
   * @param {string} [action] 行为标识
   * @memberof FormControlBase
   */
  onFormLoad(data: any = {}, action: string): void {}

  /**
   * 值填充
   *
   * @param {*} [_datas={}] 表单数据
   * @param {string} [action] 行为标识
   * @memberof FormControlBase
   */
  fillForm(_datas: any = {}, action: string): void {
    this.ignorefieldvaluechange = true;
    Object.keys(_datas).forEach((name: string) => {
      if (this.data.hasOwnProperty(name)) {
        this.data[name] = _datas[name];
      }
    });
    if (Object.is(action, 'loadDraft')) {
      this.createDefault();
    }
    this.$nextTick(function() {
      this.ignorefieldvaluechange = false;
    });
  }

  /**
   * 设置表单项是否启用
   *
   * @public
   * @param {*} data 表单数据
   * @memberof FormControlBase
   */
  setFormEnableCond(data: any): void {
    Object.values(this.detailsModel).forEach((detail: any) => {
      if (!Object.is(detail.detailType, 'FORMITEM')) {
        return;
      }
      const formItem: any = detail;
      formItem.setEnableCond(data.srfuf);
    });
  }

  /**
   * 新建默认值
   * @memberof FormControlBase
   */
  createDefault() {}

  /**
   * 重置草稿表单状态
   *
   * @public
   * @memberof FormControlBase
   */
  resetDraftFormStates(): void {
    const form: any = this.$refs[this.controlInstance.name];
    if (form) {
      form.resetFields();
    }
  }

  /**
   * 重置校验结果
   *
   * @memberof FormControlBase
   */
  resetValidates(): void {
    Object.values(this.detailsModel).forEach((detail: any) => {
      if (!Object.is(detail.detailType, 'FORMITEM')) {
        return;
      }
      const formItem: any = detail;
      formItem.setError('');
    });
  }

  /**
   * 填充校验结果 （后台）
   *
   * @param {any[]} fieldErrors 校验数组
   * @memberof FormControlBase
   */
  fillValidates(fieldErrors: any[]): void {
    fieldErrors.forEach((error: any) => {
      const formItem: any = this.detailsModel[error.field];
      if (!formItem) {
        return;
      }
      this.$nextTick(() => {
        formItem.setError(error.message);
      });
    });
  }

  /**
   * 表单校验状态
   *
   * @returns {boolean}
   * @memberof FormControlBase
   */
  formValidateStatus(): boolean {
    const form: any = this.$refs[this.controlInstance.name];
    let validatestate: boolean = true;
    form.validate((valid: boolean) => {
      validatestate = valid ? true : false;
    });
    return validatestate;
  }

  /**
   * 表单项值变更
   *
   * @param {{ name: string, value: any }} $event 名称，值
   * @returns {void}
   * @memberof FormControlBase
   */
  onFormItemValueChange($event: { name: string; value: any }): void {
    if (!$event || !$event.name || Object.is($event.name, '') || !this.data.hasOwnProperty($event.name)) {
      return;
    }
    this.validateEditorRuleAction($event.name, $event.value);
    this.data[$event.name] = $event.value;
    this.formDataChange({ name: $event.name, newVal: $event.value, oldVal: null });
  }

  /**
   * 校验编辑器基础规则后续行为
   *
   * @param {string} name 名称
   * @param {*} value 值
   * @returns {void}
   * @memberof FormControlBase
   */
  validateEditorRuleAction(name: string, value: any) {
    let allFormItems: IPSDEEditFormItem[] = ModelTool.getAllFormItems(this.controlInstance);
    if (allFormItems?.length > 0) {
      let curFormItem: IPSDEEditFormItem | undefined = allFormItems?.find((item: any) => {
        return item.name === name;
      });
      if (!curFormItem) return;
      let condition: any = Verify.buildVerConditions(curFormItem.getPSEditor());
      if (condition && condition.length > 0) {
        // todo 提示info
      }
    }
  }

  /**
   * @description 校验是否忽略输入值
   * @protected
   * @param {string} name
   * @param {('before' | 'change')} [step='change'] {before：表单值变化之前，change：表单值变化}
   * @return {*}  {boolean}
   * @memberof FormControlBase
   */
  protected checkIgnoreInput(name: string, step: 'before' | 'change' = 'change'): boolean {
    const formDetail = this.detailsModel[name];
    if (formDetail) {
      switch (formDetail.ignoreInput) {
        case 4: //表单项禁用
          if (formDetail.disabled) {
            return true;
          }
          break;
      }
    }
    return false;
  }

  /**
   * 设置数据项值
   *
   * @param {string} name 名称
   * @param {*} value 值
   * @returns {void}
   * @memberof FormControlBase
   */
  setDataItemValue(name: string, value: any): void {
    if (!name || Object.is(name, '') || !this.data.hasOwnProperty(name)) {
      return;
    }
    if (Object.is(this.data[name], value)) {
      return;
    }
    this.data[name] = value;
  }

  /**
   * 分组界面行为事件
   *
   * @param {*} $event
   * @memberof FormControlBase
   */
  groupUIActionClick($event: any): void {}

  /**
   * 编辑表单初始化
   *
   * @memberof FormControlBase
   */
  ctrlInit(args?: any) {
    super.ctrlInit(args);
    // 初始化默认值
    this.dataChang = new Subject();
    this.ignorefieldvaluechange = false;
  }

  viewStateAction(tag: string, action: string, data: any) {
    if (!Object.is(tag, this.name)) {
      return;
    }
    super.viewStateAction(tag, action, data);
    if (Object.is('autoload', action)) {
      this.autoLoad(data);
    }
    if (Object.is('load', action)) {
      this.load(data);
    }
    if (Object.is('loaddraft', action)) {
      this.loadDraft(data);
    }
  }

  /**
   * 表单销毁
   *
   * @memberof FormControlBase
   */
  ctrlDestroyed(args?: any) {
    super.ctrlDestroyed();
    this.dataChang?.complete();
    this.formState.complete();
  }

  /**
   * 表单自动加载
   *
   * @param {*} [arg={}] 加载参数
   * @returns {void}
   * @memberof FormControlBase
   */
  autoLoad(arg: any = {}): void {
    if (arg.srfkey && !Object.is(arg.srfkey, '')) {
      Object.assign(arg, { srfkey: arg.srfkey });
      this.load(arg);
      return;
    }
    if (arg.srfkeys && !Object.is(arg.srfkeys, '')) {
      Object.assign(arg, { srfkey: arg.srfkeys });
      this.load(arg);
      return;
    }
    this.loadDraft(arg);
  }

  /**
   * 加载
   *
   * @public
   * @param {*} [opt={}] 加载参数
   * @memberof FormControlBase
   */
  async load(opt: any = {}) {
    if (!this.loadAction) {
      this.$throw(`${this.controlInstance.codeName}` + (this.$t('app.formpage.notconfig.loadaction') as string), 'load');
      return;
    }
    const arg: any = { ...opt };
    let tempContext: any = JSON.parse(JSON.stringify(this.context));
    let viewparamResult: any = Object.assign(arg, this.viewparams);
    let beforeloadResult: any = await this.executeCtrlEventLogic('onbeforeload', {
      action: this.loadAction,
      sender: this,
      navContext: this.context,
      navParam: viewparamResult,
      navData: this.navdatas,
      data: this.getData(),
    });
    if (beforeloadResult && beforeloadResult?.hasOwnProperty('srfret') && !beforeloadResult.srfret) {
      return;
    }
    this.ctrlEvent({
      controlname: this.controlInstance.name,
      action: 'onbeforeload',
      data: { action: this.loadAction, sender: this, navContext: this.context, navParam: viewparamResult, navData: this.navdatas, data: this.getData() },
    });
    this.onControlRequset('load', tempContext, viewparamResult);
    const get: Promise<any> = this.service.get(this.loadAction, tempContext, { viewparams: viewparamResult }, this.showBusyIndicator);
    get
      .then(async (response: any) => {
        this.onControlResponse('load', response);
        if (!response.status || response.status !== 200) {
          let loaderrorResult: any = await this.executeCtrlEventLogic('onloaderror', {
            action: this.loadAction,
            sender: this,
            navContext: this.context,
            navParam: viewparamResult,
            navData: this.navdatas,
            data: response?.data,
          });
          if (loaderrorResult && loaderrorResult?.hasOwnProperty('srfret') && !loaderrorResult.srfret) {
            return;
          }
          this.ctrlEvent({
            controlname: this.controlInstance.name,
            action: 'onloaderror',
            data: { action: this.loadAction, sender: this, navContext: this.context, navParam: viewparamResult, navData: this.navdatas, data: response?.data },
          });
          this.$throw(response, 'load');
          return;
        }
        const data = response.data;
        let loadsuccessResult: any = await this.executeCtrlEventLogic('onloadsuccess', {
          action: this.loadAction,
          sender: this,
          navContext: this.context,
          navParam: viewparamResult,
          navData: this.navdatas,
          data: data,
        });
        if (loadsuccessResult && loadsuccessResult?.hasOwnProperty('srfret') && !loadsuccessResult.srfret) {
          return;
        }
        this.ctrlEvent({
          controlname: this.controlInstance.name,
          action: 'onloadsuccess',
          data: { action: this.loadAction, sender: this, navContext: this.context, navParam: viewparamResult, navData: this.navdatas, data: data },
        });
        this.onFormLoad(data, 'load');
        this.ctrlEvent({
          controlname: this.controlInstance.name,
          action: 'load',
          data: this.data,
        });
        this.$nextTick(() => {
          this.formState.next({ type: 'load', data: data });
        });
      })
      .catch(async (error: any) => {
        this.onControlResponse('load', error);
        let loaderrorResult: any = await this.executeCtrlEventLogic('onloaderror', {
          action: this.loadAction,
          sender: this,
          navContext: this.context,
          navParam: viewparamResult,
          navData: this.navdatas,
          data: error?.data,
        });
        if (loaderrorResult && loaderrorResult?.hasOwnProperty('srfret') && !loaderrorResult.srfret) {
          return;
        }
        this.ctrlEvent({
          controlname: this.controlInstance.name,
          action: 'onloaderror',
          data: { action: this.loadAction, sender: this, navContext: this.context, navParam: viewparamResult, navData: this.navdatas, data: error?.data },
        });
        this.$throw(error, 'load');
      });
  }

  /**
   * 加载草稿
   *
   * @param {*} [opt={}]
   * @memberof FormControlBase
   */
  loadDraft(opt: any = {}, mode?: string): void {
    if (!this.loaddraftAction) {
      this.$throw(this.$t('app.searchform.notconfig.loaddraftaction') as string, 'loadDraft');
      return;
    }
    const arg: any = { ...opt };
    Object.assign(arg, { viewparams: this.viewparams });
    const tempContext: any = JSON.parse(JSON.stringify(this.context));
    this.onControlRequset('loadDraft', tempContext, arg);
    let post: Promise<any> = this.service.loadDraft(this.loaddraftAction, tempContext, arg, this.showBusyIndicator);
    post
      .then((response: any) => {
        this.onControlResponse('loadDraft', response);
        if (!response.status || response.status !== 200) {
          this.$throw(response, 'loadDraft');
          return;
        }
        const data = response.data;
        this.resetDraftFormStates();
        this.onFormLoad(data, 'loadDraft');
        setTimeout(() => {
          const form: any = this.$refs[this.controlInstance.name];
          if (form) {
            form.fields.forEach((field: any) => {
              field.validateMessage = '';
              field.validateState = '';
              field.validateStatus = false;
            });
          }
        });
        if (Object.is(mode, 'RESET')) {
          if (!this.formValidateStatus()) {
            return;
          }
        }
        this.ctrlEvent({
          controlname: this.controlInstance.name,
          action: 'load',
          data: data,
        });
        this.$nextTick(() => {
          this.formState.next({ type: 'load', data: data });
        });
      })
      .catch((response: any) => {
        this.onControlResponse('loadDraft', response);
        this.$throw(response, 'loadDraft');
      });
  }

  /**
   * 表单项更新
   *
   * @param {string} mode 界面行为名称
   * @param {*} [data={}] 请求数据
   * @param {string[]} updateDetails 更新项
   * @param {boolean} [showloading] 是否显示加载状态
   * @returns {void}
   * @memberof FormControlBase
   */
  updateFormItems(mode: string, data: any = {}, updateDetails: string[], showloading?: boolean): void {}

  /**
   * 回车事件
   *
   * @param {*} $event
   * @memberof FormControlBase
   */
  onEnter($event: any): void {}

  /**
   * 搜索
   *
   * @memberof FormControlBase
   */
  onSearch() {
    if (!this.formValidateStatus()) {
      return;
    }
    this.ctrlEvent({
      controlname: this.controlInstance.name,
      action: 'search',
      data: this.data,
    });
  }

  /**
   * 重置
   *
   * @memberof FormControlBase
   */
  onReset() {
    this.loadDraft({}, 'RESET');
  }

  /**
   * 通过属性名获取表单项
   *
   * @memberof FormControlBase
   */
  findFormItemByField(fieldName: string) {
    const formItems: IPSDEEditFormItem[] = ModelTool.getAllFormItems(this.controlInstance);
    return formItems.find((formItem: any) => {
      return formItem.getPSAppDEField()?.name == fieldName;
    });
  }

  /**
   * 处理部件UI请求
   *
   * @param {string} action 行为名称
   * @param {*} context 上下文
   * @param {*} viewparam 视图参数
   * @memberof FormControlBase
   */
  onControlRequset(action: string, context: any, viewparam: any) {
    if (!Object.is(action, 'updateFormItems')) {
      this.ctrlBeginLoading();
    }
  }

  /**
   * 处理部件UI响应
   *
   * @param {string} action 行为名称
   * @param {*} response 响应
   * @memberof FormControlBase
   */
  onControlResponse(action: string, response: any) {
    if (!Object.is(action, 'updateFormItems')) {
      this.ctrlEndLoading();
    }
    if (Object.is(action, 'load')) {
      this.isControlLoaded = true;
    }
    if (response && response.status && response.status == 403) {
      this.enableControlUIAuth = false;
      this.ctrlEvent({
        controlname: this.controlInstance.name,
        action: 'authlimit',
        data: response,
      });
    }
    if (response && response.status && response.status != 200 && response.data) {
      const data: any = response.data;
      if (data.code && Object.is(AppErrorCode.INPUTERROR, data.code) && data.details?.length > 0) {
        let errorMsg: string = '';
        data.details.forEach((detail: any) => {
          if (Object.is(EntityFieldErrorCode.ERROR_VALUERULE, detail.fielderrortype) && detail.fieldname) {
            const tempFormItem: any = this.findFormItemByField(detail.fieldname);
            if (tempFormItem) {
              Object.assign(this.detailsModel[tempFormItem.name], { error: new String(detail.fielderrorinfo ? detail.fielderrorinfo : data.message) });
            } else {
              errorMsg += `${detail.fieldlogicname}${detail.fielderrorinfo ? detail.fielderrorinfo : data.message}<br/>`;
            }
          }
        });
        response.data.message = errorMsg ? errorMsg : (this.$t('app.searchform.globalerrortip') as string);
        this.$forceUpdate();
      }
    }
  }
}
