import { countTag } from '../domain/field-types';
import { parseTag } from '../domain/tags';
import type { FieldDefinition, TemplateBinding, TemplateControlSnapshot } from '../domain/types';
import type { FormulaResultLockSnapshot } from '../domain/formula-mapping';
import {
  disableTrackRevisionsCommand,
  insertTypedControlCommand,
  listContentControlsCommand,
  readControlTextCommand,
  readTrackRevisionsStateCommand,
  setFormulaResultLockCommand,
  updateControlSpecCommand,
} from './commands';
import type { AscGlobal, OfficeContentControl, OnlyOfficeCommand } from './types';

interface CommandResult {
  controls?: OfficeContentControl[];
  error?: string;
  hasRevisionChanges?: boolean;
  ok: boolean;
  snapshot?: unknown;
  text?: string;
  trackRevisions?: boolean;
}

interface LifecycleCallbacks {
  onReady: () => void;
  onThemeChanged: (theme: unknown) => void;
}

interface LifecycleBinding {
  callbacks: LifecycleCallbacks;
}

interface LifecycleState {
  bindings: LifecycleBinding[];
  button: NonNullable<AscGlobal['plugin']['button']>;
  init: NonNullable<AscGlobal['plugin']['init']>;
  onThemeChanged: NonNullable<AscGlobal['plugin']['onThemeChanged']>;
  originalButton: AscGlobal['plugin']['button'];
  originalInit: AscGlobal['plugin']['init'];
  originalThemeChanged: AscGlobal['plugin']['onThemeChanged'];
}

export interface SnapshotControlOptions {
  inheritedFormulaLockBindingIds?: ReadonlySet<string>;
}

export interface OnlyOfficeBridge {
  applyControlPlan(bindings: TemplateBinding[], desired: TemplateControlSnapshot[], signal?: AbortSignal): Promise<void>;
  bindLifecycle(callbacks: LifecycleCallbacks): () => void;
  close(): void;
  deleteControl(internalId: string): Promise<OfficeContentControl[]>;
  disableTrackRevisions(): Promise<void>;
  getRuntimeOptions(): unknown;
  insertBinding(binding: TemplateBinding, signal?: AbortSignal): Promise<void>;
  insertReadOnlyBinding(binding: TemplateBinding, signal?: AbortSignal): Promise<void>;
  insertField(definition: FieldDefinition): Promise<OfficeContentControl[]>;
  insertReference(binding: TemplateBinding, primaryTag: string, signal?: AbortSignal): Promise<void>;
  listControls(): Promise<OfficeContentControl[]>;
  lockFormulaResult(primaryTag: string): Promise<FormulaResultLockSnapshot>;
  readSelectedText(): Promise<string>;
  unlockFormulaResult(primaryTag: string): Promise<void>;
  restoreFormulaResultLocks(snapshot: FormulaResultLockSnapshot): Promise<void>;
  isFormulaResultLocked(primaryTag: string): Promise<boolean>;
  selectControl(internalId: string, signal?: AbortSignal): Promise<void>;
  snapshotControls(bindings: TemplateBinding[], controls: OfficeContentControl[], options?: SnapshotControlOptions): TemplateControlSnapshot[];
}

function isFormulaLockSnapshot(value: unknown): value is FormulaResultLockSnapshot {
  return (
    typeof value === 'object' &&
    value !== null &&
    'primaryTag' in value &&
    typeof value.primaryTag === 'string' &&
    'locks' in value &&
    Array.isArray(value.locks) &&
    value.locks.every(
      (item) => typeof item === 'object' && item !== null && 'tag' in item && typeof item.tag === 'string' && 'lock' in item && typeof item.lock === 'string',
    )
  );
}

const lifecycleStates = new WeakMap<AscGlobal, LifecycleState>();

function debugBridge(event: string, details: Record<string, unknown>): void {
  if (import.meta.env.DEV && import.meta.env.MODE !== 'test') {
    console.info('[ELN annotator]', new Date().toISOString(), event, details);
  }
}

function toError(error: unknown): Error {
  return error instanceof Error ? error : new Error(String(error));
}

function normalizeOptions(value: unknown): Array<{ display: string; value: string }> {
  if (!Array.isArray(value)) return [];
  return value.map((item) => {
    if (typeof item === 'object' && item && 'display' in item && 'value' in item) {
      return { display: String(item.display), value: String(item.value) };
    }
    return { display: String(item), value: String(item) };
  });
}

