#!/usr/bin/env bun
import { existsSync, mkdirSync, readdirSync, readFileSync, writeFileSync } from 'fs'
import { join } from 'path'

const root = process.cwd()
const contentRoot = join(root, 'data', 'content')
const productsRoot = join(contentRoot, 'products')
const decisionRoot = join(contentRoot, 'compliance')
const decisionPath = join(decisionRoot, 'clinical-blank-decisions.json')
const decisionCsvPath = join(decisionRoot, 'clinical-blank-decisions.csv')
const outRoot = join(root, 'content_population_exports')
const generatedAt = new Date().toISOString()

function readJson(path: string, fallback: any = null) {
  if (!existsSync(path)) return fallback
  return JSON.parse(readFileSync(path, 'utf8').replace(/^\uFEFF/, ''))
}

function csv(rows: any[], headers: string[]) {
  const esc = (value: any) => {
    if (Array.isArray(value)) value = value.join(' | ')
    if (value === undefined || value === null) value = ''
    value = String(value)
    return /[",\n\r]/.test(value) ? `"${value.replace(/"/g, '""')}"` : value
  }
  return [headers.join(','), ...rows.map(row => headers.map(header => esc(row[header])).join(','))].join('\n') + '\n'
}
function parseCsv(text: string) {
  const rows: string[][] = []
  let row: string[] = []
  let cell = ''
  let quoted = false
  for (let i = 0; i < text.length; i++) {
    const ch = text[i]
    const next = text[i + 1]
    if (quoted && ch === '"' && next === '"') {
      cell += '"'
      i += 1
    } else if (ch === '"') {
      quoted = !quoted
    } else if (!quoted && ch === ',') {
      row.push(cell)
      cell = ''
    } else if (!quoted && (ch === '\n' || ch === '\r')) {
      if (ch === '\r' && next === '\n') i += 1
      row.push(cell)
      if (row.some(value => value !== '')) rows.push(row)
      row = []
      cell = ''
    } else {
      cell += ch
    }
  }
  if (cell || row.length) {
    row.push(cell)
    if (row.some(value => value !== '')) rows.push(row)
  }
  const headers = rows.shift() || []
  return rows.map(values => Object.fromEntries(headers.map((header, index) => [header.replace(/^\uFEFF/, ''), values[index] ?? ''])))
}
function bool(value: any) {
  return value === true || String(value || '').toLowerCase() === 'true' || String(value || '').toLowerCase() === 'yes'
}

mkdirSync(decisionRoot, { recursive: true })
mkdirSync(outRoot, { recursive: true })

const existing = readJson(decisionPath, { decisions: [] })
const csvExisting = existsSync(decisionCsvPath)
  ? parseCsv(readFileSync(decisionCsvPath, 'utf8')).map((row: any) => ({
      sku: row.sku || row.SKU,
      decision: row.decision || row.Decision,
      approvedBy: row.approvedBy || row.ApprovedBy,
      approvedAt: row.approvedAt || row.ApprovedAt,
      rationale: row.rationale || row.Rationale,
      evidencePath: row.evidencePath || row.EvidencePath,
      constraintsAcknowledged: bool(row.constraintsAcknowledged || row.ConstraintsAcknowledged),
      note: row.note || row.Note
    }))
  : []
const existingBySku = new Map([...(existing.decisions || []), ...csvExisting].map((row: any) => [row.sku, row]))
const products = readdirSync(productsRoot)
  .filter(file => file.endsWith('.json'))
  .sort()
  .map(file => readJson(join(productsRoot, file), {}))

const decisions = products
  .filter(product => !product.metafields?.pdpClinicallyShownTo)
  .map(product => {
    const previous: any = existingBySku.get(product.sku) || {}
    return {
      sku: product.sku,
      productName: product.name || '',
      handle: product.handle || '',
      decision: previous.decision || 'pending',
      approvedBy: previous.approvedBy || '',
      approvedAt: previous.approvedAt || '',
      rationale: previous.rationale || '',
      evidencePath: previous.evidencePath || `data/intelligence/${product.sku}/claims-evidence.json`,
      constraintsAcknowledged: Boolean(previous.constraintsAcknowledged),
      note: previous.note || 'Use decision=approved_blank only when compliance explicitly approves that this SKU should ship without pdp.clinically_shown_to. Do not use this to bypass missing evidence.'
    }
  })

const validBlankApprovals = decisions.filter(row =>
  row.decision === 'approved_blank' &&
  row.approvedBy &&
  row.approvedAt &&
  row.rationale &&
  row.constraintsAcknowledged === true
)
const invalidApprovedBlank = decisions.filter(row =>
  row.decision === 'approved_blank' &&
  !validBlankApprovals.some(valid => valid.sku === row.sku)
)

const decisionFile = {
  generatedAt,
  purpose: 'Compliance-owner decisions for SKUs where clinically_shown_to is intentionally blank.',
  allowedDecisions: ['pending', 'approved_blank', 'requires_source_backed_claim'],
  guardrail: 'A blank clinical-claim field is export-safe only when decision=approved_blank, approvedBy/approvedAt/rationale are filled, and constraintsAcknowledged=true.',
  decisions
}
writeFileSync(decisionPath, JSON.stringify(decisionFile, null, 2) + '\n', 'utf8')

const rows = decisions.map(row => ({
  SKU: row.sku,
  ProductName: row.productName,
  Decision: row.decision,
  ApprovedBy: row.approvedBy,
  ApprovedAt: row.approvedAt,
  ConstraintsAcknowledged: row.constraintsAcknowledged ? 'yes' : 'no',
  ValidBlankApproval: validBlankApprovals.some(valid => valid.sku === row.sku) ? 'yes' : 'no',
  Rationale: row.rationale,
  EvidencePath: row.evidencePath,
  Note: row.note
}))

const manifest = {
  generatedAt,
  ready: decisions.length > 0 && validBlankApprovals.length === decisions.length,
  decisionFile: 'data/content/compliance/clinical-blank-decisions.json',
  counts: {
    decisions: decisions.length,
    pending: decisions.filter(row => row.decision === 'pending').length,
    requiresSourceBackedClaim: decisions.filter(row => row.decision === 'requires_source_backed_claim').length,
    validBlankApprovals: validBlankApprovals.length,
    invalidApprovedBlank: invalidApprovedBlank.length
  },
  guardrail: decisionFile.guardrail,
  rows
}

writeFileSync(join(outRoot, 'clinical_blank_decision_template.json'), JSON.stringify(manifest, null, 2) + '\n', 'utf8')
writeFileSync(join(outRoot, 'clinical_blank_decision_template.csv'), csv(rows, ['SKU', 'ProductName', 'Decision', 'ApprovedBy', 'ApprovedAt', 'ConstraintsAcknowledged', 'ValidBlankApproval', 'Rationale', 'EvidencePath', 'Note']), 'utf8')
writeFileSync(decisionCsvPath, csv(rows.map(row => ({
  sku: row.SKU,
  productName: row.ProductName,
  decision: row.Decision,
  approvedBy: row.ApprovedBy,
  approvedAt: row.ApprovedAt,
  rationale: row.Rationale,
  evidencePath: row.EvidencePath,
  constraintsAcknowledged: row.ConstraintsAcknowledged,
  note: row.Note
})), ['sku', 'productName', 'decision', 'approvedBy', 'approvedAt', 'rationale', 'evidencePath', 'constraintsAcknowledged', 'note']), 'utf8')
writeFileSync(join(outRoot, 'clinical_blank_decision_template.md'), [
  '# Clinical blank-decision template',
  '',
  `Generated: ${generatedAt}`,
  '',
  'This is the approval template for SKUs that should intentionally ship with a blank `pdp.clinically_shown_to` field. It does not approve anything by itself.',
  '',
  `Decision file: \`${manifest.decisionFile}\``,
  `Editable CSV: \`data/content/compliance/clinical-blank-decisions.csv\``,
  '',
  '## Guardrail',
  manifest.guardrail,
  '',
  '## Counts',
  `- Decisions: ${manifest.counts.decisions}`,
  `- Pending: ${manifest.counts.pending}`,
  `- Requires source-backed claim: ${manifest.counts.requiresSourceBackedClaim}`,
  `- Valid blank approvals: ${manifest.counts.validBlankApprovals}`,
  `- Invalid approved_blank rows: ${manifest.counts.invalidApprovedBlank}`,
  '',
  '## How to use',
  '1. Compliance owner reviews each SKU.',
  '2. Set `decision` to `approved_blank` only if the clinical-claim module should be omitted for that SKU.',
  '3. Fill `approvedBy`, `approvedAt`, `rationale`, and set `constraintsAcknowledged` to `true`.',
  '4. Re-run `bun run compliance:blank-decisions && bun run export:content`.',
  ''
].join('\n'), 'utf8')

console.log(`Clinical blank decisions: ${decisions.length}`)
console.log(`Valid blank approvals: ${validBlankApprovals.length}`)
if (!manifest.ready) process.exitCode = 1
