import { describe, expect, it } from 'vitest';

import { disableTrackRevisionsCommand, insertTypedControlCommand, listContentControlsCommand, writeTextDefaultCommand } from '../../src/onlyoffice/commands';
import { installOnlyOfficeGlobals, makeApiFixture, makeInsertSpec, type FixtureApi, type InsertSpec, type TextDefaultSpec } from '../fixtures/onlyoffice';

interface CommandResult {
  controlType?: string;
  controls?: Array<{ Alias: string; ControlType: string; InternalId: string; Tag: string }>;
  error?: string;
  ok: boolean;
}

function parseResult(value: string): CommandResult {
  return JSON.parse(value) as CommandResult;
}

function setFixtureInsertScope(Api: FixtureApi, overrides: Partial<InsertSpec> & Pick<InsertSpec, 'controlType'>): void {
  installOnlyOfficeGlobals(Api, {
    elnTemplateInsert: makeInsertSpec(overrides),
  });
}

function setTextDefaultScope(Api: FixtureApi, spec: TextDefaultSpec): void {
  installOnlyOfficeGlobals(Api, { elnTemplateTextDefault: spec });
}

describe('insertTypedControlCommand', () => {
  it('使用显示值、业务值和默认项创建下拉控件', () => {
    const fixture = makeApiFixture();
    const options = [
      { display: '合格', value: 'pass' },
      { display: '不合格', value: 'fail' },
    ];
    setFixtureInsertScope(fixture.Api, { controlType: 'dropdown', options, selectedIndex: 0 });

    const result = parseResult(insertTypedControlCommand());

    expect(result).toEqual({ ok: true, controlType: 'dropdown' });
    expect(fixture.calls.combo).toEqual([[options, 0]]);
    expect(fixture.calls.inserted[0]).toMatchObject({ block: false, replaceSelection: true });
    expect(fixture.calls.inserted[0]?.control.tag).toBe('eln.field.result');
    expect(fixture.calls.inserted[0]?.control.lock).toBe('unlocked');
  });

  it('使用配置的默认状态创建复选框且不写占位文本', () => {
    const fixture = makeApiFixture();
    setFixtureInsertScope(fixture.Api, { controlType: 'checkbox', defaultChecked: true });

    const result = parseResult(insertTypedControlCommand());

    expect(result.ok).toBe(true);
    expect(fixture.calls.checkbox).toEqual([{ checked: true, checkedSymbol: 0x2611, uncheckedSymbol: 0x2610 }]);
    expect(fixture.calls.inserted[0]?.control.placeholder).toBeUndefined();
  });

  it('使用本地年月日创建日期默认值', () => {
    const fixture = makeApiFixture();
    setFixtureInsertScope(fixture.Api, { controlType: 'date', defaultValue: '2026-08-01' });

    const result = parseResult(insertTypedControlCommand());

    expect(result.ok).toBe(true);
    expect(fixture.calls.dateProperties).toEqual({ format: 'yyyy-MM-dd', lang: 'zh-CN' });
    expect(fixture.calls.date[0]?.getFullYear()).toBe(2026);
    expect(fixture.calls.date[0]?.getMonth()).toBe(7);
    expect(fixture.calls.date[0]?.getDate()).toBe(1);
  });

  it('把多行默认值拆成独立段落并作为块级内容插入', () => {
    const fixture = makeApiFixture();
    setFixtureInsertScope(fixture.Api, {
      controlType: 'multiline',
      tag: 'eln.field.note',
      alias: '备注',
      defaultValue: '第一行\n第二行',
      placeholder: '请输入备注',
    });

    const result = parseResult(insertTypedControlCommand());

    expect(result.ok).toBe(true);
    expect(fixture.calls.paragraphTexts).toEqual(['第一行', '第二行']);
    expect(fixture.calls.inserted[0]).toMatchObject({ block: true, replaceSelection: false });
    expect(fixture.calls.inserted[0]?.control.elements).toHaveLength(2);
  });

  it('无默认值时设置占位文本', () => {
    const fixture = makeApiFixture();
    setFixtureInsertScope(fixture.Api, { controlType: 'date', defaultValue: '', placeholder: '请选择日期' });

    const result = parseResult(insertTypedControlCommand());

    expect(result.ok).toBe(true);
    expect(fixture.calls.inserted[0]?.control.placeholder).toBe('请选择日期');
  });

  it('构造器缺失时报告不支持且不插入', () => {
    const fixture = makeApiFixture();
    delete fixture.Api.CreateDatePickerContentControl;
    setFixtureInsertScope(fixture.Api, { controlType: 'date' });

    const result = parseResult(insertTypedControlCommand());

    expect(result.ok).toBe(false);
    expect(result.error).toMatch(/不支持日期控件/);
    expect(fixture.calls.inserted).toHaveLength(0);
  });

  it('关键控件 setter 返回 false 时拒绝插入', () => {
    const fixture = makeApiFixture();
    const control = fixture.makeControl('date');
    control.SetTag = () => false;
    fixture.Api.CreateDatePickerContentControl = () => control;
    setFixtureInsertScope(fixture.Api, { controlType: 'date' });

    const result = parseResult(insertTypedControlCommand());

    expect(result.ok).toBe(false);
    expect(result.error).toMatch(/Tag/);
    expect(fixture.calls.inserted).toHaveLength(0);
  });

  it.each([
    {
      type: 'date' as const,
      defaultValue: '2026-08-01',
      patch: (Api: FixtureApi, control: ReturnType<ReturnType<typeof makeApiFixture>['makeControl']>) => {
        control.SetDate = () => false;
        Api.CreateDatePickerContentControl = () => control;
      },
    },
    {
      type: 'multiline' as const,
      defaultValue: '第一行\n第二行',
      patch: (Api: FixtureApi, control: ReturnType<ReturnType<typeof makeApiFixture>['makeControl']>) => {
        control.RemoveAllElements = () => false;
        Api.CreateBlockLvlSdt = () => control;
      },
    },
    {
      type: 'multiline' as const,
      defaultValue: '第一行\n第二行',
      patch: (Api: FixtureApi, control: ReturnType<ReturnType<typeof makeApiFixture>['makeControl']>) => {
        control.AddElement = () => false;
        Api.CreateBlockLvlSdt = () => control;
      },
    },
  ])('$type 默认值写入返回 false 时拒绝插入', ({ type, defaultValue, patch }) => {
    const fixture = makeApiFixture();
    const control = fixture.makeControl(type);
    patch(fixture.Api, control);
    setFixtureInsertScope(fixture.Api, { controlType: type, defaultValue });

    const result = parseResult(insertTypedControlCommand());

    expect(result.ok).toBe(false);
    expect(result.error).toMatch(/失败/);
    expect(fixture.calls.inserted).toHaveLength(0);
  });

  it.each(['SetPlaceholderText', 'SetColor'] as const)('配置相应值时要求 %s API 存在', (missingMethod) => {
    const fixture = makeApiFixture();
    const control = fixture.makeControl('date');
    delete control[missingMethod];
    fixture.Api.CreateDatePickerContentControl = () => control;
    setFixtureInsertScope(fixture.Api, { controlType: 'date', defaultValue: '' });

    const result = parseResult(insertTypedControlCommand());

    expect(result.ok).toBe(false);
    expect(result.error).toMatch(/不支持/);
    expect(fixture.calls.inserted).toHaveLength(0);
  });

  it('文档 InsertContent 返回 false 时报告失败', () => {
    const fixture = makeApiFixture();
    fixture.document.InsertContent = () => false;
    setFixtureInsertScope(fixture.Api, { controlType: 'checkbox' });

    const result = parseResult(insertTypedControlCommand());

    expect(result.ok).toBe(false);
    expect(result.error).toMatch(/插入/);
  });
});