function normalizeControlConfig(controlType: string, controlRole: string, value: null | Record<string, unknown> | undefined): Record<string, unknown> {
  const config = value ?? {};
  const placeholder = typeof config.placeholder === 'string' ? config.placeholder : '';
  switch (controlType.toUpperCase()) {
    case 'DROPDOWN':
      return {
        options: normalizeOptions(config.options),
        placeholder,
        ...(controlRole === 'PRIMARY' ? { defaultValue: typeof config.defaultValue === 'string' ? config.defaultValue : '' } : {}),
      };
    case 'CHECKBOX':
      return { defaultChecked: Boolean(config.defaultChecked) };
    case 'DATE':
      return { dateFormat: typeof config.dateFormat === 'string' && config.dateFormat ? config.dateFormat : 'yyyy-MM-dd', placeholder };
    case 'MULTILINE':
    case 'TEXT':
      return { placeholder };
    default:
      return config;
  }
}

function normalizeExistingControlConfig(controlType: string, controlRole: string, value: null | Record<string, unknown> | undefined): Record<string, unknown> {
  if (controlType.toUpperCase() === 'CHECKBOX') return {};
  return normalizeControlConfig(controlType, controlRole, value);
}

function bindingConfigForSnapshot(binding: TemplateBinding): null | Record<string, unknown> {
  const config = binding.controlConfig ?? {};
  const placeholder = typeof config.placeholder === 'string' ? config.placeholder : '';
  if (binding.controlType !== 'CHECKBOX' && !placeholder.trim()) {
    return { ...config, placeholder: binding.controlAlias || binding.fieldCode };
  }
  return binding.controlConfig;
}

function stableValue(value: unknown): unknown {
  if (Array.isArray(value)) return value.map(stableValue);
  if (value && typeof value === 'object') {
    return Object.fromEntries(
      Object.entries(value)
        .sort(([left], [right]) => left.localeCompare(right))
        .map(([key, item]) => [key, stableValue(item)]),
    );
  }
  return value;
}

function configsEqual(left: Record<string, unknown>, right: Record<string, unknown>): boolean {
  return JSON.stringify(stableValue(left)) === JSON.stringify(stableValue(right));
}

function snapshotControlType(bindingType: string, actualType: string): string {
  const expected = bindingType.toUpperCase();
  const actual = actualType.toUpperCase();
  return expected === 'MULTILINE' && actual === 'TEXT' ? expected : actual;
}

function bindingLock(binding: TemplateBinding, role = binding.controlRole): 'contentLocked' | 'sdtContentLocked' | 'unlocked' {
  if (role === 'REFERENCE') return 'contentLocked';
  return binding.readOnly === true && binding.valueSource === 'CROSS_TEMPLATE' ? 'sdtContentLocked' : 'unlocked';
}

function controlMatchesTarget(binding: TemplateBinding, target: TemplateControlSnapshot, control: OfficeContentControl): boolean {
  if (
    typeof control.Alias !== 'string' ||
    !control.ControlConfig ||
    typeof control.ControlRole !== 'string' ||
    typeof control.ControlType !== 'string' ||
    typeof control.Lock !== 'string'
  )
    return false;
  const role = target.controlRole || binding.controlRole;
  const type = (target.controlType || binding.controlType).toUpperCase();
  const alias = target.controlAlias || binding.controlAlias || binding.fieldCode;
  const expectedConfig = normalizeExistingControlConfig(type, role, target.controlConfig ?? binding.controlConfig);
  const actualConfig = normalizeExistingControlConfig(control.ControlType, control.ControlRole, control.ControlConfig);
  return (
    control.Alias === alias &&
    control.ControlRole === role &&
    control.ControlType.toUpperCase() === type &&
    control.Lock === bindingLock(binding, role) &&
    configsEqual(actualConfig, expectedConfig)
  );
}

function isCommandResult(value: unknown): value is CommandResult {
  return typeof value === 'object' && value !== null && !Array.isArray(value) && 'ok' in value && typeof value.ok === 'boolean';
}

