/**
 * scaffold-theme / generate.test.ts (2026-05-27)
 *
 * Covers Fix #5: Shoelace imports are now conditional on the `useShoelace`
 * flag. Default OFF so projects without `@shoelace-style/shoelace` in their
 * package.json don't fail Vite resolution at boot.
 */
import { describe, it, expect } from 'vitest'
import { generate } from '../generate.js'
import type { ScaffoldThemeInput } from '../types.js'

function fixture(overrides: Partial<ScaffoldThemeInput> = {}): ScaffoldThemeInput {
  return {
    projectPath: '/test',
    appCode: 'TestV2',
    force: false,
    useShoelace: false,
    ...overrides,
  } as ScaffoldThemeInput
}

describe('scaffold-theme / generate — Fix #5 (Shoelace imports conditional)', () => {
  it('omits Shoelace @import lines by default (useShoelace not set)', () => {
    const files = generate(fixture())
    const css = files.find(f => f.path.endsWith('index.css'))!
    expect(css.content).not.toMatch(/@shoelace-style\/shoelace/)
    expect(css.content).not.toMatch(/themes\/light\.css/)
    expect(css.content).not.toMatch(/themes\/dark\.css/)
  })

  it('emits Shoelace @import lines when useShoelace: true', () => {
    const files = generate(fixture({ useShoelace: true }))
    const css = files.find(f => f.path.endsWith('index.css'))!
    expect(css.content).toMatch(/@import '@shoelace-style\/shoelace\/dist\/themes\/light\.css';/)
    expect(css.content).toMatch(/@import '@shoelace-style\/shoelace\/dist\/themes\/dark\.css' \(prefers-color-scheme: dark\);/)
  })

  it('always emits Tailwind + SmartStack theme imports regardless of useShoelace', () => {
    for (const useShoelace of [true, false]) {
      const css = generate(fixture({ useShoelace })).find(f => f.path.endsWith('index.css'))!
      expect(css.content, `useShoelace=${useShoelace}`).toMatch(/@import 'tailwindcss';/)
      expect(css.content, `useShoelace=${useShoelace}`).toMatch(/@import '@atlashub\/smartstack\/theme\.css';/)
    }
  })

  it('emits the accent ramp from the PRD theme slice when provided', () => {
    const files = generate(fixture({
      prdSlice: { theme: { accent: '#ff0000' } },
    }))
    const css = files.find(f => f.path.endsWith('index.css'))!
    expect(css.content).toMatch(/--color-accent-500: #ff0000;/)
  })
})

describe('scaffold-theme / generate — form-control radius (square vs rounded)', () => {
  it('emits --radius-input and --radius-button (default 8px) alongside --radius-card', () => {
    const css = generate(fixture()).find(f => f.path.endsWith('index.css'))!
    expect(css.content).toMatch(/--radius-input: 8px;/)
    expect(css.content).toMatch(/--radius-button: 8px;/)
    expect(css.content).toMatch(/--radius-card: 8px;/)
  })

  it('radiusControl: 0 gives square form controls, independent of radiusCard', () => {
    const css = generate(fixture({
      prdSlice: { theme: { radiusControl: 0, radiusCard: 12 } },
    })).find(f => f.path.endsWith('index.css'))!
    expect(css.content).toMatch(/--radius-input: 0px;/)
    expect(css.content).toMatch(/--radius-button: 0px;/)
    expect(css.content).toMatch(/--radius-card: 12px;/)
  })
})

describe('scaffold-theme / generate — dataviz tokens (dashboards/charts)', () => {
  it('emits the 8-slot categorical palette in BOTH :root and .dark', () => {
    const css = generate(fixture()).find(f => f.path.endsWith('index.css'))!
    for (let i = 1; i <= 8; i++) {
      // each slot defined twice (light + dark), as a valid hex
      const matches = css.content.match(new RegExp(`--dataviz-${i}: #[0-9a-f]{6};`, 'g')) ?? []
      expect(matches.length, `--dataviz-${i}`).toBe(2)
    }
  })

  it('emits chart chrome (grid/axis/tooltip) in both modes and KPI aliases once', () => {
    const css = generate(fixture()).find(f => f.path.endsWith('index.css'))!
    expect((css.content.match(/--chart-grid:/g) ?? []).length).toBe(2)
    expect((css.content.match(/--chart-axis:/g) ?? []).length).toBe(2)
    expect((css.content.match(/--chart-tooltip-bg:/g) ?? []).length).toBe(2)
    expect((css.content.match(/--chart-tooltip-text:/g) ?? []).length).toBe(2)
    // KPI aliases follow the package text/status tokens — defined once in :root
    expect(css.content).toMatch(/--kpi-value: var\(--text-primary\);/)
    expect(css.content).toMatch(/--kpi-label: var\(--text-muted\);/)
    expect(css.content).toMatch(/--kpi-trend-up: var\(--success-text\);/)
    expect(css.content).toMatch(/--kpi-trend-down: var\(--error-text\);/)
  })

  it('honors a caller-provided dataviz palette, derives the rest from the accent', () => {
    const css = generate(fixture({
      prdSlice: { theme: { accent: '#3b82f6', dataviz: ['#aabbcc', '#ddeeff'] } },
    })).find(f => f.path.endsWith('index.css'))!
    expect(css.content).toMatch(/--dataviz-1: #aabbcc;/)
    expect(css.content).toMatch(/--dataviz-2: #ddeeff;/)
    // slot 3+ are derived (valid hex, not the provided ones)
    expect(css.content).toMatch(/--dataviz-3: #[0-9a-f]{6};/)
  })

  it('never reuses a status token hex for a dataviz slot (R17 — decorative ≠ status)', () => {
    const css = generate(fixture()).find(f => f.path.endsWith('index.css'))!
    const dataviz = [...css.content.matchAll(/--dataviz-\d: (#[0-9a-f]{6});/g)].map((m) => m[1])
    // the generated palette must not collide with the semantic status hues
    for (const status of ['#10b981', '#f59e0b', '#ef4444']) {
      expect(dataviz).not.toContain(status)
    }
  })
})
