import React from 'react';

type DecisionOption = {
  code: string;
  label: string;
  tone: string;
  helper: string;
  requireComment: boolean;
};

type WriteTargetInfo = {
  paramName: string;
  raw: any;
  candidates: string[];
  displayValue: string;
};

export class CodeInApplication implements CodeInComp {
  description() {
    return "在易企办审批表单中选择处理决定、填写意见并校验必填说明。";
  }

  requiredFields(params?: CodeInParams) {
    const required: string[] = [];
    const safeParams: any = params || {};
    const targetMap: any = {};
    ['decisionField', 'commentField'].forEach((key) => {
      const candidates = this.collectRequiredFieldCandidates(safeParams[key]).filter((value) => this.isLikelyWritableTargetName(value));
      if (candidates.length > 0) {
        targetMap[key] = candidates[0];
      }
      candidates.forEach((value) => {
        if (value && required.indexOf(value) === -1) {
          required.push(value);
        }
      });
    });
    /* 表达式参数可能在 render() 前已被解析为当前值。requiredFields(params) 先读取原始配置，以保留要写入的字段或变量标识。 */
    (this as any).approvalDecisionTargetMap = targetMap;
    return required;
  }

  inputParameters(): InputParameter[] {
    return [
      {
        id: 'decisionField',
        name: "决定保存目标",
        type: 'variable',
        desc: "用于保存决定代码的可写审批表单字段或变量。",
      },
      {
        id: 'commentField',
        name: "意见保存目标",
        type: 'variable',
        desc: "用于保存审批意见或处理理由的可写字段或变量。",
      },
      {
        id: 'titleText',
        name: "标题",
        type: 'string',
        desc: "显示在审批选项上方的标题。",
      },
      {
        id: 'subtitleText',
        name: "副标题",
        type: 'string',
        desc: "显示在标题下方的操作说明。",
      },
      {
        id: 'approveLabel',
        name: "同意按钮文字",
        type: 'string',
        desc: "默认显示「同意」。",
      },
      {
        id: 'rejectLabel',
        name: "拒绝按钮文字",
        type: 'string',
        desc: "默认显示「拒绝」。",
      },
      {
        id: 'reviseLabel',
        name: "退回修改按钮文字",
        type: 'string',
        desc: "默认显示「退回修改」。",
      },
      {
        id: 'defaultDecision',
        name: "默认决定",
        type: 'variable',
        desc: "无已保存决定时显示的默认代码，例如 approve、reject 或 revise。",
      },
      {
        id: 'requireCommentOnReject',
        name: "拒绝时必填意见",
        type: 'variable',
        desc: "开启后先填写意见，再写入拒绝决定。支持布尔值或表达式。",
      },
      {
        id: 'requireCommentOnRevise',
        name: "退回时必填意见",
        type: 'variable',
        desc: "开启后先填写意见，再写入退回决定。支持布尔值或表达式。",
      },
      {
        id: 'showCommentBox',
        name: "显示意见输入框",
        type: 'variable',
        desc: "控制意见输入框显示。支持布尔值或表达式。",
      },
      {
        id: 'commentPlaceholder',
        name: "意见输入提示",
        type: 'string',
        desc: "意见输入框内的提示文字。",
      },
      {
        id: 'panelStyle',
        name: "面板样式",
        type: 'string',
        desc: "可用样式为 standard、compact 或 bordered，默认 standard。",
      },
      {
        id: 'showStatusSummary',
        name: "显示当前决定摘要",
        type: 'variable',
        desc: "控制当前决定摘要显示。支持布尔值或表达式。",
      },
      {
        id: 'readonlyText',
        name: "只读空状态文字",
        type: 'string',
        desc: "只读状态下没有已保存决定时的文字。",
      },
      {
        id: 'decisionOptionsJson',
        name: "自定义决定选项",
        type: 'variable',
        desc: "可选 JSON 数组。每项支持代码、显示文字、样式、辅助说明和意见必填开关。",
      },
      {
        id: 'validationMessageReject',
        name: "拒绝校验提示",
        type: 'string',
        desc: "需要填写拒绝理由时的提示。",
      },
      {
        id: 'validationMessageRevise',
        name: "退回校验提示",
        type: 'string',
        desc: "需要填写退回理由时的提示。",
      },
    ];
  }

  render(context: CodeInContext, fieldsValues: any, readonly: boolean) {
    return (
      <ApprovalDecisionPanel
        context={context}
        fieldsValues={fieldsValues}
        readonly={readonly}
        params={(context && context.params) || {}}
        configuredOutputTargets={(this as any).approvalDecisionTargetMap || {}}
      />
    );
  }

