import { describe, it, expect } from 'vitest'
import {
  CHECK_FIELDS,
  classifyFailedCheck,
  effectiveRadius,
} from './visual-verify/lib/drift-compare-core'
import type { ExcuseRow } from './visual-verify/lib/render-excused-fields'

const excuses: ExcuseRow[] = [
  {
    component: 'Chart',
    field: 'rootWidth',
    entryScope: '*',
    reason: 'fixture',
    fixDirection: 'harness-verifier',
    addedAt: '2026-08-07',
    reviewBy: '2027-02-03',
  },
]

function entry(over: Record<string, unknown> = {}) {
  return {
    manifestId: 'chart--type-bar--x',
    figmaNodeId: '1:1',
    figmaName: 'Chart',
    figmaVariantName: 'Chart/bar',
    codeComponent: 'Chart',
    codeProps: {},
    renderRoute: '/x',
    _warnings: ['harness limitation across all token / colour / sizing checks.'],
    ...over,
  } as never
}

const fail = (field: string, expected: unknown, actual: unknown) =>
  ({ field, expected, actual, pass: false, status: 'fail' }) as never

describe('classifyFailedCheck — 散文 warning 不再是判据', () => {
  it('表里有该 (组件,字段) → B', () => {
    expect(classifyFailedCheck(entry(), fail('rootWidth', 240, 192), excuses)).toBe('B_RESIDUAL_SCHEMA_GAP')
  })

  it('同一 entry 的**别的字段** → A（entry 级免疫已消失，本条是本任务的核心回归）', () => {
    expect(classifyFailedCheck(entry(), fail('rootFillHex', '#0b2b13', '#ff00ff'), excuses)).toBe(
      'A_TRUE_DRIFT_CANDIDATE',
    )
  })

  it('带足六个旧短语的 warning 也不再降级', () => {
    const noisy = entry({
      _warnings: [
        'pseudo-state runtime prop runtime type runtime UX harness limitation verifier resolution',
      ],
    })
    expect(classifyFailedCheck(noisy, fail('rootFillHex', '#000000', '#ff00ff'), excuses)).toBe(
      'A_TRUE_DRIFT_CANDIDATE',
    )
  })

  it('空表 → 一律 A', () => {
    expect(classifyFailedCheck(entry(), fail('rootWidth', 240, 192), [])).toBe('A_TRUE_DRIFT_CANDIDATE')
  })

  it('manifestId 落在 entryScope 外 → A', () => {
    const scoped: ExcuseRow[] = [{ ...excuses[0], entryScope: 'chart--type-donut--*' }]
    expect(classifyFailedCheck(entry(), fail('rootWidth', 240, 192), scoped)).toBe('A_TRUE_DRIFT_CANDIDATE')
  })

  it('renderTarget / navigation 仍是 B（基础设施信号，不经表）', () => {
    expect(classifyFailedCheck(entry(), fail('navigation', 'route loaded', 'ERR'), [])).toBe(
      'B_RESIDUAL_SCHEMA_GAP',
    )
    expect(classifyFailedCheck(entry(), fail('renderTarget', 'visible', 'missing: .x'), [])).toBe(
      'B_RESIDUAL_SCHEMA_GAP',
    )
  })

  it('≤5% 的宽高差仍归 C（数值容差判据保留）', () => {
    expect(classifyFailedCheck(entry(), fail('rootWidth', 100, 102), [])).toBe('C_BOUNDARY_CASE')
  })

  it('pass 的 check 不分类', () => {
    expect(
      classifyFailedCheck(entry(), { field: 'rootWidth', expected: 1, actual: 1, pass: true, status: 'pass' } as never, []),
    ).toBeUndefined()
  })
})

describe('effectiveRadius — CSS Backgrounds §5.5 corner clamp', () => {
  it('40x20 上 100 与 20 都 clamp 到 10（浏览器实测已证像素等价）', () => {
    expect(effectiveRadius(100, 40, 20)).toBe(10)
    expect(effectiveRadius(20, 40, 20)).toBe(10)
  })

  it('未超出时原样返回', () => {
    expect(effectiveRadius(4, 40, 20)).toBe(4)
  })

  it('盒子拿不到时退回字面值（fail closed —— 不因为量不到就放行）', () => {
    expect(effectiveRadius(100, null, 20)).toBe(100)
    expect(effectiveRadius(100, 40, null)).toBe(100)
  })

  it('radius 为 null 时保持 null', () => {
    expect(effectiveRadius(null, 40, 20)).toBeNull()
  })
})

describe('CHECK_FIELDS 是字段闭集的唯一定义', () => {
  it('含 buildChecks 会产出的全部字段（含 pickup 漏列的六个）', () => {
    for (const f of [
      'rootWidth', 'rootHeight',
      'rootPadding.top', 'rootPadding.right', 'rootPadding.bottom', 'rootPadding.left',
      'rootRadius', 'rootGap', 'rootOpacity', 'rootBorderWidth',
      'rootFillHex', 'rootBorderHex', 'textFillHex',
      'textInset.left', 'textInset.right',
      'renderTarget', 'navigation',
    ]) {
      expect(CHECK_FIELDS, f).toContain(f)
    }
  })
})
