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

import type { MeasureWidthImpl } from '../../measure/types';
import { applyOverflowFallback } from '../fallback';

function t(text: string, start: number) {
  return { text, start, end: start + text.length };
}

describe('textWrap overflow-fallback', () => {
  it('SYSTEM_DEFAULT：仍返回单行，但 meta 标记 SYSTEM_DEFAULT', async () => {
    const res = await applyOverflowFallback({
      tokens: [t('I', 0), t('am', 2), t('tired', 5)],
      lang: 'EN',
      context: 'APP',
      availableWidth: 10,
      maxLines: 2,
      overflowMode: 'SYSTEM_DEFAULT',
      ellipsisToken: '…',
      reason: 'NO_CANDIDATE',
      partialLayout: null,
    });

    expect(res.lines.length).toBe(1);
    expect(res.meta.fallback_type).toBe('SYSTEM_DEFAULT');
    expect(res.meta.overflow_type).toBe('NONE');
  });

  it('CLIP：截断到 maxLines', async () => {
    const res = await applyOverflowFallback({
      tokens: [t('a', 0), t('b', 2), t('c', 4)],
      lang: 'EN',
      context: 'APP',
      availableWidth: 10,
      maxLines: 2,
      overflowMode: 'CLIP',
      ellipsisToken: '…',
      reason: 'TOO_LONG',
      partialLayout: {
        breaks: [1, 2],
        lines: [
          { start: 0, end: 1, text: 'a' },
          { start: 1, end: 2, text: 'b' },
          // 造一个“未覆盖到 N”的 partial，触发 overflow
          { start: 2, end: 2, text: 'c' },
        ],
      },
    });

    expect(res.lines).toEqual(['a', 'b']);
    expect(res.meta.overflow_type).toBe('CLIP');
  });

  it('ELLIPSIS：清理末尾空白与 TC 末尾标点（不输出 \"  …\" / \"，…\"）', async () => {
    const res = await applyOverflowFallback({
      tokens: [t('我', 0), t('好', 1), t('累', 2), t('，', 3)],
      lang: 'TC',
      context: 'WIDGET',
      availableWidth: 10,
      maxLines: 1,
      overflowMode: 'ELLIPSIS',
      ellipsisToken: '…',
      reason: 'NO_CANDIDATE',
      partialLayout: {
        breaks: [],
        // 造一个“未覆盖到 N”的 partial，触发 overflow
        lines: [{ start: 0, end: 3, text: '我好累，  ' }],
      },
      tcPunctuations: ['，'],
    });

    expect(res.lines[0]).toBe('我好累…');
    expect(res.meta.overflow_type).toBe('ELLIPSIS');
  });

  it('ELLIPSIS：可测量时，超宽则按 token 回退直到不超宽', async () => {
    // mock：含省略号时宽度=100，否则宽度=文本长度
    const impl: MeasureWidthImpl = vi.fn(async ({ text }) => (text.includes('…') ? 100 : text.length));

    const res = await applyOverflowFallback({
      tokens: [t('I', 0), t('am', 2), t('so', 5), t('tired', 8)],
      lang: 'EN',
      context: 'APP',
      availableWidth: 10,
      maxLines: 1,
      overflowMode: 'ELLIPSIS',
      ellipsisToken: '…',
      reason: 'NO_CANDIDATE',
      partialLayout: {
        breaks: [],
        // 造一个“未覆盖到 N”的 partial，触发 overflow
        lines: [{ start: 0, end: 3, text: 'I am so' }],
      },
      measure: {
        context: 'APP',
        contextProfile: 'APP|ios|ellipsis',
        fontSpec: { fontFamily: 'PingFangSC-Regular', fontWeight: '400', fontSize: 16 },
        measureWidthImpl: impl,
      },
    });

    // 因为任何带 ellipsis 的测量都超宽，这里会一路回退到空串 + ellipsis
    expect(res.lines[0]).toBe('…');
    expect(res.meta.overflow_type).toBe('ELLIPSIS');
  });
});