  normalizeExpressionText(value: any) {
    if (value === undefined || value === null) {
      return '';
    }
    if (typeof value === 'string' || typeof value === 'number' || typeof value === 'boolean') {
      return String(value).trim();
    }
    if (Array.isArray(value)) {
      return value.map((item) => this.normalizeExpressionText(item)).filter(Boolean).join(',');
    }
    if (typeof value === 'object') {
      return this.normalizeExpressionText(
        value.value !== undefined ? value.value :
        value.Value !== undefined ? value.Value :
        value.key !== undefined ? value.key :
        value.Key !== undefined ? value.Key :
        value.id !== undefined ? value.id :
        value.Id !== undefined ? value.Id :
        value.fieldId !== undefined ? value.fieldId :
        value.FieldId !== undefined ? value.FieldId :
        value.name !== undefined ? value.name :
        value.Name !== undefined ? value.Name :
        value.label !== undefined ? value.label :
        value.Label !== undefined ? value.Label :
        value.title !== undefined ? value.title :
        value.Title !== undefined ? value.Title : ''
      );
    }
    return String(value).trim();
  }

  collectRequiredFieldCandidates(value: any) {
    const candidates: string[] = [];
    const seen: any = {};
    const add = (candidate: any) => {
      const cleaned = this.normalizeExpressionText(candidate);
      if (cleaned && !seen[cleaned]) {
        seen[cleaned] = true;
        candidates.push(cleaned);
      }
    };
    const visit = (current: any, depth: number) => {
      if (current === undefined || current === null || depth > 3) {
        return;
      }
      if (typeof current === 'string' || typeof current === 'number' || typeof current === 'boolean') {
        add(current);
        return;
      }
      if (Array.isArray(current)) {
        current.forEach((item) => visit(item, depth + 1));
        return;
      }
      if (typeof current !== 'object') {
        return;
      }
      [
        'fieldId', 'FieldId', 'fieldName', 'FieldName',
        'variableId', 'VariableId', 'variableName', 'VariableName',
        'tempVariableId', 'TempVariableId', 'tempVariableName', 'TempVariableName',
        'tempVarId', 'TempVarId', 'id', 'Id', 'key', 'Key',
        'name', 'Name', 'code', 'Code', 'label', 'Label', 'title', 'Title',
        'path', 'Path', 'value', 'Value',
      ].forEach((key) => {
        if (current[key] !== undefined && current[key] !== null) {
          add(current[key]);
        }
      });
      ['target', 'Target', 'binding', 'Binding', 'field', 'Field', 'variable', 'Variable', 'tempVariable', 'TempVariable', 'data', 'Data', 'meta', 'Meta', 'metadata', 'Metadata'].forEach((key) => {
        if (current[key] !== undefined && current[key] !== null) {
          visit(current[key], depth + 1);
        }
      });
    };
    visit(value, 0);
    return candidates;
  }

  isLikelyWritableTargetName(candidate: any) {
    const value = this.normalizeExpressionText(candidate);
    if (!value || value === '[]' || value === '{}') {
      return false;
    }
    if (value.charAt(0) === '[' || value.charAt(0) === '{') {
      return false;
    }
    if (value.indexOf('":') !== -1 || value.indexOf('","') !== -1) {
      return false;
    }
    return value.length <= 160;
  }
}

class ApprovalDecisionPanel extends React.Component<any, any> {
  constructor(props: any) {
    super(props);
    const config = this.getConfigFromProps(props);
    const savedDecision = this.readCurrentTargetValue(config.decisionTarget);
    const savedComment = this.readCurrentTargetValue(config.commentTarget);
    const initialDecision = this.normalizeDecisionCode(savedDecision) || this.normalizeDecisionCode(config.defaultDecision);
    const initialComment = this.toCleanString(savedComment);
    this.state = {
      decision: initialDecision,
      comment: initialComment,
      validationMessage: this.getValidationMessage(initialDecision, initialComment, config),
    };
  }

  componentDidUpdate(previousProps: any) {
    if (this.getConfigKey(previousProps) !== this.getConfigKey(this.props)) {
      const config = this.getConfig();
      const savedDecision = this.readCurrentTargetValue(config.decisionTarget);
      const savedComment = this.readCurrentTargetValue(config.commentTarget);
      const nextDecision = this.normalizeDecisionCode(savedDecision) || this.normalizeDecisionCode(config.defaultDecision);
      const nextComment = this.toCleanString(savedComment);
      this.setState({
        decision: nextDecision,
        comment: nextComment,
        validationMessage: this.getValidationMessage(nextDecision, nextComment, config),
      });
    }
  }

  getConfig() {
    return this.getConfigFromProps(this.props);
  }

