#!/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 workflowRoot = join(contentRoot, 'workflow')
const approvalRoot = join(contentRoot, 'approvals')
const decisionPath = join(approvalRoot, 'product-approval-decisions.json')
const decisionCsvPath = join(approvalRoot, 'product-approval-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 writeJson(path: string, data: any) {
  mkdirSync(join(path, '..'), { recursive: true })
  writeFileSync(path, JSON.stringify(data, null, 2) + '\n', 'utf8')
}
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'
}
function validDecision(row: any) {
  return row?.decision === 'approve_all' &&
    Boolean(row.approvedBy) &&
    Boolean(row.approvedAt) &&
    Boolean(row.reviewEvidence) &&
    row.contentReviewed === true &&
    row.complianceReviewed === true &&
    row.imagesReviewed === true &&
    row.exportReviewed === true &&
    row.noFakeEvidenceAcknowledged === true
}
function currentBlocking(product: any) {
  const approval = product.approval || {}
  const blockers = []
  if (approval.content !== 'approved') blockers.push('content')
  if (approval.compliance !== 'approved') blockers.push('compliance')
  if (!['ready', 'approved'].includes(approval.images)) blockers.push('images')
  if (!['ready', 'approved'].includes(approval.export)) blockers.push('export')
  return blockers
}

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

const products = readdirSync(productsRoot).filter(file => file.endsWith('.json')).sort().map(file => readJson(join(productsRoot, file), {}))
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,
      reviewEvidence: row.reviewEvidence || row.ReviewEvidence,
      contentReviewed: bool(row.contentReviewed || row.ContentReviewed),
      complianceReviewed: bool(row.complianceReviewed || row.ComplianceReviewed),
      imagesReviewed: bool(row.imagesReviewed || row.ImagesReviewed),
      exportReviewed: bool(row.exportReviewed || row.ExportReviewed),
      noFakeEvidenceAcknowledged: bool(row.noFakeEvidenceAcknowledged || row.NoFakeEvidenceAcknowledged),
      note: row.note || row.Note
    }))
  : []
const existingBySku = new Map([...(existing.decisions || []), ...csvExisting].map((row: any) => [row.sku, row]))
const decisions = products.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 || '',
    reviewEvidence: previous.reviewEvidence || '',
    contentReviewed: Boolean(previous.contentReviewed),
    complianceReviewed: Boolean(previous.complianceReviewed),
    imagesReviewed: Boolean(previous.imagesReviewed),
    exportReviewed: Boolean(previous.exportReviewed),
    noFakeEvidenceAcknowledged: Boolean(previous.noFakeEvidenceAcknowledged),
    currentApproval: product.approval || {},
    currentBlockers: currentBlocking(product),
    note: previous.note || 'Set decision=approve_all only after human review of content, compliance, images, export, and supporting evidence. This script will not infer approvals.'
  }
})
writeJson(decisionPath, {
  generatedAt,
  purpose: 'Human product approval decisions for the JV content handoff.',
  allowedDecisions: ['pending', 'approve_all', 'reject_or_needs_changes'],
  guardrail: 'A row applies only when decision=approve_all and approver/timestamp/evidence plus all review acknowledgements are present. No approvals are inferred by scripts.',
  decisions
})
writeFileSync(decisionCsvPath, csv(decisions.map(row => ({
  sku: row.sku,
  productName: row.productName,
  decision: row.decision,
  approvedBy: row.approvedBy,
  approvedAt: row.approvedAt,
  reviewEvidence: row.reviewEvidence,
  contentReviewed: row.contentReviewed ? 'yes' : 'no',
  complianceReviewed: row.complianceReviewed ? 'yes' : 'no',
  imagesReviewed: row.imagesReviewed ? 'yes' : 'no',
  exportReviewed: row.exportReviewed ? 'yes' : 'no',
  noFakeEvidenceAcknowledged: row.noFakeEvidenceAcknowledged ? 'yes' : 'no',
  note: row.note
})), ['sku', 'productName', 'decision', 'approvedBy', 'approvedAt', 'reviewEvidence', 'contentReviewed', 'complianceReviewed', 'imagesReviewed', 'exportReviewed', 'noFakeEvidenceAcknowledged', 'note']), 'utf8')

