import { describe, expect, it } from 'vitest'
import { parseAcFromUseCaseMd } from '../parse-ac.js'

describe('parse-ac — happy path', () => {
  it('parses one UC with three ACs (em-dash separator)', () => {
    const md = `<!-- ba:use-case level=section code=opportunites -->
# Cas d'usage — CRM / PIPELINE / opportunites

### UC-CRM-PIPELINE-OPPORTUNITES-001 — Créer une opportunité
- **Acteur principal** : BA-001-AC-001 (Commercial)
- **Préconditions** : le prospect existe.
- **Flux principal** :
  1. Le commercial ouvre le formulaire.
- **Postconditions** : opportunité visible.
- **Acceptance Criteria** :
  - [ ] AC-01 — POST /api/opportunities avec un body valide renvoie 201 + l'identifiant créé.
  - [ ] AC-02 — POST avec un montant négatif renvoie 400 et le code d'erreur \`amount-positive\`.
  - [ ] AC-03 — La nouvelle opportunité apparaît dans GET /api/opportunities.
`
    const { ucs, warnings } = parseAcFromUseCaseMd(md, 'opportunites/use-case.md')
    expect(ucs).toHaveLength(1)
    const uc = ucs[0]!
    expect(uc.ucCode).toBe('UC-CRM-PIPELINE-OPPORTUNITES-001')
    expect(uc.title).toBe('Créer une opportunité')
    expect(uc.sectionCode).toBe('OPPORTUNITES')
    expect(uc.sectionFolder).toBe('opportunites')
    expect(uc.acs).toHaveLength(3)
    expect(uc.acs[0]!.localId).toBe('AC-01')
    expect(uc.acs[0]!.text).toContain('POST /api/opportunities')
    expect(uc.acs[1]!.localId).toBe('AC-02')
    expect(uc.acs[2]!.localId).toBe('AC-03')
    expect(warnings).toEqual([])
  })

  it('parses multiple UCs in the same file', () => {
    const md = `### UC-CRM-PIPELINE-OPPORTUNITES-001 — Créer
- **Acteur principal** : X
- **Acceptance Criteria** :
  - [ ] AC-01 — Texte 1.

### UC-CRM-PIPELINE-OPPORTUNITES-002 — Modifier
- **Acteur principal** : Y
- **Acceptance Criteria** :
  - [ ] AC-01 — Texte 2.
  - [ ] AC-02 — Texte 3.
`
    const { ucs } = parseAcFromUseCaseMd(md, 'opportunites/use-case.md')
    expect(ucs).toHaveLength(2)
    expect(ucs[0]!.acs).toHaveLength(1)
    expect(ucs[1]!.acs).toHaveLength(2)
  })

  it('tolerates ASCII dash as separator (graceful fallback)', () => {
    const md = `### UC-CRM-PIPELINE-OPP-001 - Plain dash title
- **Acceptance Criteria** :
  - [ ] AC-01 - Plain dash AC text.
`
    const { ucs } = parseAcFromUseCaseMd(md, 'opp/use-case.md')
    expect(ucs).toHaveLength(1)
    expect(ucs[0]!.title).toBe('Plain dash title')
    expect(ucs[0]!.acs).toHaveLength(1)
    expect(ucs[0]!.acs[0]!.text).toBe('Plain dash AC text.')
  })

  it('handles tabbed indentation under the AC field', () => {
    const md = `### UC-CRM-PIPELINE-OPP-001 — Title
- **Acceptance Criteria** :
\t- [ ] AC-01 — Tab-indented AC.
`
    const { ucs } = parseAcFromUseCaseMd(md, 'opp/use-case.md')
    expect(ucs[0]!.acs).toHaveLength(1)
    expect(ucs[0]!.acs[0]!.text).toBe('Tab-indented AC.')
  })

  it('handles checked boxes ([x] and [X])', () => {
    const md = `### UC-CRM-PIPELINE-OPP-001 — Title
- **Acceptance Criteria** :
  - [x] AC-01 — Lower-x checkbox.
  - [X] AC-02 — Upper-X checkbox.
  - [ ] AC-03 — Empty checkbox.
`
    const { ucs } = parseAcFromUseCaseMd(md, 'opp/use-case.md')
    expect(ucs[0]!.acs).toHaveLength(3)
  })
})