  getConfigFromProps(props: any) {
    const params = (props && props.params) || {};
    const decisionTarget = this.resolveWriteTargetFromProps(props, 'decisionField', params.decisionField);
    const commentTarget = this.resolveWriteTargetFromProps(props, 'commentField', params.commentField);
    const requireReject = this.toBoolean(params.requireCommentOnReject, true);
    const requireRevise = this.toBoolean(params.requireCommentOnRevise, true);
    return {
      decisionTarget: decisionTarget,
      commentTarget: commentTarget,
      titleText: this.toCleanString(params.titleText) || "审批决定",
      subtitleText: this.toCleanString(params.subtitleText) || "请选择处理决定，并按要求填写意见。",
      approveLabel: this.toCleanString(params.approveLabel) || "同意",
      rejectLabel: this.toCleanString(params.rejectLabel) || "拒绝",
      reviseLabel: this.toCleanString(params.reviseLabel) || "退回修改",
      defaultDecision: this.toCleanString(params.defaultDecision),
      requireCommentOnReject: requireReject,
      requireCommentOnRevise: requireRevise,
      showCommentBox: this.toBoolean(params.showCommentBox, true),
      commentPlaceholder: this.toCleanString(params.commentPlaceholder) || "请输入意见或理由",
      panelStyle: this.normalizePanelStyle(params.panelStyle),
      showStatusSummary: this.toBoolean(params.showStatusSummary, true),
      readonlyText: this.toCleanString(params.readonlyText) || "暂无已保存的审批决定。",
      validationMessageReject: this.toCleanString(params.validationMessageReject) || "请填写拒绝理由。",
      validationMessageRevise: this.toCleanString(params.validationMessageRevise) || "请填写退回修改理由。",
      options: this.buildDecisionOptions(params, requireReject, requireRevise),
    };
  }

  getConfigKey(props: any) {
    const params = (props && props.params) || {};
    const configuredTargets = (props && props.configuredOutputTargets) || {};
    try {
      return JSON.stringify({
        decisionField: this.toCleanString(configuredTargets.decisionField || params.decisionField),
        commentField: this.toCleanString(configuredTargets.commentField || params.commentField),
        defaultDecision: this.toCleanString(params.defaultDecision),
        decisionOptionsJson: this.toCleanString(this.resolveParameterValue(params.decisionOptionsJson)),
      });
    } catch (error) {
      return String(new Date().getTime());
    }
  }

  buildDecisionOptions(params: any, requireReject: boolean, requireRevise: boolean): DecisionOption[] {
    const configured = this.parseDecisionOptions(params.decisionOptionsJson);
    if (configured.length > 0) {
      return configured;
    }
    return [
      {
        code: 'approve',
        label: this.toCleanString(params.approveLabel) || "同意",
        tone: 'approve',
        helper: "同意申请进入后续流程。",
        requireComment: false,
      },
      {
        code: 'reject',
        label: this.toCleanString(params.rejectLabel) || "拒绝",
        tone: 'reject',
        helper: "拒绝本次申请，并说明理由。",
        requireComment: requireReject,
      },
      {
        code: 'revise',
        label: this.toCleanString(params.reviseLabel) || "退回修改",
        tone: 'revise',
        helper: "请申请人修改内容或补充资料。",
        requireComment: requireRevise,
      },
    ];
  }

  parseDecisionOptions(rawValue: any): DecisionOption[] {
    const resolved = this.resolveParameterValue(rawValue);
    const parsed = this.safeParseJson(resolved);
    const source = Array.isArray(parsed) ? parsed : Array.isArray(resolved) ? resolved : [];
    const result: DecisionOption[] = [];
    source.forEach((item: any, index: number) => {
      if (!item) {
        return;
      }
      if (typeof item === 'string') {
        const code = this.normalizeDecisionCode(item);
        if (code) {
          result.push({
            code: code,
            label: this.toTitleLabel(code),
            tone: this.normalizeTone(code),
            helper: '',
            requireComment: false,
          });
        }
        return;
      }
      if (typeof item === 'object') {
        const code = this.normalizeDecisionCode(this.firstDefined([item.code, item.value, item.key, item.id, item.name]));
        const label = this.toCleanString(this.firstDefined([item.label, item.title, item.text, item.name])) || this.toTitleLabel(code);
        if (!code || !label) {
          return;
        }
        result.push({
          code: code,
          label: label,
          tone: this.normalizeTone(this.toCleanString(this.firstDefined([item.tone, item.color, item.type])) || code),
          helper: this.toCleanString(this.firstDefined([item.helper, item.description, item.subtitle])),
          requireComment: this.toBoolean(this.firstDefined([item.requireComment, item.requiresComment, item.commentRequired]), false),
        });
      }
    });
    return this.dedupeOptions(result);
  }

  dedupeOptions(options: DecisionOption[]) {
    const seen: any = {};
    const result: DecisionOption[] = [];
    options.forEach((option) => {
      const code = this.normalizeDecisionCode(option && option.code);
      if (!code || seen[code]) {
        return;
      }
      seen[code] = true;
      result.push({
        code: code,
        label: this.toCleanString(option.label) || this.toTitleLabel(code),
        tone: this.normalizeTone(option.tone || code),
        helper: this.toCleanString(option.helper),
        requireComment: option.requireComment === true,
      });
    });
    return result;
  }