function bindLifecycleHandlers(asc: AscGlobal, callbacks: LifecycleCallbacks): () => void {
  let state = lifecycleStates.get(asc);

  if (!state) {
    const bindings: LifecycleBinding[] = [];
    const currentBinding = () => bindings[bindings.length - 1];
    state = {
      bindings,
      originalInit: asc.plugin.init,
      originalButton: asc.plugin.button,
      originalThemeChanged: asc.plugin.onThemeChanged,
      init: () => currentBinding()?.callbacks.onReady(),
      button: (id) => {
        if (id === -1 && currentBinding()) {
          asc.plugin.executeCommand('close', '');
        }
      },
      onThemeChanged: (theme) => {
        if (currentBinding()) {
          asc.plugin.onThemeChangedBase(theme);
          currentBinding()?.callbacks.onThemeChanged(theme);
        }
      },
    };
    lifecycleStates.set(asc, state);
    asc.plugin.init = state.init;
    asc.plugin.button = state.button;
    asc.plugin.onThemeChanged = state.onThemeChanged;
  }

  const binding = { callbacks };
  state.bindings.push(binding);
  let active = true;

  return () => {
    if (!active) {
      return;
    }
    active = false;
    const index = state.bindings.indexOf(binding);
    if (index >= 0) {
      state.bindings.splice(index, 1);
    }
    if (state.bindings.length > 0) {
      return;
    }

    if (asc.plugin.init === state.init) {
      asc.plugin.init = state.originalInit;
    }
    if (asc.plugin.button === state.button) {
      asc.plugin.button = state.originalButton;
    }
    if (asc.plugin.onThemeChanged === state.onThemeChanged) {
      asc.plugin.onThemeChanged = state.originalThemeChanged;
    }
    if (lifecycleStates.get(asc) === state) {
      lifecycleStates.delete(asc);
    }
  };
}