describe('disableTrackRevisionsCommand', () => {
  it('关闭文档内部的修订跟踪', () => {
    const fixture = makeApiFixture();
    let enabled: boolean | undefined;
    fixture.document.SetTrackRevisions = (value) => {
      enabled = value;
      return true;
    };
    installOnlyOfficeGlobals(fixture.Api, { elnTemplateReview: {} });

    expect(parseResult(disableTrackRevisionsCommand())).toEqual({ ok: true });
    expect(enabled).toBe(false);
  });
});

describe('listContentControlsCommand', () => {
  it('回读内容控件属性并识别五种具体类型', () => {
    const fixture = makeApiFixture();
    const definitions = [
      { alias: '文本', id: 'text-1', tag: 'eln.field.text', type: 'text' },
      { alias: '下拉', id: 'dropdown-1', tag: 'eln.field.dropdown', type: 'dropdown' },
      { alias: '复选', id: 'checkbox-1', tag: 'eln.field.checkbox', type: 'checkbox' },
      { alias: '日期', id: 'date-1', tag: 'eln.field.date', type: 'date' },
      { alias: '多行', id: 'multiline-1', tag: 'eln.field.multiline', type: 'multiline' },
    ] as const;

    fixture.document.controls = definitions.map(({ alias, id, tag, type }) =>
      Object.assign(fixture.makeControl(type, tag), {
        GetAlias: () => alias,
        GetInternalId: () => id,
        IsBlockLevel: () => type === 'multiline',
        IsCheckBox: () => type === 'checkbox',
        IsComboBox: () => type === 'dropdown',
        IsDatePicker: () => type === 'date',
        IsDropDownList: () => false,
      }),
    );
    installOnlyOfficeGlobals(fixture.Api, { elnTemplateList: {} });

    expect(parseResult(listContentControlsCommand())).toEqual({
      ok: true,
      controls: [
        { Alias: '文本', ControlType: 'text', InternalId: 'text-1', Tag: 'eln.field.text' },
        { Alias: '下拉', ControlType: 'dropdown', InternalId: 'dropdown-1', Tag: 'eln.field.dropdown' },
        { Alias: '复选', ControlType: 'checkbox', InternalId: 'checkbox-1', Tag: 'eln.field.checkbox' },
        { Alias: '日期', ControlType: 'date', InternalId: 'date-1', Tag: 'eln.field.date' },
        { Alias: '多行', ControlType: 'multiline', InternalId: 'multiline-1', Tag: 'eln.field.multiline' },
      ],
    });
  });

  it('多个类型特征同时存在时按复选、下拉、日期、多行的顺序判定', () => {
    const fixture = makeApiFixture();
    fixture.document.controls = [
      Object.assign(fixture.makeControl('ambiguous', 'eln.field.ambiguous'), {
        GetAlias: () => '歧义字段',
        GetInternalId: () => 'ambiguous-1',
        IsBlockLevel: () => true,
        IsCheckBox: () => true,
        IsComboBox: () => true,
        IsDatePicker: () => true,
        IsDropDownList: () => true,
      }),
    ];
    installOnlyOfficeGlobals(fixture.Api, { elnTemplateList: {} });

    expect(parseResult(listContentControlsCommand()).controls?.[0]?.ControlType).toBe('checkbox');
  });
});

