import fs from 'node:fs'
import path from 'node:path'
import { describe, expect, it } from 'vitest'

/**
 * Regression lock for the owner ruling 2026-08-14 (code-conventions.md §R9 disabled row):
 * when Figma has NO disabled variant for a control, the code side binds the existing
 * SEMANTIC disabled tokens — it does not dim the whole control with `opacity`.
 *
 * ⚠️ Why these assertions read the SFC SOURCE instead of computed styles:
 * these are `<style scoped>` blocks, and **scoped SFC CSS is not applied in jsdom** —
 * a `getComputedStyle(...).opacity` assertion here would pass no matter what the
 * stylesheet says (it would be an empty test that only looks green). Source-level
 * assertions are the only form that can actually fail in this environment. The real
 * rendered values are covered by the Playwright render-verification harness instead.
 *
 * Fault-injection record (2026-08-14, run before landing): putting `opacity: 0.4` back
 * into any one of the four disabled blocks turns the matching `has no opacity dimming`
 * case RED, and deleting a token binding turns the matching `binds` case RED — verified
 * per file, so the four cases are not vacuous.
 */

const SRC = path.join(process.cwd(), 'src')

/** Returns the body of the first CSS rule whose selector list contains `selector`. */
function ruleBody(css: string, selector: string): string {
  // Match `... <selector> ... { ... }` — selector must appear in the selector list,
  // i.e. between the previous `}` (or start) and the opening `{`.
  const re = new RegExp(String.raw`(^|\})([^{}]*${selector.replace(/[.*+?^${}()|[\]\\]/g, '\\$&')}[^{}]*)\{([^}]*)\}`, 'm')
  const m = re.exec(css)
  if (!m) throw new Error(`selector not found: ${selector}`)
  return m[3]
}

const CASES = [
  {
    file: 'components/Rating/Rating.vue',
    selector: '.rating--disabled',
    // Both star states resolve to the disabled icon colour; the value stays readable
    // because the filled/empty difference is carried by the glyph, not the colour.
    binds: ['--rating-star-on: var(--icon-disabled)', '--rating-star-off: var(--icon-disabled)'],
  },
  {
    file: 'components/Slider/Slider.vue',
    selector: '.slider.slider--disabled',
    binds: [
      '--slider-fill-color: var(--control-disabled-selected-bg)',
      '--slider-track-bg: var(--control-disabled-bg)',
      '--slider-value-color: var(--text-disabled)',
    ],
  },
  {
    file: 'components/Select/Select.vue',
    selector: '.select:disabled',
    binds: ['color: var(--text-tips)'],
  },
  {
    file: 'components/InputNumber/InputNumber.vue',
    selector: '.inputnum--disabled .inputnum-input',
    binds: ['color: var(--text-tips)'],
  },
] as const

describe('disabled state binds semantic tokens, not opacity (code-conventions §R9)', () => {
  for (const c of CASES) {
    const css = fs.readFileSync(path.join(SRC, c.file), 'utf8')

    it(`${c.file} — ${c.selector} binds the semantic disabled token(s)`, () => {
      const body = ruleBody(css, c.selector)
      for (const decl of c.binds) expect(body.replace(/\s+/g, ' ')).toContain(decl)
    })

    it(`${c.file} — ${c.selector} has no opacity dimming`, () => {
      const body = ruleBody(css, c.selector)
      expect(body).not.toMatch(/(^|[^-\w])opacity\s*:/)
    })
  }

  it('Switch and Button keep their Figma-sourced opacity (⛔ not part of this ruling)', () => {
    // The 2026-08-13 ruling is the other half: where the Figma variant itself draws a
    // container opacity, the code follows Figma. Extract evidence: 80 hits in
    // button_light_s__1545_51851.json, 4 in switch__348_3483.json. If a later sweep
    // "cleans up" these two, that is a FIGMA_AS_SOURCE_OF_TRUTH violation, not tidying.
    const sw = fs.readFileSync(path.join(SRC, 'components/Switch/Switch.vue'), 'utf8')
    expect(sw).toMatch(/opacity/)
  })
})
