import type { FillFieldType } from '../domain/types';

export function inspectFieldsCommand(): string {
  function errorMessage(error: unknown): string {
    return error instanceof Error ? error.message : String(error);
  }

  function textOf(control: any): string {
    return String(control.GetRange().GetText() ?? '');
  }

  function kindOf(control: any): FillFieldType {
    if (control.IsCheckBox?.()) return 'checkbox';
    if (control.IsComboBox?.() || control.IsDropDownList?.()) return 'select';
    if (control.IsDatePicker?.()) return 'date';
    if (control.IsBlockLevel?.()) return 'multiline';
    return 'text';
  }

  function optionsOf(control: any): Array<{ display: string; value: string }> {
    const list = control.GetDropdownList?.();
    if (!list?.GetElementsCount || !list.GetItem) return [];
    const options = [];
    for (let index = 0; index < list.GetElementsCount(); index += 1) {
      const item = list.GetItem(index);
      options.push({ display: String(item.GetText()), value: String(item.GetValue()) });
    }
    return options;
  }

  function readValue(control: any, type: FillFieldType): boolean | string {
    const text = textOf(control);
    if (type === 'checkbox') return ['☒', '☑'].includes(text);
    if (type === 'select') return optionsOf(control).find((item) => item.display === text)?.value ?? '';
    return text;
  }

  try {
    const controls = Api.GetDocument().GetAllContentControls();
    const fields = controls.filter((control: any) => /^eln\.(field\.[^.]+|repeat\.[^.]+\.[^.]+\.[^.]+)$/u.test(control.GetTag())).map((control: any) => {
      const type = kindOf(control);
      return {
        alias: String(control.GetAlias?.() ?? ''),
        currentValue: readValue(control, type),
        internalId: String(control.GetInternalId?.() ?? ''),
        options: type === 'select' ? optionsOf(control) : [],
        tag: String(control.GetTag()),
        type,
      };
    });
    const tags = fields.map((field: any) => field.tag);
    if (new Set(tags).size !== tags.length) throw new Error('文档存在重复字段 Tag');
    return JSON.stringify({ ok: true, fields });
  } catch (error) {
    return JSON.stringify({ ok: false, error: errorMessage(error), fields: [] });
  }
}

export function applyFieldsCommand(): string {
  function errorMessage(error: unknown): string {
    return error instanceof Error ? error.message : String(error);
  }

  function textOf(control: any): string {
    return String(control.GetRange().GetText() ?? '');
  }

  function kindOf(control: any): FillFieldType {
    if (control.IsCheckBox?.()) return 'checkbox';
    if (control.IsComboBox?.() || control.IsDropDownList?.()) return 'select';
    if (control.IsDatePicker?.()) return 'date';
    if (control.IsBlockLevel?.()) return 'multiline';
    return 'text';
  }

  function optionsOf(control: any): Array<{ display: string; value: string }> {
    const list = control.GetDropdownList?.();
    if (!list?.GetElementsCount || !list.GetItem) return [];
    const options = [];
    for (let index = 0; index < list.GetElementsCount(); index += 1) {
      const item = list.GetItem(index);
      options.push({ display: String(item.GetText()), value: String(item.GetValue()) });
    }
    return options;
  }

  function readValue(control: any, type: FillFieldType): boolean | string {
    const text = textOf(control);
    if (type === 'checkbox') return ['☒', '☑'].includes(text);
    if (type === 'select') return optionsOf(control).find((item) => item.display === text)?.value ?? '';
    return text;
  }

  function writeValue(control: any, type: FillFieldType, value: boolean | string): void {
    if (type === 'checkbox') {
      control.SetCheckBoxChecked(Boolean(value));
      return;
    }
    if (type === 'select') {
      if (!optionsOf(control).some((item) => item.value === value)) throw new Error('下拉值不在文档选项中');
      control.SelectListItem(value);
      return;
    }
    if (type === 'date') {
      const parts = String(value).split('-').map(Number);
      if (parts.length !== 3 || parts.some((part) => !Number.isInteger(part))) throw new Error('日期格式无效');
      control.SetDate(new Date(parts[0]!, parts[1]! - 1, parts[2]!));
      return;
    }
    control.RemoveAllElements();
    if (type === 'multiline') {
      String(value).split('\n').forEach((line, index) => {
        const paragraph = Api.CreateParagraph();
        const run = Api.CreateRun();
        run.AddText(line);
        paragraph.AddElement(run);
        control.AddElement(paragraph, index);
      });
      return;
    }
    const run = Api.CreateRun();
    run.AddText(String(value));
    control.AddElement(run, 0);
  }

  const changes = Asc.scope.elnTemplateFillChanges;
  if (!Array.isArray(changes)) return JSON.stringify({ ok: false, error: '缺少字段修改', results: [] });
  const controls = Api.GetDocument().GetAllContentControls();
  const results = changes.map((change) => {
    const matches = controls.filter((control: any) => control.GetTag() === change.tag);
    if (matches.length !== 1) return { tag: change.tag, ok: false, error: '无法按 Tag 定位唯一控件' };
    const control = matches[0];
    const actualType = kindOf(control);
    if (actualType !== change.type) return { tag: change.tag, ok: false, error: '字段类型与文档控件不一致' };
    const before = readValue(control, actualType);
    try {
      writeValue(control, actualType, change.value);
      if (readValue(control, actualType) !== change.value) throw new Error('字段写入后回读不一致');
      return { tag: change.tag, ok: true };
    } catch (error) {
      try { writeValue(control, actualType, before); } catch { /* 原生撤销是最终恢复手段。 */ }
      return { tag: change.tag, ok: false, error: errorMessage(error) };
    }
  });
  return JSON.stringify({ ok: true, results });
}
