import type { StabilityAnalysisRow } from '#/api/x/lims/wendingxing';

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

import { buildStabilityChartModels, createStabilityChartOption, formatDisplayNumber, formatPeriodUnit } from '../stabilityAnalysis';

function row(overrides: Partial<StabilityAnalysisRow> = {}): StabilityAnalysisRow {
  return {
    batch_key: 'plan-a::250102',
    biaozhun_id: 'standard-a',
    exception_type: '',
    jieguo_luru_riqi: '2026-01-01',
    jihua_bianhao: 'STP-A',
    jihua_id: 'plan-a',
    numeric_result: 6.7,
    pihao: '250102',
    raw_result: '6.7',
    shangxian: '7',
    shangxian_value: 7,
    shuju_leixing: '数值',
    unit: '',
    xiaxian: '5',
    xiaxian_value: 5,
    xiangmu_bianma: 'XM1899',
    xiangmu_id: 'ph',
    xiangmu_mingcheng: 'pH值',
    yangpin_mingcheng: '脉络宁口服液',
    zhouqi: '0',
    zhouqi_danwei: '/',
    zhouqi_order: 0,
    ...overrides,
  };
}

describe('buildStabilityChartModels', () => {
  it('keeps duplicate batch numbers separate and limits the axis to observed periods', () => {
    const models = buildStabilityChartModels(
      [
        row(),
        row({ numeric_result: 6, raw_result: '6', zhouqi: '1', zhouqi_danwei: '月', zhouqi_order: 30 }),
        row({
          batch_key: 'plan-b::250102',
          jieguo_luru_riqi: '2026-01-02',
          jihua_bianhao: 'STP-B',
          jihua_id: 'plan-b',
          numeric_result: 6.8,
        }),
        row({
          batch_key: 'plan-b::250102',
          jieguo_luru_riqi: '2026-01-09',
          jihua_bianhao: 'STP-B',
          jihua_id: 'plan-b',
          numeric_result: 6.5,
          pihao: '250102',
          raw_result: '6.5',
          zhouqi: '7',
          zhouqi_danwei: '天',
          zhouqi_order: 7,
        }),
      ],
      [{ xiangmu_id: 'ph', zhouqi: '3', zhouqi_danwei: '月', zhouqi_order: 90 }],
    );

    expect(models).toHaveLength(1);
    expect(models[0]?.categories).toEqual(['0', '7天', '1月']);
    expect(models[0]?.series.find((series) => series.id === 'plan-a::250102')).toMatchObject({
      data: [6.7, null, 6],
      name: 'STP-A / 250102',
    });
    expect(models[0]?.series.find((series) => series.id === 'plan-b::250102')?.name).toBe('STP-B / 250102');
    expect(models[0]?.series.find((series) => series.id === 'upper-limit')?.data).toEqual([7, 7, 7]);
    expect(models[0]?.series.find((series) => series.id === 'lower-limit')?.data).toEqual([5, 5, 5]);
    expect(models[0]?.series.map((series) => series.id).slice(-2)).toEqual(['lower-limit', 'upper-limit']);
  });

  it('splits the same item when standard, limits, or units differ', () => {
    const models = buildStabilityChartModels([
      row(),
      row({ biaozhun_id: 'standard-b', shangxian: '7.5', shangxian_value: 7.5 }),
      row({ biaozhun_id: 'standard-c', unit: 'mg/ml' }),
    ]);

    expect(models.filter((model) => model.item_id === 'ph')).toHaveLength(3);
    expect(models[0]).toMatchObject({ lower_limit: '5', standard_id: 'standard-a', upper_limit: '7' });
  });

  it('uses the project code as the primary grouping key', () => {
    const models = buildStabilityChartModels([row(), row({ xiangmu_id: 'ph-new-id' })]);

    expect(models).toHaveLength(1);
  });

  it('keeps qualitative items as table-only models', () => {
    const [model] = buildStabilityChartModels([
      row({
        biaozhun_id: 'standard-text',
        numeric_result: null,
        raw_result: '符合规定',
        shangxian: '',
        shangxian_value: null,
        shuju_leixing: '文本',
        xiaxian: '',
        xiaxian_value: null,
        xiangmu_bianma: 'XM-TEXT',
        xiangmu_id: 'appearance',
        xiangmu_mingcheng: '性状',
      }),
    ]);

    expect(model?.text_only).toBe(true);
    expect(model?.series).toEqual([]);
  });

  it('retains exception metadata and ignores qualified numeric text', () => {
    const [model] = buildStabilityChartModels([
      row({ exception_type: 'oos' }),
      row({
        batch_key: 'plan-a::250103',
        numeric_result: null,
        pihao: '250103',
        raw_result: '<0.01',
      }),
    ]);

    expect(model?.series.find((series) => series.id === 'plan-a::250102')?.points[0]?.exception_type).toBe('oos');
    expect(model?.series.find((series) => series.id === 'plan-a::250103')?.data).toEqual([null]);
  });
});

describe('createStabilityChartOption', () => {
  it('renders independent dashed limits, a scroll legend, and disconnected record lines', () => {
    const [model] = buildStabilityChartModels([row()]);
    const option = createStabilityChartOption(model!, '稳定性趋势');
    const series = option.series as Array<Record<string, any>>;

    expect(option.legend).toMatchObject({ type: 'scroll' });
    expect(series.find((item) => item.id === 'upper-limit')).toMatchObject({ lineStyle: { color: '#ef4444', type: 'dashed' }, name: '上限' });
    expect(series.find((item) => item.id === 'lower-limit')).toMatchObject({ lineStyle: { color: '#f59e0b', type: 'dashed' }, name: '下限' });
    expect(series.find((item) => item.id === 'plan-a::250102')?.connectNulls).toBe(false);
    expect(option.yAxis).toMatchObject({ scale: true });
    const tooltip = option.tooltip as { formatter: (params: unknown) => string };
    expect(tooltip.formatter([{ axisValueLabel: '0', dataIndex: 0, marker: '', seriesId: 'plan-a::250102', seriesName: '250102', value: 6.7 }])).toContain(
      '单位：-',
    );
  });

  it('renders exception results as ordinary batch points', () => {
    const [model] = buildStabilityChartModels([row({ exception_type: 'oos' })]);
    const option = createStabilityChartOption(model!, '稳定性趋势');
    const recordSeries = (option.series as Array<Record<string, any>>).find((item) => item.id === 'plan-a::250102');

    expect(recordSeries?.data).toEqual([6.7]);
  });
});

describe('table display formatting', () => {
  it('removes database numeric trailing zeroes', () => {
    expect(formatDisplayNumber('18.000000000000000')).toBe('18');
    expect(formatDisplayNumber('0.050000000000000')).toBe('0.05');
    expect(formatDisplayNumber('')).toBe('-');
  });

  it('translates stored period units for display', () => {
    expect(formatPeriodUnit('month')).toBe('月');
    expect(formatPeriodUnit('day')).toBe('天');
    expect(formatPeriodUnit('/')).toBe('/');
  });
});
