import { describe, expect, it } from 'vitest'
import { defaultNamespace, generate, globalAcRef, methodSlug, pascal } from '../generate.js'
import type { ScaffoldTestsFromAcSpec, UcWithAc } from '../types.js'

function uc(overrides: Partial<UcWithAc> = {}): UcWithAc {
  return {
    ucCode: 'UC-CRM-PIPELINE-OPPORTUNITES-001',
    title: 'Créer une opportunité',
    sectionCode: 'OPPORTUNITES',
    sectionFolder: 'opportunites',
    sourceFile: 'opportunites/use-case.md',
    acs: [{ localId: 'AC-01', text: 'POST returns 201.' }],
    ...overrides,
  }
}

const baseSpec: ScaffoldTestsFromAcSpec = {
  moduleDir: '/tmp/.smartstack/ba/CRM/PIPELINE',
  projectPath: '/tmp/my-app',
  appCode: 'crm',
  module: 'pipeline',
  dryRun: true,
}

describe('generate — helpers', () => {
  it('pascal converts kebab and snake', () => {
    expect(pascal('opportunites')).toBe('Opportunites')
    expect(pascal('opportunites-rdv')).toBe('OpportunitesRdv')
    expect(pascal('exchange_history')).toBe('ExchangeHistory')
  })

  it('methodSlug strips accents + special chars', () => {
    expect(methodSlug('POST renvoie 201 + id créé')).toMatch(/^post_renvoie_201/)
    expect(methodSlug('Montant négatif → 400')).toContain('montant_negatif')
  })

  it('methodSlug caps the number of words', () => {
    const text = 'one two three four five six seven eight nine ten'
    const slug = methodSlug(text, 5)
    expect(slug).toBe('one_two_three_four_five')
  })

  it('globalAcRef joins UC code + AC localId with #', () => {
    expect(globalAcRef('UC-CRM-PIPELINE-OPP-001', 'AC-02')).toBe('UC-CRM-PIPELINE-OPP-001#AC-02')
  })

  it('defaultNamespace is Pascal.Tests.Pascal.Acceptance', () => {
    expect(defaultNamespace('crm', 'pipeline')).toBe('Crm.Tests.Pipeline.Acceptance')
    expect(defaultNamespace('my-app', 'human-resources')).toBe('MyApp.Tests.HumanResources.Acceptance')
  })
})

describe('generate — file emission', () => {
  it('emits one file per section, with correct path', () => {
    const ucs: UcWithAc[] = [
      uc({ sectionFolder: 'opportunites' }),
      uc({
        ucCode: 'UC-CRM-PIPELINE-CLIENTS-001',
        sectionFolder: 'clients',
        sectionCode: 'CLIENTS',
        title: 'Lister les clients',
        acs: [{ localId: 'AC-01', text: 'GET returns 200.' }],
      }),
    ]
    const { files } = generate({ spec: baseSpec, ucs })
    expect(files).toHaveLength(2)
    const paths = files.map(f => f.path).sort()
    expect(paths).toEqual([
      'Tests/Pipeline/Acceptance/ClientsAcceptanceTests.cs',
      'Tests/Pipeline/Acceptance/OpportunitesAcceptanceTests.cs',
    ])
  })

  it('skips sections with zero ACs across their UCs', () => {
    const ucs: UcWithAc[] = [uc({ acs: [] })]
    const { files, factsEmitted } = generate({ spec: baseSpec, ucs })
    expect(files).toHaveLength(0)
    expect(factsEmitted).toBe(0)
  })

  it('counts UCs scanned and ACs parsed correctly', () => {
    const ucs: UcWithAc[] = [
      uc({
        acs: [
          { localId: 'AC-01', text: 'a' },
          { localId: 'AC-02', text: 'b' },
        ],
      }),
      uc({
        ucCode: 'UC-CRM-PIPELINE-OPPORTUNITES-002',
        acs: [{ localId: 'AC-01', text: 'c' }],
      }),
      uc({
        ucCode: 'UC-CRM-PIPELINE-CLIENTS-001',
        sectionFolder: 'clients',
        sectionCode: 'CLIENTS',
        acs: [],
      }),
    ]
    const { ucsScanned, acsParsed, factsEmitted } = generate({ spec: baseSpec, ucs })
    expect(ucsScanned).toBe(3)
    expect(acsParsed).toBe(3)
    expect(factsEmitted).toBe(3)
  })

  it('honors namespace override', () => {
    const ucs: UcWithAc[] = [uc()]
    const { files } = generate({
      spec: { ...baseSpec, namespace: 'My.Custom.Ns.Acceptance' },
      ucs,
    })
    expect(files[0]!.content).toContain('namespace My.Custom.Ns.Acceptance;')
  })
})

