/**
 * cli:scaffold-tests-from-ac — parse-ac.ts
 *
 * Pure parser for `use-case.md` → array of `UcWithAc`. No filesystem, no I/O.
 * Reads ONE file's text + its source path metadata; the caller does the Glob.
 *
 * Grammar handled (see `_workflow/doc-templates.md`):
 *
 *   ### UC-{APP}-{MOD}-{SEC}-NNN — Title
 *   - **Acteur principal** : …
 *   …
 *   - **Acceptance Criteria** :
 *     - [ ] AC-01 — Assertion 1.
 *     - [ ] AC-02 — Assertion 2.
 *
 * Tolerant of:
 *   - missing `**Acceptance Criteria**` field (returns acs: [])
 *   - extra whitespace
 *   - the field appearing in any position among the UC bullets
 *   - both 4-space and tabbed indentation under the AC field
 *
 * Strict on:
 *   - `AC-NN` MUST match `^AC-\d{2}$` (UC-013 audit)
 *   - `- [ ] AC-NN — …` separator is the em-dash ` — ` (U+2014, what
 *     `/ba-create-use-case` writes) OR a plain ASCII ` - ` (graceful fallback)
 */

import type { Ac, UcWithAc } from './types.js'

/** Extract the section folder code from a UC code (uppercase form → kebab). */
function deriveSectionFolder(ucCode: string): string {
  // UC-CRM-PIPELINE-OPPORTUNITES-001  →  ['UC','CRM','PIPELINE','OPPORTUNITES','001']
  const parts = ucCode.split('-')
  if (parts.length < 5) return ''
  // section = part [3], kebab = lowercase with `_` → `-`
  return parts[3]!.toLowerCase().replace(/_/g, '-')
}

/** Extract the SECTION_CODE (UPPERCASE) — segment [3] of UC-APP-MOD-SEC-NNN. */
function deriveSectionCode(ucCode: string): string {
  const parts = ucCode.split('-')
  return parts.length >= 5 ? parts[3]! : ''
}

/** Match a UC heading and capture (code, title). */
const UC_HEADING_RE = /^###\s+(UC-[A-Z0-9_]+-[A-Z0-9_]+-[A-Z0-9_]+-\d{3})\s+[—\-]\s+(.+?)\s*$/

/** Match the `**Acceptance Criteria**` field opener. */
const AC_FIELD_OPENER_RE = /^-\s*\*\*Acceptance\s+Criteria\*\*\s*:\s*$/i

/** Match `**<Field>**` bullets that end the AC field block. */
const FIELD_BULLET_RE = /^-\s*\*\*[^*]+\*\*\s*:/

/**
 * Match a single AC bullet :
 *   `  - [ ] AC-NN — text`     (4 spaces, em-dash)
 *   `\t- [x] AC-NN - text`     (tab, ASCII dash)
 * Captures (localId, text).
 */
const AC_BULLET_RE = /^\s+-\s*\[[ xX]?\]\s*(AC-\d{2})\s+[—\-]\s+(.+?)\s*$/

export interface ParseAcResult {
  ucs: UcWithAc[]
  warnings: string[]
}

/**
 * Parse one `use-case.md` file content.
 *
 * @param text     - Raw markdown content of the file.
 * @param relPath  - Source file path RELATIVE to moduleDir (e.g. `opportunites/use-case.md`).
 *                   The section folder is taken from the first path segment.
 */
export function parseAcFromUseCaseMd(text: string, relPath: string): ParseAcResult {
  const ucs: UcWithAc[] = []
  const warnings: string[] = []

  // Section folder is the path segment OWNING use-case.md
  // e.g. `opportunites/use-case.md` → `opportunites`
  // The UC heading will confirm via its 4th segment (UPPERCASE).
  const pathSectionFolder = relPath.split(/[/\\]/).slice(-2, -1)[0] ?? ''

  const lines = text.split(/\r?\n/)
  let i = 0
  while (i < lines.length) {
    const line = lines[i]!
    const headingMatch = line.match(UC_HEADING_RE)
    if (!headingMatch) {
      i++
      continue
    }

    const ucCode = headingMatch[1]!
    const title = headingMatch[2]!
    const sectionCode = deriveSectionCode(ucCode)
    const derivedFolder = deriveSectionFolder(ucCode)

    // Cross-check section folder consistency
    if (derivedFolder && pathSectionFolder && derivedFolder !== pathSectionFolder) {
      warnings.push(
        `${relPath}: UC ${ucCode} declares section "${derivedFolder}" but lives under folder "${pathSectionFolder}"`
      )
    }

    // Scan forward for the AC field within THIS UC's bullet block.
    // Stop at next UC heading, next H1/H2 heading, or EOF.
    let j = i + 1
    let acs: Ac[] = []
    let foundAcField = false
    while (j < lines.length) {
      const lj = lines[j]!
      if (UC_HEADING_RE.test(lj) || /^#{1,2}\s/.test(lj)) break

      if (AC_FIELD_OPENER_RE.test(lj)) {
        foundAcField = true
        // Consume bullets indented under this field
        let k = j + 1
        const seenIds = new Set<string>()
        while (k < lines.length) {
          const lk = lines[k]!
          // Stop on next top-level UC bullet (- **Field**:) or UC heading or H1/H2
          if (UC_HEADING_RE.test(lk) || /^#{1,2}\s/.test(lk)) break
          if (FIELD_BULLET_RE.test(lk)) break
          // Empty line: tolerate
          if (lk.trim() === '') {
            k++
            continue
          }
          const acMatch = lk.match(AC_BULLET_RE)
          if (acMatch) {
            const localId = acMatch[1]!
            const acText = acMatch[2]!
            if (seenIds.has(localId)) {
              warnings.push(`${relPath}: UC ${ucCode} duplicates ${localId}`)
            } else {
              seenIds.add(localId)
              acs.push({ localId, text: acText })
            }
          } else if (/^\s+-/.test(lk)) {
            // bullet that does not match AC-NN — flag as malformed
            warnings.push(
              `${relPath}: UC ${ucCode} has a malformed AC bullet: "${lk.trim()}". Expected "- [ ] AC-NN — text".`
            )
          }
          k++
        }
        j = k
        continue
      }
      j++
    }

    if (!foundAcField) {
      warnings.push(
        `${relPath}: UC ${ucCode} has no **Acceptance Criteria** field (will emit no [Fact]; audit UC-012 may flag this).`
      )
    }

    ucs.push({
      ucCode,
      title,
      sectionCode,
      sectionFolder: derivedFolder || pathSectionFolder,
      sourceFile: relPath,
      acs,
    })

    i = j
  }

  return { ucs, warnings }
}