  handleDecisionClick(code: string) {
    if (this.props.readonly) {
      return;
    }
    const config = this.getConfig();
    const nextDecision = this.normalizeDecisionCode(code);
    const nextComment = this.state.comment || '';
    const validationMessage = this.getValidationMessage(nextDecision, nextComment, config);
    this.setState({
      decision: nextDecision,
      validationMessage: validationMessage,
    });
    this.persistOutputs(nextDecision, nextComment, config, validationMessage);
  }

  handleCommentChange(event: any) {
    if (this.props.readonly) {
      return;
    }
    const config = this.getConfig();
    const nextComment = event && event.target ? event.target.value : '';
    const validationMessage = this.getValidationMessage(this.state.decision, nextComment, config);
    this.setState({
      comment: nextComment,
      validationMessage: validationMessage,
    });
    this.persistOutputs(this.state.decision, nextComment, config, validationMessage);
  }

  persistOutputs(decision: string, comment: string, config: any, validationMessage: string) {
    if (config.commentTarget.candidates.length > 0) {
      this.writeTargetValue(config.commentTarget, comment || '');
    }
    if (decision && !validationMessage && config.decisionTarget.candidates.length > 0) {
      this.writeTargetValue(config.decisionTarget, decision);
    }
    if (decision && validationMessage && config.decisionTarget.candidates.length > 0) {
      this.writeTargetValue(config.decisionTarget, '');
    }
    if (!decision && config.decisionTarget.candidates.length > 0) {
      this.writeTargetValue(config.decisionTarget, '');
    }
  }

  getValidationMessage(decision: string, comment: string, config: any) {
    const option = this.findOption(decision, config.options);
    if (!option) {
      return '';
    }
    const needsComment = option.requireComment === true ||
      (option.code === 'reject' && config.requireCommentOnReject) ||
      (option.code === 'revise' && config.requireCommentOnRevise);
    if (needsComment && !this.toCleanString(comment)) {
      if (option.code === 'reject') {
        return config.validationMessageReject;
      }
      if (option.code === 'revise') {
        return config.validationMessageRevise;
      }
      return "请先填写此决定的处理意见。";
    }
    return '';
  }

  findOption(code: string, options: DecisionOption[]) {
    const normalized = this.normalizeDecisionCode(code);
    for (let index = 0; index < (options || []).length; index += 1) {
      if (options[index].code === normalized) {
        return options[index];
      }
    }
    return null;
  }

  readCurrentTargetValue(target: WriteTargetInfo) {
    const candidates = target && target.candidates ? target.candidates : [];
    for (let index = 0; index < candidates.length; index += 1) {
      const value = this.readCurrentValue(candidates[index]);
      if (value !== undefined && value !== null && this.toCleanString(value) !== '') {
        return value;
      }
    }
    return '';
  }

  readCurrentValue(fieldName: string): any {
    const context = this.props.context || {};
    const fieldsValues = this.props.fieldsValues || {};
    if (!fieldName) {
      return undefined;
    }
    if (fieldsValues && fieldsValues[fieldName] !== undefined) {
      return fieldsValues[fieldName];
    }
    if (context && typeof context.getFieldValue === 'function') {
      try {
        return context.getFieldValue(fieldName);
      } catch (error) {
        return undefined;
      }
    }
    return undefined;
  }

  writeTargetValue(target: WriteTargetInfo, value: any) {
    const candidates = target && target.candidates ? target.candidates : [];
    for (let index = 0; index < candidates.length; index += 1) {
      if (this.writeFieldValue(candidates[index], value)) {
        return true;
      }
    }
    return false;
  }

  writeFieldValue(fieldName: string, value: any) {
    const context = this.props.context || {};
    const fieldsValues = this.props.fieldsValues || {};
    if (!fieldName) {
      return false;
    }

    const writerNames = [
      'setFieldValue',
      'setFormFieldValue',
      'setFieldData',
      'setValue',
      'setVariableValue',
      'setVariable',
      'setTempVariableValue',
      'setTempVariable',
      'setTemporaryVariableValue',
      'setTemporaryVariable',
      'updateFieldValue',
      'updateVariableValue',
    ];
    const setterHosts = this.getSetterHosts(context);

    for (let hostIndex = 0; hostIndex < setterHosts.length; hostIndex += 1) {
      const host = setterHosts[hostIndex].host;
      for (let writerIndex = 0; writerIndex < writerNames.length; writerIndex += 1) {
        const writer = host && host[writerNames[writerIndex]];
        if (typeof writer === 'function') {
          try {
            const result = writer.call(host, fieldName, value);
            if (result !== false) {
              return true;
            }
          } catch (error) {
            /* 继续尝试其他可写入口。 */
          }
        }
      }
    }

    const objectSetterNames = ['setFieldsValue', 'setFieldValues', 'setValues', 'setVariables', 'setTempVariables'];
    for (let hostIndex = 0; hostIndex < setterHosts.length; hostIndex += 1) {
      const host = setterHosts[hostIndex].host;
      for (let writerIndex = 0; writerIndex < objectSetterNames.length; writerIndex += 1) {
        const writer = host && host[objectSetterNames[writerIndex]];
        if (typeof writer === 'function') {
          try {
            const payload: any = {};
            payload[fieldName] = value;
            const result = writer.call(host, payload);
            if (result !== false) {
              return true;
            }
          } catch (error) {
            /* 继续尝试其他可写入口。 */
          }
        }
      }
    }

    fieldsValues[fieldName] = value;
    return true;
  }