describe('generate — emitted code shape', () => {
  it('emits Trait attributes with the global AC ref', () => {
    const { files } = generate({
      spec: baseSpec,
      ucs: [uc({ acs: [{ localId: 'AC-02', text: 'POST returns 400.' }] })],
    })
    const code = files[0]!.content
    expect(code).toContain(`[Trait("Category", "Acceptance")]`)
    expect(code).toContain(`[Trait("Module", "Pipeline")]`)
    expect(code).toContain(`[Trait("AC", "UC-CRM-PIPELINE-OPPORTUNITES-001#AC-02")]`)
  })

  it('embeds the verbatim AC text in a comment', () => {
    const { files } = generate({
      spec: baseSpec,
      ucs: [uc({ acs: [{ localId: 'AC-01', text: 'verbatim AC text 123' }] })],
    })
    expect(files[0]!.content).toContain('// AC: verbatim AC text 123')
  })

  it('embeds the // TODO[AC-NN] marker and Assert.Fail', () => {
    const { files } = generate({
      spec: baseSpec,
      ucs: [uc({ acs: [{ localId: 'AC-01', text: 'X' }] })],
    })
    const code = files[0]!.content
    expect(code).toContain('// TODO[AC-01]:')
    expect(code).toContain('Assert.Fail(')
  })

  it('escapes double quotes in AC text before injecting into Assert.Fail', () => {
    const { files } = generate({
      spec: baseSpec,
      ucs: [uc({ acs: [{ localId: 'AC-01', text: 'returns "ok"' }] })],
    })
    // Assert.Fail uses the global ref, not the AC text directly, but the
    // comment carries the raw AC text — verify both are present and quotes are
    // not breaking the class.
    const code = files[0]!.content
    expect(code).toContain('// AC: returns "ok"')
    expect(code).not.toMatch(/Assert\.Fail\("[^"]*returns "ok"/)
  })

  it('emits a section header comment per UC', () => {
    const { files } = generate({
      spec: baseSpec,
      ucs: [
        uc(),
        uc({
          ucCode: 'UC-CRM-PIPELINE-OPPORTUNITES-002',
          title: 'Modifier',
          acs: [{ localId: 'AC-01', text: 'PUT returns 200.' }],
        }),
      ],
    })
    const code = files[0]!.content
    expect(code).toContain('// ==== UC-CRM-PIPELINE-OPPORTUNITES-001 — Créer une opportunité ====')
    expect(code).toContain('// ==== UC-CRM-PIPELINE-OPPORTUNITES-002 — Modifier ====')
  })

  it('emits the IClassFixture<WebApplicationFactory<Program>> fixture', () => {
    const { files } = generate({ spec: baseSpec, ucs: [uc()] })
    const code = files[0]!.content
    expect(code).toContain('IClassFixture<WebApplicationFactory<Program>>')
    expect(code).toContain('private readonly HttpClient _client;')
    expect(code).toContain('_client = factory.CreateClient();')
  })

  it('emits the auto-generated banner comment', () => {
    const { files } = generate({ spec: baseSpec, ucs: [uc()] })
    expect(files[0]!.content.startsWith('// <auto-generated by scaffold-tests-from-ac />')).toBe(true)
  })
})