export function createOnlyOfficeBridge(asc: AscGlobal): OnlyOfficeBridge {
  let verifyNativeControlTag = '';
  let preferNativeControls = false;
  const insertedTextDefinitions = new Map<string, FieldDefinition>();

  function executeMethod(name: string, args: unknown[] = []): Promise<unknown> {
    return new Promise((resolve, reject) => {
      try {
        debugBridge('method:start', { args, methodName: name });
        asc.plugin.executeMethod(name, args, (result) => {
          debugBridge('method:callback', { methodName: name, result });
          resolve(result);
        });
      } catch (error: unknown) {
        debugBridge('method:threw', { error: toError(error).message, methodName: name });
        reject(toError(error));
      }
    });
  }

  function executeCommand(command: OnlyOfficeCommand, scopeKey: string, payload: unknown, allowMissingResult = false): Promise<CommandResult> {
    return new Promise((resolve, reject) => {
      const commandName = command.name || scopeKey;
      const clearScope = () => {
        if (asc.scope[scopeKey] === payload) {
          delete asc.scope[scopeKey];
        }
      };

      try {
        asc.scope[scopeKey] = payload;
        debugBridge('command:start', { allowMissingResult, commandName, payload, scopeKey });
        asc.plugin.callCommand(command, false, true, (raw) => {
          try {
            debugBridge('command:callback', { commandName, raw, scopeKey });
            if (raw === undefined && allowMissingResult) {
              debugBridge('command:missing-callback-accepted', { commandName, scopeKey });
              resolve({ ok: true });
              return;
            }
            const result: unknown = typeof raw === 'string' ? JSON.parse(raw) : raw;

            if (!isCommandResult(result)) {
              debugBridge('command:invalid-result', { commandName, raw, scopeKey });
              reject(new Error('编辑器命令返回无效结果'));
              return;
            }

            if (!result.ok) {
              debugBridge('command:rejected', { commandName, error: result.error, scopeKey });
              reject(new Error(typeof result.error === 'string' ? result.error : '编辑器命令执行失败'));
              return;
            }

            debugBridge('command:success', { commandName, scopeKey });
            resolve(result);
          } catch {
            debugBridge('command:invalid-result', { commandName, raw, scopeKey });
            reject(new Error('编辑器命令返回无效结果'));
          } finally {
            clearScope();
          }
        });
      } catch (error: unknown) {
        clearScope();
        debugBridge('command:threw', { commandName, error: toError(error).message, scopeKey });
        reject(toError(error));
      }
    });
  }

  async function readControls(errorMessage: string): Promise<OfficeContentControl[]> {
    const result = await executeCommand(listContentControlsCommand, 'elnTemplateList', {});

    if (!Array.isArray(result.controls)) {
      throw new Error(errorMessage);
    }

    const targetTag = verifyNativeControlTag;
    const commandTargetCount = targetTag ? countTag(result.controls, targetTag) : 0;
    const shouldReadNative = preferNativeControls || Boolean(targetTag && commandTargetCount === 0);
    verifyNativeControlTag = '';
    if (!shouldReadNative) return result.controls;

    const rawNative = await executeMethod('GetAllContentControls');
    if (!Array.isArray(rawNative)) throw new Error(errorMessage);
    const native = rawNative.map((item) => nativeControl(item)).filter((control): control is OfficeContentControl => control !== null);
    const nativeTargetCount = targetTag ? countTag(native, targetTag) : 0;
    if (targetTag && commandTargetCount === 0 && nativeTargetCount > 0) preferNativeControls = true;

    return preferNativeControls ? mergeNativeControls(native, result.controls) : result.controls;
  }

  function nativeLock(value: unknown): string | undefined {
    if (typeof value === 'string') return value;
    if (value === 0) return 'contentLocked';
    if (value === 1) return 'sdtContentLocked';
    if (value === 2) return 'sdtLocked';
    if (value === 3) return 'unlocked';
    return undefined;
  }

  function nativeControl(value: unknown): null | OfficeContentControl {
    if (!value || typeof value !== 'object') return null;
    const item = value as Record<string, unknown>;
    const tag = typeof item.Tag === 'string' ? item.Tag : '';
    const definition = insertedTextDefinitions.get(tag);
    const controlRole = tag.startsWith('eln.ref.')
      ? 'REFERENCE'
      : tag.startsWith('eln.field.')
        ? 'PRIMARY'
        : tag.startsWith('eln.repeat.')
          ? 'REPEAT'
          : 'UNKNOWN';
    return {
      Alias: typeof item.Alias === 'string' ? item.Alias : undefined,
      ControlConfig: definition ? { placeholder: definition.placeholder } : undefined,
      ControlRole: definition ? controlRole : undefined,
      ControlType: definition ? 'text' : undefined,
      InternalId: typeof item.InternalId === 'string' ? item.InternalId : undefined,
      Lock: nativeLock(item.Lock),
      Tag: tag,
    };
  }

  function mergeNativeControls(native: OfficeContentControl[], detailed: OfficeContentControl[]): OfficeContentControl[] {
    return native.map((control) => {
      const match = detailed.find((item) => (control.InternalId && item.InternalId === control.InternalId) || (control.Tag && item.Tag === control.Tag));
      if (!match) return control;
      return {
        ...match,
        ...control,
        Alias: control.Alias ?? match.Alias,
        ControlConfig: match.ControlConfig ?? control.ControlConfig,
        ControlRole: match.ControlRole ?? control.ControlRole,
        ControlType: control.Tag && insertedTextDefinitions.has(control.Tag) ? control.ControlType : (match.ControlType ?? control.ControlType),
        InternalId: control.InternalId ?? match.InternalId,
        Lock: control.Lock ?? match.Lock,
        Tag: control.Tag || match.Tag,
      };
    });
  }

  async function writeTextDefaultWithNativeControl(tag: string, value: string): Promise<void> {
    const rawControls = await executeMethod('GetAllContentControls');
    if (!Array.isArray(rawControls)) throw new Error('插入后无法读取文本控件');
    const targets = rawControls.map((item) => nativeControl(item)).filter((control): control is OfficeContentControl => control?.Tag === tag);
    if (targets.length !== 1) throw new Error('插入后 Tag 计数不是 1');
    const target = targets[0]!;
    if (!target?.InternalId) throw new Error('插入后无法定位文本控件');

    const moved = await executeMethod('MoveCursorToContentControl', [target.InternalId, true]);
    if (moved === false) throw new Error('插入后无法定位文本控件');
    const input = await executeMethod('InputText', [value]);
    if (input === false) throw new Error('文本默认值写入失败');
    const rawCurrent = await executeMethod('GetCurrentContentControlPr', ['text']);
    const current = rawCurrent && typeof rawCurrent === 'object' ? (rawCurrent as Record<string, unknown>) : null;
    if (current?.InternalId !== target.InternalId || current.Tag !== tag) throw new Error('文本默认值写入后控件定位不一致');
    if (current?.content !== value) throw new Error('文本默认值回读不一致');
  }

  function listControls(): Promise<OfficeContentControl[]> {
    return readControls('编辑器未返回内容控件列表');
  }

  async function readSelectedText(): Promise<string> {
    const result = await executeMethod('GetSelectedText', [{ Math: false, Numbering: false }]);
    return typeof result === 'string' ? result : '';
  }

  async function disableTrackRevisions(): Promise<void> {
    await executeCommand(disableTrackRevisionsCommand, 'elnTemplateReview', {}, true);
    const state = await executeCommand(readTrackRevisionsStateCommand, 'elnTemplateReviewState', {});
    if (state.hasRevisionChanges !== false) {
      throw new Error('历史修订仍未全部接受');
    }
    if (state.trackRevisions !== false) {
      throw new Error('修订跟踪仍处于开启状态');
    }
  }

  async function lockFormulaResult(primaryTag: string): Promise<FormulaResultLockSnapshot> {
    const result = await executeCommand(setFormulaResultLockCommand, 'elnTemplateFormulaLocks', { mode: 'lock', primaryTag });
    if (!isFormulaLockSnapshot((result as { snapshot?: unknown }).snapshot)) throw new Error('编辑器未返回公式结果锁快照');
    return (result as { snapshot: FormulaResultLockSnapshot }).snapshot;
  }

  async function restoreFormulaResultLocks(snapshot: FormulaResultLockSnapshot): Promise<void> {
    const result = await executeCommand(setFormulaResultLockCommand, 'elnTemplateFormulaLocks', { mode: 'restore', snapshot });
    if (!isFormulaLockSnapshot((result as { snapshot?: unknown }).snapshot)) throw new Error('编辑器未确认公式结果锁恢复');
  }

  async function unlockFormulaResult(primaryTag: string): Promise<void> {
    const result = await executeCommand(setFormulaResultLockCommand, 'elnTemplateFormulaLocks', { mode: 'unlock', primaryTag });
    if (!isFormulaLockSnapshot((result as { snapshot?: unknown }).snapshot)) throw new Error('编辑器未确认公式结果锁解除');
  }

  async function isFormulaResultLocked(primaryTag: string): Promise<boolean> {
    const result = await executeCommand(setFormulaResultLockCommand, 'elnTemplateFormulaLocks', {
      mode: 'verify',
      primaryTag,
    });
    return isFormulaLockSnapshot((result as { snapshot?: unknown }).snapshot);
  }

  async function insertField(definition: FieldDefinition): Promise<OfficeContentControl[]> {
    const before = await listControls();
    const beforeCount = countTag(before, definition.tag);
    debugBridge('field:before-insert', { beforeCount, controlType: definition.controlType, tag: definition.tag });

    if (beforeCount > 0) {
      throw new Error('字段 ID 已存在');
    }

    const alias = definition.alias.trim();
    if (before.some((control) => parseTag(control.Tag) && (control.Alias || '').trim() === alias)) {
      throw new Error('字段名称已存在');
    }

    await writeDefinition(definition);

    const after = await readControls('插入后无法读取内容控件列表');
    const afterCount = countTag(after, definition.tag);
    debugBridge('field:after-insert', { afterCount, beforeCount, controlType: definition.controlType, tag: definition.tag });
    if (afterCount !== beforeCount + 1) {
      throw new Error('插入后 Tag 计数未增加');
    }

    return after;
  }

  async function writeDefinition(definition: FieldDefinition, lock?: 'contentLocked' | 'sdtContentLocked' | 'unlocked', signal?: AbortSignal): Promise<void> {
    if (signal?.aborted) return;
    const currentControlId = await executeMethod('GetCurrentContentControl');
    if (signal?.aborted) return;
    if (typeof currentControlId === 'string' && currentControlId) {
      throw new Error('请将光标移到已有字段控件外再插入');
    }

    if (definition.controlType === 'text') {
      const result = await executeMethod('AddContentControl', [
        2,
        {
          Tag: definition.tag,
          Alias: definition.alias,
          Lock: lock === 'contentLocked' ? 0 : lock === 'sdtContentLocked' ? 1 : 3,
          PlaceHolderText: definition.placeholder,
          Appearance: 1,
          Color: definition.color,
        },
      ]);
      if (signal?.aborted) return;
      if (
        !result ||
        typeof result !== 'object' ||
        typeof (result as Record<string, unknown>).InternalId !== 'string' ||
        (result as Record<string, unknown>).Tag !== definition.tag
      ) {
        throw new Error('编辑器未能在光标位置插入文本控件，请在目标位置单击出现文字光标后重试');
      }

      insertedTextDefinitions.set(definition.tag, definition);
      verifyNativeControlTag = definition.tag;
      if (definition.defaultValue) {
        await writeTextDefaultWithNativeControl(definition.tag, definition.defaultValue);
        if (signal?.aborted) return;
      }
      return;
    }

    const isDropdown = definition.controlType === 'dropdown';
    await executeCommand(
      insertTypedControlCommand,
      'elnTemplateInsert',
      {
        controlType: definition.controlType,
        tag: definition.tag,
        alias: definition.alias,
        placeholder: definition.placeholder,
        dateFormat: 'dateFormat' in definition ? definition.dateFormat : undefined,
        debug: import.meta.env.DEV,
        defaultValue: isDropdown ? '' : definition.defaultValue,
        defaultChecked: isDropdown ? false : definition.defaultChecked,
        options: isDropdown ? definition.options : [],
        selectedIndex: isDropdown ? definition.selectedIndex : -1,
        color: definition.color,
        lock,
      },
      true,
    );
    if (signal?.aborted) return;

    if (lock === 'contentLocked') {
      await updateControlSpec({
        ...bindingSpec(definition),
        currentTag: definition.tag,
        lock,
        tag: definition.tag,
      });
    }
  }

  function bindingSpec(definition: FieldDefinition) {
    return {
      alias: definition.alias,
      controlConfig: {
        placeholder: definition.placeholder,
        ...('defaultValue' in definition ? { defaultValue: definition.defaultValue } : {}),
        ...('defaultChecked' in definition ? { defaultChecked: definition.defaultChecked } : {}),
        ...('dateFormat' in definition && definition.dateFormat ? { dateFormat: definition.dateFormat } : {}),
        ...('options' in definition ? { options: definition.options } : {}),
      },
      controlType: definition.controlType.toUpperCase(),
    };
  }

  function definitionFromBinding(binding: TemplateBinding, currentText?: string): FieldDefinition {
    const config = binding.controlConfig ?? {};
    const alias = binding.controlAlias || binding.fieldCode;
    const configuredPlaceholder = typeof config.placeholder === 'string' ? config.placeholder : '';
    const placeholder = configuredPlaceholder.trim() || alias;
    const rawOptions = Array.isArray(config.options) ? config.options : [];
    const options = rawOptions.map((item) => {
      if (typeof item === 'object' && item && 'display' in item && 'value' in item) return { display: String(item.display), value: String(item.value) };
      return { display: String(item), value: String(item) };
    });
    const base = { fieldCode: binding.fieldCode, alias, tag: binding.controlTag, color: { R: 23, G: 109, B: 92 } };
    if (binding.controlType === 'DROPDOWN') {
      const value = currentText ?? String(config.defaultValue ?? '');
      return {
        ...base,
        controlType: 'dropdown',
        placeholder,
        options,
        selectedIndex: options.findIndex((item) => item.display === value || item.value === value),
      };
    }
    if (binding.controlType === 'CHECKBOX')
      return {
        ...base,
        controlType: 'checkbox',
        placeholder: '',
        defaultValue: '',
        defaultChecked: currentText ? /^(?:true|1|☑|是)$/iu.test(currentText) : Boolean(config.defaultChecked),
      };
    if (binding.controlType === 'DATE')
      return {
        ...base,
        controlType: 'date',
        dateFormat: typeof config.dateFormat === 'string' && config.dateFormat ? config.dateFormat : 'yyyy-MM-dd',
        placeholder,
        defaultValue: currentText ?? String(config.defaultValue ?? ''),
        defaultChecked: false,
      };
    if (binding.controlType === 'MULTILINE')
      return { ...base, controlType: 'multiline', placeholder, defaultValue: currentText ?? String(config.defaultValue ?? ''), defaultChecked: false };
    return { ...base, controlType: 'text', placeholder, defaultValue: currentText ?? String(config.defaultValue ?? ''), defaultChecked: false };
  }

  function updateControlSpec(update: {
    alias: string;
    controlConfig: null | Record<string, unknown>;
    controlType: string;
    currentTag: string;
    lock: 'contentLocked' | 'sdtContentLocked' | 'unlocked';
    tag: string;
  }): Promise<CommandResult> {
    return executeCommand(updateControlSpecCommand, 'elnTemplateUpdateControls', { removals: [], updates: [update] });
  }

  async function insertBinding(binding: TemplateBinding, signal?: AbortSignal): Promise<void> {
    if (signal?.aborted) return;
    await writeDefinition(definitionFromBinding(binding), bindingLock(binding), signal);
  }

  async function insertReadOnlyBinding(binding: TemplateBinding, signal?: AbortSignal): Promise<void> {
    if (signal?.aborted) return;
    await writeDefinition(definitionFromBinding(binding), 'sdtContentLocked', signal);
  }

  async function insertReference(binding: TemplateBinding, primaryTag: string, signal?: AbortSignal): Promise<void> {
    if (signal?.aborted) return;
    const result = await executeCommand(readControlTextCommand, 'elnTemplateReadText', { tag: primaryTag });
    if (signal?.aborted) return;
    await writeDefinition(definitionFromBinding(binding, typeof result.text === 'string' ? result.text : ''), 'contentLocked', signal);
  }

  async function applyControlPlan(bindings: TemplateBinding[], desired: TemplateControlSnapshot[], signal?: AbortSignal): Promise<void> {
    if (signal?.aborted) return;
    const controls = await listControls();
    if (signal?.aborted) return;
    const desiredById = new Map(desired.map((item) => [item.bindingId, item]));
    const alreadyAppliedIds = new Set<string>();
    const appliedTargetTags = new Set<string>();
    for (const binding of bindings.filter((item) => item.status === 'ACTIVE')) {
      const target = desiredById.get(binding.id);
      if (!target) continue;
      const currentMatches = controls.filter((item) => item.Tag === binding.controlTag);
      if (target.controlTag === binding.controlTag) {
        if (currentMatches.length === 1 && controlMatchesTarget(binding, target, currentMatches[0]!)) alreadyAppliedIds.add(binding.id);
        continue;
      }
      if (currentMatches.length > 0) continue;
      const matches = controls.filter((item) => item.Tag === target.controlTag);
      if (matches.length === 1 && controlMatchesTarget(binding, target, matches[0]!)) {
        alreadyAppliedIds.add(binding.id);
        appliedTargetTags.add(target.controlTag);
      }
    }
    const removals: string[] = [];
    const updates: Array<{
      alias: string;
      controlConfig: null | Record<string, unknown>;
      controlType: string;
      currentTag: string;
      lock: 'contentLocked' | 'sdtContentLocked' | 'unlocked';
      tag: string;
    }> = [];
    for (const binding of bindings.filter((item) => item.status === 'ACTIVE')) {
      const target = desiredById.get(binding.id);
      const control = controls.find((item) => item.Tag === binding.controlTag);
      if (!target) {
        if (control && !appliedTargetTags.has(binding.controlTag)) removals.push(binding.controlTag);
        continue;
      }
      if (alreadyAppliedIds.has(binding.id)) continue;
      if (!control) throw new Error(`待更新控件不存在：${binding.controlTag}`);
      const targetType = (target.controlType || binding.controlType).toUpperCase();
      if (typeof control.ControlType === 'string' && control.ControlType.toUpperCase() !== targetType) {
        throw new Error('当前阶段不支持修改已有控件类型');
      }
      const currentPlaceholder = control.ControlConfig?.placeholder;
      const targetPlaceholder = (target.controlConfig ?? binding.controlConfig)?.placeholder;
      if (targetType !== 'CHECKBOX' && targetPlaceholder === '' && typeof currentPlaceholder === 'string' && currentPlaceholder !== '') {
        throw new Error('当前编辑器不支持清空已有占位文本');
      }
      updates.push({
        currentTag: binding.controlTag,
        tag: target.controlTag,
        alias: target.controlAlias || binding.controlAlias || binding.fieldCode,
        controlType: targetType,
        controlConfig: target.controlConfig ?? binding.controlConfig,
        lock: bindingLock(binding, target.controlRole ?? binding.controlRole),
      });
    }
    if (signal?.aborted) return;
    if (removals.length > 1) throw new Error('当前阶段不支持一次删除多个控件');
    if (updates.length || removals.length) await executeCommand(updateControlSpecCommand, 'elnTemplateUpdateControls', { removals, updates });
  }

  function snapshotControls(bindings: TemplateBinding[], controls: OfficeContentControl[], options: SnapshotControlOptions = {}): TemplateControlSnapshot[] {
    return bindings.map((binding) => {
      const matches = controls.filter((control) => control.Tag === binding.controlTag);
      if (matches.length !== 1) throw new Error(`控件枚举数量异常：${binding.controlTag}`);
      const control = matches[0]!;
      if (
        typeof control.Alias !== 'string' ||
        !control.ControlConfig ||
        typeof control.ControlRole !== 'string' ||
        typeof control.ControlType !== 'string' ||
        typeof control.Lock !== 'string'
      ) {
        throw new Error(`控件枚举属性不完整：${binding.controlTag}`);
      }
      const actualType = snapshotControlType(binding.controlType, control.ControlType);
      const actualConfig = normalizeExistingControlConfig(actualType, control.ControlRole, control.ControlConfig);
      const expectedConfig = normalizeExistingControlConfig(binding.controlType, binding.controlRole, bindingConfigForSnapshot(binding));
      const expectedLock = bindingLock(binding, control.ControlRole);
      const lockMatches = control.Lock === expectedLock || (control.Lock === 'sdtContentLocked' && options.inheritedFormulaLockBindingIds?.has(binding.id));
      if (
        control.Alias !== (binding.controlAlias ?? '') ||
        actualType !== binding.controlType ||
        control.ControlRole !== binding.controlRole ||
        !configsEqual(actualConfig, expectedConfig) ||
        !lockMatches
      ) {
        throw new Error(`控件属性与绑定不一致：${binding.controlTag}`);
      }
      return {
        bindingId: binding.id,
        controlTag: control.Tag!,
        controlAlias: control.Alias,
        controlType: actualType,
        controlRole: control.ControlRole,
        fieldVersionId: binding.fieldVersionId,
        primaryBindingId: binding.primaryBindingId,
        repeatGroupCode: binding.repeatGroupCode,
        repeatPrototypeRowId: binding.repeatPrototypeRowId,
        stepId: binding.stepId,
        controlConfig: actualConfig,
      };
    });
  }

  async function selectControl(internalId: string, signal?: AbortSignal): Promise<void> {
    if (!internalId) {
      throw new Error('该内容控件没有可用的内部 ID');
    }
    if (signal?.aborted) return;

    const controls = await listControls();
    if (signal?.aborted) return;
    if (!controls.some((control) => control.InternalId === internalId)) {
      throw new Error('定位目标已不存在，请刷新字段列表');
    }

    const result = await executeMethod('SelectContentControl', [internalId]);
    if (result === false) {
      throw new Error('编辑器未能定位字段');
    }
  }

  async function deleteControl(internalId: string): Promise<OfficeContentControl[]> {
    if (!internalId) {
      throw new Error('该内容控件没有可用的内部 ID');
    }

    const before = await listControls();
    if (!before.some((control) => control.InternalId === internalId)) {
      throw new Error('删除目标已不存在，请刷新字段列表');
    }

    if ((await executeMethod('RemoveContentControl', [internalId])) === false) {
      throw new Error('编辑器未能移除字段控件');
    }

    const after = await readControls('解除后无法读取内容控件列表');
    if (after.some((control) => control.InternalId === internalId)) {
      throw new Error('解除后字段仍然存在');
    }
    return after;
  }

  function close(): void {
    asc.plugin.executeCommand('close', '');
  }

  function getRuntimeOptions(): unknown {
    return asc.plugin.info?.options;
  }

  function bindLifecycle(callbacks: LifecycleCallbacks): () => void {
    return bindLifecycleHandlers(asc, callbacks);
  }

  return {
    applyControlPlan,
    bindLifecycle,
    close,
    deleteControl,
    disableTrackRevisions,
    getRuntimeOptions,
    insertBinding,
    insertReadOnlyBinding,
    insertField,
    insertReference,
    isFormulaResultLocked,
    listControls,
    lockFormulaResult,
    readSelectedText,
    restoreFormulaResultLocks,
    selectControl,
    snapshotControls,
    unlockFormulaResult,
  };
}