  getSetterHosts(context: any) {
    const hosts: any[] = [];
    const addHost = (name: string, host: any) => {
      if (host && hosts.filter((item) => item.host === host).length === 0) {
        hosts.push({ name: name, host: host });
      }
    };
    addHost('context', context);
    addHost('context.formContext', context && context.formContext);
    addHost('context.pageContext', context && context.pageContext);
    addHost('context.variableContext', context && context.variableContext);
    addHost('context.variables', context && context.variables);
    addHost('context.tempVariables', context && context.tempVariables);
    addHost('context.runtimeContext', context && context.runtimeContext);
    addHost('context.dataContext', context && context.dataContext);
    return hosts;
  }

  resolveWriteTargetFromProps(props: any, paramName: string, rawValue: any): WriteTargetInfo {
    const configuredTargets = (props && props.configuredOutputTargets) || {};
    const candidates = this.collectWritableTargetCandidates(configuredTargets[paramName] || rawValue)
      .filter((candidate) => this.isLikelyWritableTargetName(candidate));
    return {
      paramName: paramName,
      raw: rawValue,
      candidates: candidates,
      displayValue: candidates.length > 0 ? candidates[0] : this.toCleanString(rawValue),
    };
  }

  collectWritableTargetCandidates(rawValue: any) {
    const candidates: string[] = [];
    const seen: any = {};
    const addCandidate = (value: any) => {
      const cleaned = this.toCleanString(value);
      if (!cleaned || seen[cleaned]) {
        return;
      }
      seen[cleaned] = true;
      candidates.push(cleaned);
    };

    const visit = (value: any, depth: number) => {
      if (value === undefined || value === null || depth > 4) {
        return;
      }
      if (typeof value === 'string' || typeof value === 'number' || typeof value === 'boolean') {
        addCandidate(value);
        this.extractExpressionTokens(value).forEach(addCandidate);
        return;
      }
      if (Array.isArray(value)) {
        value.forEach((item) => visit(item, depth + 1));
        return;
      }
      if (typeof value !== 'object') {
        return;
      }
      [
        'fieldId', 'FieldId', 'fieldName', 'FieldName',
        'variableId', 'VariableId', 'variableName', 'VariableName',
        'tempVariableId', 'TempVariableId', 'tempVariableName', 'TempVariableName',
        'tempVarId', 'TempVarId', 'id', 'Id', 'key', 'Key',
        'name', 'Name', 'code', 'Code', 'label', 'Label', 'title', 'Title',
        'path', 'Path', 'value', 'Value',
      ].forEach((key) => {
        if (value[key] !== undefined && value[key] !== null) {
          addCandidate(value[key]);
          this.extractExpressionTokens(value[key]).forEach(addCandidate);
        }
      });
      ['target', 'Target', 'binding', 'Binding', 'field', 'Field', 'variable', 'Variable', 'tempVariable', 'TempVariable', 'expression', 'Expression', 'data', 'Data', 'meta', 'Meta', 'metadata', 'Metadata', 'props', 'Props'].forEach((key) => {
        if (value[key] !== undefined && value[key] !== null) {
          visit(value[key], depth + 1);
        }
      });
    };

    visit(rawValue, 0);
    return candidates;
  }

  extractExpressionTokens(value: any) {
    const text = this.toCleanString(value);
    if (!text) {
      return [];
    }
    const tokens: string[] = [];
    const patterns = [
      /\{\{\s*([A-Za-z0-9_.-]+)\s*\}\}/g,
      /@\{\s*([A-Za-z0-9_.-]+)\s*\}/g,
      /\$\{\s*([A-Za-z0-9_.-]+)\s*\}/g,
      /\b(?:TempVariables|tempVariables|Variables|variables|Fields|fields)\.([A-Za-z0-9_.-]+)\b/g,
    ];
    patterns.forEach((pattern) => {
      let match = pattern.exec(text);
      while (match) {
        if (match[1]) {
          tokens.push(match[1]);
          const parts = match[1].split('.');
          if (parts.length > 1) {
            tokens.push(parts[parts.length - 1]);
          }
        }
        match = pattern.exec(text);
      }
    });
    return tokens;
  }