describe('writeTextDefaultCommand', () => {
  it('按唯一 Tag 成功写入文本默认值', () => {
    const fixture = makeApiFixture();
    const target = fixture.makeControl('text', 'eln.field.sample_id');
    fixture.document.controls.push(target);
    setTextDefaultScope(fixture.Api, { tag: 'eln.field.sample_id', value: 'S-001' });

    const result = parseResult(writeTextDefaultCommand());

    expect(result.ok).toBe(true);
    expect(target.elements).toHaveLength(1);
    expect(target.elements[0]?.text).toBe('S-001');
  });

  it.each(['AddText', 'AddElement'] as const)('%s 返回 false 时拒绝文本默认值写入', (failedStep) => {
    const fixture = makeApiFixture();
    const target = fixture.makeControl('text', 'eln.field.sample_id');
    fixture.document.controls.push(target);

    if (failedStep === 'AddText') {
      fixture.Api.CreateRun = () => ({ text: '', AddText: () => false });
    } else {
      target.AddElement = () => false;
    }
    setTextDefaultScope(fixture.Api, { tag: 'eln.field.sample_id', value: 'S-001' });

    const result = parseResult(writeTextDefaultCommand());

    expect(result.ok).toBe(false);
    expect(result.error).toMatch(/失败/);
  });

  it('清空返回 false 但最终回读一致时仍接受写入', () => {
    const fixture = makeApiFixture();
    const target = fixture.makeControl('text', 'eln.field.sample_id');
    target.RemoveAllElements = () => false;
    fixture.document.controls.push(target);
    setTextDefaultScope(fixture.Api, { tag: 'eln.field.sample_id', value: 'S-001' });

    const result = parseResult(writeTextDefaultCommand());

    expect(result.ok).toBe(true);
    expect(target.GetRange?.().GetText?.()).toBe('S-001');
  });

  it('文本默认值回读不一致时拒绝写入', () => {
    const fixture = makeApiFixture();
    const target = fixture.makeControl('text', 'eln.field.sample_id');
    target.GetRange = () => ({ GetText: () => '旧值' });
    fixture.document.controls.push(target);
    setTextDefaultScope(fixture.Api, { tag: 'eln.field.sample_id', value: 'S-001' });

    const result = parseResult(writeTextDefaultCommand());

    expect(result.ok).toBe(false);
    expect(result.error).toMatch(/回读/);
  });

  it.each([0, 2])('目标 Tag 匹配数量为 %i 时拒绝写入', (targetCount) => {
    const fixture = makeApiFixture();
    for (let index = 0; index < targetCount; index += 1) {
      fixture.document.controls.push(fixture.makeControl('text', 'eln.field.sample_id'));
    }
    setTextDefaultScope(fixture.Api, { tag: 'eln.field.sample_id', value: 'S-001' });

    const result = parseResult(writeTextDefaultCommand());

    expect(result.ok).toBe(false);
    expect(result.error).toMatch(/唯一控件/);
  });
});

