import { describe, it, expect } from 'vitest'
import { generate } from '../generate.js'

describe('scaffold-layout / generate', () => {
  const spec = { projectPath: '/web', appCode: 'TestV2', force: false }

  it('writes PageTemplate at src/components/ui/PageTemplate.tsx (not the legacy src/components path)', () => {
    const files = generate(spec)
    const pt = files.find((f) => f.path.endsWith('PageTemplate.tsx'))
    expect(pt).toBeDefined()
    expect(pt!.path).toBe('src/components/ui/PageTemplate.tsx')
    // Negative: ensure the legacy non-ui path is gone — the path mismatch
    // (scaffold-layout writes here, scaffold-component imports from /ui/)
    // is the silent-spinner #1 root cause.
    expect(files.some((f) => f.path === 'src/components/PageTemplate.tsx')).toBe(false)
  })

  it('declares all six props in PageTemplateProps', () => {
    const files = generate(spec)
    const pt = files.find((f) => f.path.endsWith('PageTemplate.tsx'))!
    // Each prop name must appear in the interface
    for (const prop of ['title', 'subtitle', 'icon', 'actions', 'children', 'className', 'contentClassName']) {
      expect(pt.content).toMatch(new RegExp(`\\b${prop}\\??:`))
    }
  })

  it('destructures and renders subtitle / icon / actions / className / contentClassName', () => {
    const files = generate(spec)
    const pt = files.find((f) => f.path.endsWith('PageTemplate.tsx'))!
    // Destructured in the component signature (multiline OK)
    expect(pt.content).toMatch(/title,[\s\S]*subtitle,[\s\S]*icon,[\s\S]*actions,[\s\S]*children,[\s\S]*className,[\s\S]*contentClassName/)
    // Each prop is rendered somewhere in JSX
    expect(pt.content).toMatch(/\{subtitle\s*&&/)
    expect(pt.content).toMatch(/\{icon\s*&&/)
    expect(pt.content).toMatch(/\{actions\s*&&/)
    expect(pt.content).toMatch(/className\s*\?\?\s*''/)
    expect(pt.content).toMatch(/contentClassName\s*\?\?\s*''/)
  })

  it('still emits AppShell + Sidebar at src/components/', () => {
    const files = generate(spec)
    expect(files.some((f) => f.path === 'src/components/AppShell.tsx')).toBe(true)
    expect(files.some((f) => f.path === 'src/components/Sidebar.tsx')).toBe(true)
  })

  it('Fix #6 — emits no `: JSX.Element` return annotations (TS infers from JSX)', () => {
    // Under `verbatimModuleSyntax: true`, `JSX` is not a global and would
    // need `import type { JSX } from 'react'`. Cleanest is to omit the
    // annotation — TS infers correctly from the JSX return.
    const files = generate(spec)
    for (const file of files.filter(f => f.path.endsWith('.tsx'))) {
      expect(file.content, file.path).not.toMatch(/\):\s*JSX\.Element\s*\{/)
    }
  })

  it('Fix #6 — emits `import type { ReactNode }` (not `import { ReactNode }`)', () => {
    // Under verbatimModuleSyntax: true, value imports of types fail
    // compilation with `'ReactNode' is a type and must be imported using a
    // type-only import`. Force type-only imports everywhere ReactNode shows up.
    const files = generate(spec)
    for (const file of files.filter(f => f.path.endsWith('.tsx'))) {
      // Negative: a bare value import of ReactNode must not appear.
      expect(file.content, file.path).not.toMatch(/import\s*\{\s*ReactNode\b[^}]*\}\s*from\s*'react'/)
    }
  })

  it('exposes the two-width contract (width prop, responsive cap + gutter, default standard)', () => {
    const files = generate(spec)
    const pt = files.find((f) => f.path.endsWith('PageTemplate.tsx'))!
    // The only two sanctioned widths.
    expect(pt.content).toMatch(/width\?\s*:\s*'standard'\s*\|\s*'focused'/)
    // Defaults to standard (full-width) so existing pages are unchanged.
    expect(pt.content).toMatch(/width\s*=\s*'standard'/)
    // focused → centered column capped on the responsive (rem) token, never a px literal.
    expect(pt.content).toContain('max-w-[var(--content-max-narrow)]')
    // Horizontal gutter so content clears the edge chrome (doc launcher / sidebar handle).
    expect(pt.content).toContain('var(--content-gutter-x)')
  })
})