  isLikelyWritableTargetName(candidate: any) {
    const value = this.toCleanString(candidate);
    if (!value || value === '[]' || value === '{}') {
      return false;
    }
    if (value.charAt(0) === '[' || value.charAt(0) === '{') {
      return false;
    }
    if (value.indexOf('":') !== -1 || value.indexOf('","') !== -1) {
      return false;
    }
    return value.length <= 160;
  }

  resolveParameterValue(value: any): any {
    if (value === undefined || value === null) {
      return '';
    }
    if (typeof value === 'string' || typeof value === 'number' || typeof value === 'boolean' || Array.isArray(value)) {
      return value;
    }
    if (typeof value === 'object') {
      const preferred = this.firstDefined([
        value.value,
        value.Value,
        value.data,
        value.Data,
        value.result,
        value.Result,
        value.key,
        value.Key,
        value.id,
        value.Id,
        value.fieldId,
        value.FieldId,
        value.fieldName,
        value.FieldName,
        value.label,
        value.Label,
        value.name,
        value.Name,
      ]);
      if (preferred !== undefined) {
        return this.resolveParameterValue(preferred);
      }
    }
    return value;
  }

  safeParseJson(value: any): any {
    if (Array.isArray(value) || (value && typeof value === 'object')) {
      return value;
    }
    const text = this.toCleanString(value);
    if (!text) {
      return null;
    }
    try {
      return JSON.parse(text);
    } catch (error) {
      return null;
    }
  }

  toCleanString(value: any): string {
    const resolved = this.resolveParameterValue(value);
    if (resolved === undefined || resolved === null) {
      return '';
    }
    if (typeof resolved === 'string' || typeof resolved === 'number' || typeof resolved === 'boolean') {
      return String(resolved).trim();
    }
    if (Array.isArray(resolved)) {
      return resolved.map((item) => this.toCleanString(item)).filter(Boolean).join(', ');
    }
    if (typeof resolved === 'object') {
      const preferred = this.firstDefined([
        resolved.value,
        resolved.Value,
        resolved.text,
        resolved.Text,
        resolved.display,
        resolved.Display,
        resolved.label,
        resolved.Label,
        resolved.name,
        resolved.Name,
        resolved.title,
        resolved.Title,
        resolved.key,
        resolved.Key,
      ]);
      if (preferred !== undefined) {
        return this.toCleanString(preferred);
      }
      try {
        return JSON.stringify(resolved);
      } catch (error) {
        return '';
      }
    }
    return '';
  }

  toBoolean(value: any, fallback: boolean) {
    if (value === true || value === false) {
      return value;
    }
    if (Array.isArray(value) && value.length > 0) {
      return this.toBoolean(value[0], fallback);
    }
    if (value && typeof value === 'object') {
      const objectValue = this.firstDefined([
        value.value,
        value.Value,
        value.key,
        value.Key,
        value.checked,
        value.Checked,
        value.selected,
        value.Selected,
        value.label,
        value.Label,
      ]);
      if (objectValue !== undefined) {
        return this.toBoolean(objectValue, fallback);
      }
    }
    const cleaned = this.toCleanString(value).toLowerCase();
    if (['true', '1', 'yes', 'y', 'on'].indexOf(cleaned) !== -1) {
      return true;
    }
    if (['false', '0', 'no', 'n', 'off'].indexOf(cleaned) !== -1) {
      return false;
    }
    return fallback;
  }

  normalizeDecisionCode(value: any) {
    const text = this.toCleanString(value).toLowerCase();
    if (!text) {
      return '';
    }
    if (text === 'request changes' || text === 'requestchanges' || text === 'request_change' || text === 'request-change' || text === 'revise' || text === 'revision') {
      return 'revise';
    }
    if (text === 'approved') {
      return 'approve';
    }
    if (text === 'rejected') {
      return 'reject';
    }
    return text.replace(/\s+/g, '-');
  }

  normalizeTone(value: any) {
    const text = this.toCleanString(value).toLowerCase();
    if (text === 'approve' || text === 'approved' || text === 'success' || text === 'green') {
      return 'approve';
    }
    if (text === 'reject' || text === 'rejected' || text === 'danger' || text === 'red') {
      return 'reject';
    }
    if (text === 'revise' || text === 'request-changes' || text === 'warning' || text === 'amber') {
      return 'revise';
    }
    if (text === 'escalate' || text === 'violet' || text === 'purple') {
      return 'escalate';
    }
    if (text === 'hold' || text === 'neutral' || text === 'gray' || text === 'grey') {
      return 'hold';
    }
    return 'neutral';
  }

