/**
 * parse.test.ts — Tests for entité.md parsing and dependency extraction.
 */
import { describe, it, expect } from 'vitest';
import { parseEntities } from '../parse.js';

// We test parseEntities directly (exported) and use snapshot-style
// content strings to test the full module parsing flow indirectly.

describe('parseEntities', () => {
  it('extracts a single entity with classification', () => {
    const content = `### ENT-001 — Opportunity (agrégat racine)
- **Préfixe table** : \`pipeline_\`
`;
    const entities = parseEntities(content);
    expect(entities).toHaveLength(1);
    expect(entities[0]).toEqual({
      code: 'ENT-001',
      name: 'Opportunity',
      classification: 'agrégat racine',
    });
  });

  it('extracts multiple entities', () => {
    const content = `### ENT-001 — Opportunity (agrégat racine)
- **Préfixe table** : \`pipeline_\`

### ENT-002 — OpportunitySequence (technical)
- **Préfixe table** : \`pipeline_\`

### ENT-003 — Deal (composant)
- **Préfixe table** : \`pipeline_\`
`;
    const entities = parseEntities(content);
    expect(entities).toHaveLength(3);
    expect(entities[0].name).toBe('Opportunity');
    expect(entities[1].name).toBe('OpportunitySequence');
    expect(entities[1].classification).toBe('technical');
    expect(entities[2].name).toBe('Deal');
  });

  it('handles entity without classification', () => {
    const content = `### ENT-001 — Contact
- **Préfixe table** : \`crm_\`
`;
    const entities = parseEntities(content);
    expect(entities).toHaveLength(1);
    expect(entities[0]).toEqual({
      code: 'ENT-001',
      name: 'Contact',
      classification: undefined,
    });
  });

  it('returns empty array for placeholder content', () => {
    const content = `<!-- ba:entité level=module code=PIPELINE -->
_À définir lors de la phase « modèle de données »._
`;
    const entities = parseEntities(content);
    expect(entities).toHaveLength(0);
  });

  it('returns empty array for empty content', () => {
    expect(parseEntities('')).toHaveLength(0);
  });
});

// ---------------------------------------------------------------------------
// Cross-module FK extraction (tested via content patterns)
// ---------------------------------------------------------------------------

describe('cross-module FK patterns', () => {
  // These tests verify the regex patterns used in parse.ts by matching
  // against representative content snippets.

  const CROSS_MODULE_FK_RE =
    /FK\s+cross[- ]?module\s+vers\s+([A-Z][A-Z0-9_-]*)\/([A-Z][A-Z0-9_-]*)\.(\w+)/i;

  const CORE_REF_RE =
    /(?:FK\s+(?:Core\s+)?)?(?:cross[- ]?module\s+vers\s+)?(auth_\w+|nav_\w+|tnt_\w+)/i;

  it('matches standard cross-module FK', () => {
    const line = 'FK cross-module vers CRM/CONTACTS.Contact';
    const match = CROSS_MODULE_FK_RE.exec(line);
    expect(match).not.toBeNull();
    expect(match![1]).toBe('CRM');
    expect(match![2]).toBe('CONTACTS');
    expect(match![3]).toBe('Contact');
  });

  it('matches without hyphen (cross module)', () => {
    const line = 'FK cross module vers BILLING/INVOICES.Invoice';
    const match = CROSS_MODULE_FK_RE.exec(line);
    expect(match).not.toBeNull();
    expect(match![1]).toBe('BILLING');
    expect(match![2]).toBe('INVOICES');
    expect(match![3]).toBe('Invoice');
  });

  it('matches crossmodule (no space)', () => {
    const line = 'FK crossmodule vers HR/EMPLOYEES.Employee';
    const match = CROSS_MODULE_FK_RE.exec(line);
    expect(match).not.toBeNull();
    expect(match![1]).toBe('HR');
    expect(match![2]).toBe('EMPLOYEES');
    expect(match![3]).toBe('Employee');
  });

  it('matches Core auth_ ref', () => {
    const line = 'FK Core auth_Users';
    const match = CORE_REF_RE.exec(line);
    expect(match).not.toBeNull();
    expect(match![1]).toBe('auth_Users');
  });

  it('matches nav_ ref', () => {
    const line = 'FK cross-module vers nav_Applications';
    const match = CORE_REF_RE.exec(line);
    expect(match).not.toBeNull();
    expect(match![1]).toBe('nav_Applications');
  });

  it('matches tnt_ ref', () => {
    const line = 'tnt_Tenants';
    const match = CORE_REF_RE.exec(line);
    expect(match).not.toBeNull();
    expect(match![1]).toBe('tnt_Tenants');
  });

  it('does not match regular FK description', () => {
    const line = '≥ 0';
    expect(CROSS_MODULE_FK_RE.exec(line)).toBeNull();
  });

  it('does not match PK', () => {
    const line = 'PK';
    expect(CROSS_MODULE_FK_RE.exec(line)).toBeNull();
  });
});

// ---------------------------------------------------------------------------
// Readiness verdict parsing
// ---------------------------------------------------------------------------

describe('readiness verdict regex', () => {
  const VERDICT_RE =
    /Verdict\s*:\s*(GO|NO-GO)(?:.*?score\s+(\d+))?(?:.*?(\d+)\s+err)?(?:.*?(\d+)\s+warn)?/;

  it('parses GO verdict with full details', () => {
    const line = '_2026-05-25 · Verdict : GO · score 88/100 · 0 err · 2 warn · 15 ok_';
    const match = VERDICT_RE.exec(line);
    expect(match).not.toBeNull();
    expect(match![1]).toBe('GO');
    expect(match![2]).toBe('88');
    expect(match![3]).toBe('0');
    expect(match![4]).toBe('2');
  });

  it('parses NO-GO verdict', () => {
    const line = '_2026-05-25 · Verdict : NO-GO · score 52/100 · 3 err · 5 warn · 8 ok_';
    const match = VERDICT_RE.exec(line);
    expect(match).not.toBeNull();
    expect(match![1]).toBe('NO-GO');
    expect(match![2]).toBe('52');
    expect(match![3]).toBe('3');
    expect(match![4]).toBe('5');
  });

  it('parses GO without score', () => {
    const line = '_2026-05-25 · Verdict : GO · 0 err · 1 warn_';
    const match = VERDICT_RE.exec(line);
    expect(match).not.toBeNull();
    expect(match![1]).toBe('GO');
    expect(match![2]).toBeUndefined();
    expect(match![3]).toBe('0');
  });
});