describe('command 自包含契约', () => {
  it.each([
    ['disableTrackRevisionsCommand', disableTrackRevisionsCommand],
    ['insertTypedControlCommand', insertTypedControlCommand],
    ['listContentControlsCommand', listContentControlsCommand],
    ['writeTextDefaultCommand', writeTextDefaultCommand],
  ] as const)('%s 源码只依赖 Asc、Api 和标准全局', (_name, command) => {
    const source = command.toString();

    expect(source).not.toMatch(/\b(?:import|require|window|document)\b/);
    expect(source).toContain('Asc.scope');
    expect(source).toContain('Api');
  });

  it('insertTypedControlCommand 的源码可在隔离函数中执行', () => {
    const fixture = makeApiFixture();
    const execute = new Function('Asc', 'Api', `return (${insertTypedControlCommand.toString()})()`) as (
      Asc: { scope: { elnTemplateInsert: InsertSpec } },
      Api: FixtureApi,
    ) => string;

    const result = parseResult(execute({ scope: { elnTemplateInsert: makeInsertSpec({ controlType: 'checkbox', defaultChecked: true }) } }, fixture.Api));

    expect(result).toEqual({ ok: true, controlType: 'checkbox' });
    expect(fixture.calls.inserted).toHaveLength(1);
  });

  it('listContentControlsCommand 的源码可在隔离函数中执行', () => {
    const fixture = makeApiFixture();
    fixture.document.controls = [
      Object.assign(fixture.makeControl('date', 'eln.field.date'), {
        GetAlias: () => '日期',
        GetInternalId: () => 'date-1',
        IsDatePicker: () => true,
      }),
    ];
    const execute = new Function('Asc', 'Api', `return (${listContentControlsCommand.toString()})()`) as (
      Asc: { scope: { elnTemplateList: Record<string, never> } },
      Api: FixtureApi,
    ) => string;

    expect(parseResult(execute({ scope: { elnTemplateList: {} } }, fixture.Api))).toEqual({
      ok: true,
      controls: [{ Alias: '日期', ControlType: 'date', InternalId: 'date-1', Tag: 'eln.field.date' }],
    });
  });

  it('writeTextDefaultCommand 的源码可在隔离函数中执行', () => {
    const fixture = makeApiFixture();
    fixture.document.controls.push(fixture.makeControl('text', 'eln.field.sample_id'));
    const execute = new Function('Asc', 'Api', `return (${writeTextDefaultCommand.toString()})()`) as (
      Asc: { scope: { elnTemplateTextDefault: TextDefaultSpec } },
      Api: FixtureApi,
    ) => string;

    const result = parseResult(execute({ scope: { elnTemplateTextDefault: { tag: 'eln.field.sample_id', value: 'S-001' } } }, fixture.Api));

    expect(result).toEqual({ ok: true });
  });
});