  normalizePanelStyle(value: any) {
    const text = this.toCleanString(value).toLowerCase();
    if (text === 'compact' || text === 'bordered') {
      return text;
    }
    return 'standard';
  }

  toTitleLabel(value: string) {
    const text = this.toCleanString(value).replace(/[-_]+/g, ' ');
    return text.replace(/\w\S*/g, (word) => word.charAt(0).toUpperCase() + word.substr(1).toLowerCase());
  }

  firstDefined(values: any[]) {
    for (let index = 0; index < values.length; index += 1) {
      if (values[index] !== undefined && values[index] !== null) {
        return values[index];
      }
    }
    return undefined;
  }

  render() {
    const config = this.getConfig();
    const selectedOption = this.findOption(this.state.decision, config.options);
    const readonly = this.props.readonly === true;
    const panelClass = 'adp-panel adp-panel-' + config.panelStyle + (readonly ? ' adp-readonly' : '');
    return (
      <div className={panelClass}>
        <style>{this.getStyles()}</style>
        <div className="adp-header">
          <div>
            <div className="adp-title">{config.titleText}</div>
            {config.subtitleText ? <div className="adp-subtitle">{config.subtitleText}</div> : null}
          </div>
          {readonly ? <span className="adp-mode-badge">只读</span> : null}
        </div>

        {readonly ? (
          <div className="adp-readonly-box">
            {selectedOption ? (
              <div className={'adp-readonly-decision adp-tone-' + selectedOption.tone}>
                <span className="adp-readonly-dot" />
                <span>{selectedOption.label}</span>
              </div>
            ) : (
              <div className="adp-readonly-empty">{config.readonlyText}</div>
            )}
            {this.state.comment ? <div className="adp-readonly-comment">{this.state.comment}</div> : null}
          </div>
        ) : (
          <div>
            <div className="adp-options" role="group" aria-label="审批决定">
              {config.options.map((option: DecisionOption) => this.renderDecisionButton(option, selectedOption))}
            </div>

            {config.showCommentBox ? (
              <div className="adp-comment-wrap">
                <textarea
                  className="adp-comment"
                  value={this.state.comment || ''}
                  placeholder={config.commentPlaceholder}
                  aria-label="审批意见"
                  onChange={(event) => this.handleCommentChange(event)}
                />
              </div>
            ) : null}

            {this.state.validationMessage ? (
              <div className="adp-validation" role="status">{this.state.validationMessage}</div>
            ) : null}

            {config.showStatusSummary ? this.renderStatusSummary(selectedOption, config) : null}
          </div>
        )}
      </div>
    );
  }

  renderDecisionButton(option: DecisionOption, selectedOption: DecisionOption | null) {
    const isSelected = Boolean(selectedOption && selectedOption.code === option.code);
    const className = 'adp-option adp-tone-' + option.tone + (isSelected ? ' adp-option-selected' : '');
    return (
      <button
        type="button"
        className={className}
        key={option.code}
        aria-pressed={isSelected}
        onClick={() => this.handleDecisionClick(option.code)}
      >
        <span className="adp-option-main">
          <span className="adp-option-mark" />
          <span className="adp-option-label">{option.label}</span>
        </span>
        {option.helper ? <span className="adp-option-helper">{option.helper}</span> : null}
      </button>
    );
  }

  renderStatusSummary(selectedOption: DecisionOption | null, config: any) {
    if (!selectedOption) {
      return <div className="adp-summary">尚未选择决定。</div>;
    }
    const commentRequired = this.getValidationMessage(selectedOption.code, '', config) !== '';
    return (
      <div className="adp-summary">
        当前决定： <strong>{selectedOption.label}</strong>
        {commentRequired ? <span> 需填写意见</span> : null}
      </div>
    );
  }