describe('parse-ac — error / warning paths', () => {
  it('warns when a UC has no Acceptance Criteria field', () => {
    const md = `### UC-CRM-PIPELINE-OPP-001 — Title
- **Acteur principal** : X
- **Postconditions** : Y
`
    const { ucs, warnings } = parseAcFromUseCaseMd(md, 'opp/use-case.md')
    expect(ucs).toHaveLength(1)
    expect(ucs[0]!.acs).toEqual([])
    expect(warnings.some(w => w.includes('no **Acceptance Criteria**'))).toBe(true)
  })

  it('warns on duplicate AC ids within one UC', () => {
    const md = `### UC-CRM-PIPELINE-OPP-001 — Title
- **Acceptance Criteria** :
  - [ ] AC-01 — First.
  - [ ] AC-01 — Duplicate.
`
    const { ucs, warnings } = parseAcFromUseCaseMd(md, 'opp/use-case.md')
    expect(ucs[0]!.acs).toHaveLength(1) // duplicate dropped
    expect(warnings.some(w => w.includes('duplicates AC-01'))).toBe(true)
  })

  it('warns on malformed AC bullet (missing AC-NN id)', () => {
    const md = `### UC-CRM-PIPELINE-OPP-001 — Title
- **Acceptance Criteria** :
  - [ ] no-id — bad bullet.
`
    const { ucs, warnings } = parseAcFromUseCaseMd(md, 'opp/use-case.md')
    expect(ucs[0]!.acs).toEqual([])
    expect(warnings.some(w => w.includes('malformed AC bullet'))).toBe(true)
  })

  it('warns when section folder in path does not match UC code', () => {
    const md = `### UC-CRM-PIPELINE-OPPORTUNITES-001 — Title
- **Acceptance Criteria** :
  - [ ] AC-01 — text.
`
    const { warnings } = parseAcFromUseCaseMd(md, 'wrong-folder/use-case.md')
    expect(warnings.some(w => w.includes('section "opportunites"') && w.includes('"wrong-folder"'))).toBe(true)
  })

  it('does NOT bleed ACs across UC headings', () => {
    const md = `### UC-CRM-PIPELINE-OPP-001 — A
- **Acceptance Criteria** :
  - [ ] AC-01 — Belongs to A.

### UC-CRM-PIPELINE-OPP-002 — B
- **Acteur principal** : X
`
    const { ucs } = parseAcFromUseCaseMd(md, 'opp/use-case.md')
    expect(ucs).toHaveLength(2)
    expect(ucs[0]!.acs).toHaveLength(1)
    expect(ucs[1]!.acs).toHaveLength(0)
  })

  it('does NOT keep AC bullets after the next field of the same UC', () => {
    const md = `### UC-CRM-PIPELINE-OPP-001 — A
- **Acceptance Criteria** :
  - [ ] AC-01 — Real AC.
- **Règles liées** : BR-001
  - [ ] AC-02 — Stray bullet under another field.
`
    const { ucs } = parseAcFromUseCaseMd(md, 'opp/use-case.md')
    expect(ucs[0]!.acs).toHaveLength(1)
    expect(ucs[0]!.acs[0]!.localId).toBe('AC-01')
  })

  it('returns empty result on a file with no UC heading', () => {
    const md = `# Rollup
> Voir les cas d'usage par section.
`
    const { ucs, warnings } = parseAcFromUseCaseMd(md, 'use-case.md')
    expect(ucs).toEqual([])
    expect(warnings).toEqual([])
  })
})

describe('parse-ac — robustness', () => {
  it('rejects an AC id with only 1 digit', () => {
    const md = `### UC-CRM-PIPELINE-OPP-001 — Title
- **Acceptance Criteria** :
  - [ ] AC-1 — Wrong id (single digit).
`
    const { ucs, warnings } = parseAcFromUseCaseMd(md, 'opp/use-case.md')
    expect(ucs[0]!.acs).toEqual([])
    expect(warnings.some(w => w.includes('malformed AC bullet'))).toBe(true)
  })

  it('rejects an AC id with 3 digits', () => {
    const md = `### UC-CRM-PIPELINE-OPP-001 — Title
- **Acceptance Criteria** :
  - [ ] AC-001 — Wrong id (3 digits).
`
    const { ucs } = parseAcFromUseCaseMd(md, 'opp/use-case.md')
    expect(ucs[0]!.acs).toEqual([])
  })

  it('handles CRLF line endings', () => {
    const md = `### UC-CRM-PIPELINE-OPP-001 — Title\r\n- **Acceptance Criteria** :\r\n  - [ ] AC-01 — CRLF text.\r\n`
    const { ucs } = parseAcFromUseCaseMd(md, 'opp/use-case.md')
    expect(ucs[0]!.acs).toHaveLength(1)
    expect(ucs[0]!.acs[0]!.text).toBe('CRLF text.')
  })
})