let applied = 0
const rows: any[] = []
for (const row of decisions) {
  const productPath = join(productsRoot, `${row.sku}.json`)
  const product = readJson(productPath, null)
  const isValid = validDecision(row)
  if (product && isValid) {
    const previousApproval = product.approval || {}
    product.approval = {
      ...previousApproval,
      content: 'approved',
      compliance: 'approved',
      images: 'ready',
      export: 'ready'
    }
    product.approvalEvidence = {
      ...(product.approvalEvidence || {}),
      approvedBy: row.approvedBy,
      approvedAt: row.approvedAt,
      reviewEvidence: row.reviewEvidence,
      source: 'data/content/approvals/product-approval-decisions.json'
    }
    product.notes = Array.isArray(product.notes) ? product.notes : []
    const note = `Human approval decision applied from product-approval-decisions.json by ${row.approvedBy} at ${row.approvedAt}.`
    if (!product.notes.includes(note)) product.notes.push(note)
    product.updatedAt = generatedAt
    writeJson(productPath, product)

    const workflowPath = join(workflowRoot, `${row.sku}.json`)
    const workflow = readJson(workflowPath, null)
    if (workflow) {
      workflow.approval = { ...(workflow.approval || {}), content: 'approved', compliance: 'approved', images: 'ready', export: 'ready' }
      workflow.events = Array.isArray(workflow.events) ? workflow.events : []
      workflow.events.unshift({
        at: generatedAt,
        type: 'approval_decision_applied',
        actor: row.approvedBy,
        field: 'all',
        from: previousApproval,
        to: workflow.approval,
        summary: `Applied valid approve_all decision. Evidence: ${row.reviewEvidence}`
      })
      workflow.updatedAt = generatedAt
      writeJson(workflowPath, workflow)
    }
    applied += 1
  }
  rows.push({
    SKU: row.sku,
    ProductName: row.productName,
    Decision: row.decision,
    ValidApproval: isValid ? 'yes' : 'no',
    Applied: isValid ? 'yes' : 'no',
    ApprovedBy: row.approvedBy,
    ApprovedAt: row.approvedAt,
    ReviewEvidence: row.reviewEvidence,
    CurrentBlockers: row.currentBlockers,
    Note: row.note
  })
}

const indexPath = join(workflowRoot, 'index.json')
const workflowIndex = readJson(indexPath, null)
if (workflowIndex?.products) {
  workflowIndex.products = workflowIndex.products.map((entry: any) => {
    const product = readJson(join(productsRoot, `${entry.sku}.json`), null)
    return product ? { ...entry, approval: product.approval || entry.approval } : entry
  })
  workflowIndex.updatedAt = generatedAt
  writeJson(indexPath, workflowIndex)
}

const manifest = {
  generatedAt,
  ready: decisions.length > 0 && decisions.every(validDecision),
  decisionFile: 'data/content/approvals/product-approval-decisions.json',
  counts: {
    products: decisions.length,
    validApprovals: decisions.filter(validDecision).length,
    applied,
    pending: decisions.filter(row => row.decision === 'pending').length,
    rejectedOrNeedsChanges: decisions.filter(row => row.decision === 'reject_or_needs_changes').length
  },
  guardrail: 'No product is approved unless the editable decision file contains a valid approve_all row with all acknowledgements.',
  rows
}
writeJson(join(outRoot, 'product_approval_decision_gate.json'), manifest)
writeFileSync(join(outRoot, 'product_approval_decision_gate.csv'), csv(rows, ['SKU', 'ProductName', 'Decision', 'ValidApproval', 'Applied', 'ApprovedBy', 'ApprovedAt', 'ReviewEvidence', 'CurrentBlockers', 'Note']), 'utf8')
writeFileSync(join(outRoot, 'product_approval_decision_gate.md'), [
  '# Product approval decision gate',
  '',
  `Generated: ${generatedAt}`,
  '',
  `Decision file: \`${manifest.decisionFile}\``,
  'Editable CSV: `data/content/approvals/product-approval-decisions.csv`',
  '',
  'This gate provides an editable approval file and applies only explicit human approvals. It does not infer approval from generated content or passing asset checks.',
  '',
  '## Counts',
  `- Products: ${manifest.counts.products}`,
  `- Valid approvals: ${manifest.counts.validApprovals}`,
  `- Applied this run: ${manifest.counts.applied}`,
  `- Pending: ${manifest.counts.pending}`,
  `- Rejected / needs changes: ${manifest.counts.rejectedOrNeedsChanges}`,
  '',
  '## Required fields for apply',
  '- `decision`: `approve_all`',
  '- `approvedBy`',
  '- `approvedAt`',
  '- `reviewEvidence`',
  '- `contentReviewed`, `complianceReviewed`, `imagesReviewed`, `exportReviewed`, `noFakeEvidenceAcknowledged`: all `true`',
  '',
  'After valid approvals are entered, run `bun run approval:decisions && bun run approval:review-packet && bun run export:content && bun run audit:content`.',
  ''
].join('\n'), 'utf8')

console.log(`Product approval decisions: ${decisions.length}`)
console.log(`Valid approvals: ${manifest.counts.validApprovals}`)
console.log(`Applied approvals: ${applied}`)
if (!manifest.ready) process.exitCode = 1