  getStyles() {
    return [
      '.adp-panel{box-sizing:border-box;width:100%;min-width:0;background:#fff;border:1px solid #e5eaf3;border-radius:8px;padding:18px;color:#111827;font-family:Inter,-apple-system,BlinkMacSystemFont,"Segoe UI",sans-serif;box-shadow:0 1px 2px rgba(16,24,40,.04);}',
      '.adp-panel *{box-sizing:border-box;}',
      '.adp-panel-compact{padding:14px;}',
      '.adp-panel-bordered{border-color:#cbd7ea;box-shadow:none;}',
      '.adp-readonly{background:#f8fafc;}',
      '.adp-header{display:flex;align-items:flex-start;justify-content:space-between;gap:14px;margin-bottom:14px;}',
      '.adp-title{font-size:16px;line-height:22px;font-weight:700;color:#101828;}',
      '.adp-subtitle{margin-top:3px;font-size:12px;line-height:18px;color:#667085;}',
      '.adp-mode-badge{flex:0 0 auto;font-size:12px;line-height:18px;color:#667085;background:#eef2f7;border:1px solid #d8deea;border-radius:999px;padding:3px 9px;}',
      '.adp-options{display:grid;grid-template-columns:repeat(3,minmax(0,1fr));gap:10px;}',
      '.adp-option{appearance:none;border:1px solid #d8deea;background:#fff;border-radius:8px;padding:12px 12px;text-align:left;cursor:pointer;color:#344054;min-height:72px;transition:border-color .15s ease,background-color .15s ease,box-shadow .15s ease;}',
      '.adp-option:hover{border-color:#b8c7dc;background:#fbfcfe;}',
      '.adp-option-selected{box-shadow:0 0 0 3px rgba(20,111,246,.12);}',
      '.adp-option-main{display:flex;align-items:center;gap:8px;}',
      '.adp-option-mark{width:14px;height:14px;border-radius:999px;border:2px solid #b8c7dc;background:#fff;flex:0 0 auto;}',
      '.adp-option-selected .adp-option-mark{border-width:4px;}',
      '.adp-option-label{font-size:13px;line-height:18px;font-weight:700;color:#101828;}',
      '.adp-option-helper{display:block;margin-top:6px;font-size:12px;line-height:17px;color:#667085;}',
      '.adp-tone-approve.adp-option-selected{border-color:#12b76a;background:#f2fbf6;}',
      '.adp-tone-approve.adp-option-selected .adp-option-mark{border-color:#12b76a;}',
      '.adp-tone-reject.adp-option-selected{border-color:#e45665;background:#fff7f7;}',
      '.adp-tone-reject.adp-option-selected .adp-option-mark{border-color:#e45665;}',
      '.adp-tone-revise.adp-option-selected{border-color:#f5a623;background:#fffbf2;}',
      '.adp-tone-revise.adp-option-selected .adp-option-mark{border-color:#f5a623;}',
      '.adp-tone-escalate.adp-option-selected{border-color:#7a5af8;background:#f7f5ff;}',
      '.adp-tone-escalate.adp-option-selected .adp-option-mark{border-color:#7a5af8;}',
      '.adp-tone-hold.adp-option-selected,.adp-tone-neutral.adp-option-selected{border-color:#667085;background:#f8fafc;}',
      '.adp-tone-hold.adp-option-selected .adp-option-mark,.adp-tone-neutral.adp-option-selected .adp-option-mark{border-color:#667085;}',
      '.adp-comment-wrap{margin-top:12px;}',
      '.adp-comment{display:block;width:100%;min-height:92px;resize:vertical;border:1px solid #d8deea;border-radius:8px;padding:10px 12px;color:#101828;background:#fff;font-size:13px;line-height:20px;font-family:inherit;outline:none;}',
      '.adp-comment:focus{border-color:#146ff6;box-shadow:0 0 0 3px rgba(20,111,246,.12);}',
      '.adp-validation{margin-top:10px;border:1px solid #fecdca;background:#fff7f6;color:#b42318;border-radius:8px;padding:9px 10px;font-size:12px;line-height:18px;}',
      '.adp-summary{margin-top:10px;font-size:12px;line-height:18px;color:#667085;background:#f8fafc;border:1px solid #e5eaf3;border-radius:8px;padding:9px 10px;}',
      '.adp-summary strong{color:#101828;}',
      '.adp-readonly-box{border:1px solid #e5eaf3;border-radius:8px;background:#fff;padding:12px;}',
      '.adp-readonly-decision{display:inline-flex;align-items:center;gap:8px;border-radius:999px;padding:5px 10px;font-size:13px;line-height:18px;font-weight:700;background:#f8fafc;color:#344054;}',
      '.adp-readonly-dot{width:8px;height:8px;border-radius:999px;background:#667085;}',
      '.adp-readonly-decision.adp-tone-approve{background:#f2fbf6;color:#067647;}',
      '.adp-readonly-decision.adp-tone-approve .adp-readonly-dot{background:#12b76a;}',
      '.adp-readonly-decision.adp-tone-reject{background:#fff7f7;color:#b42318;}',
      '.adp-readonly-decision.adp-tone-reject .adp-readonly-dot{background:#e45665;}',
      '.adp-readonly-decision.adp-tone-revise{background:#fffbf2;color:#b54708;}',
      '.adp-readonly-decision.adp-tone-revise .adp-readonly-dot{background:#f5a623;}',
      '.adp-readonly-empty{font-size:13px;line-height:20px;color:#667085;}',
      '.adp-readonly-comment{margin-top:10px;padding-top:10px;border-top:1px solid #eef2f7;font-size:13px;line-height:20px;color:#344054;white-space:pre-wrap;}',
      '@media(max-width:640px){.adp-panel{padding:16px;}.adp-header{flex-direction:column;align-items:flex-start;}.adp-options{grid-template-columns:1fr;}.adp-option{min-height:auto;}}',
    ].join('');
  }
}
