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

const root = process.cwd()
const outRoot = join(root, 'content_population_exports')
mkdirSync(outRoot, { recursive: true })

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 (value === undefined || value === null) value = ''
    if (Array.isArray(value)) value = value.join(' | ')
    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'
}

const generatedAt = new Date().toISOString()
const refresh = readJson(join(outRoot, 'handoff_packet_refresh.json'), {})
const delivery = readJson(join(outRoot, 'delivery_index.json'), {})
const steps = refresh.steps || []
const refreshStartedAt = steps.length ? steps.map((step: any) => step.StartedAt).sort()[0] : refresh.generatedAt
const refreshStartMs = refreshStartedAt ? new Date(refreshStartedAt).getTime() : 0

const allowedStaticFiles = new Set([
  'planned_image_filenames.json',
  'planned_image_filenames.csv',
  'metaobject_svg_asset_generation.json',
  'metaobject_svg_asset_generation.csv',
  'draft_pdp_asset_generation.json',
  'draft_pdp_asset_generation.csv',
  'clinical_claim_mapping_report.json',
  'clinical_claim_mapping_report.csv',
  'unmatched_claims_review_packet.md',
  'unmatched_claims_review_packet.csv',
  'unmatched_claims_review_packet.json'
])
const allowedStaticGroups = new Set(['Migration CSV', 'Migration validation'])
const refreshGeneratedFiles = new Set(['handoff_packet_refresh.md', 'handoff_packet_refresh.json', 'handoff_packet_refresh.csv'])

const rows = (delivery.files || []).map((file: any) => {
  const present = file.Present === 'yes' && existsSync(file.Path)
  const stat = present ? statSync(file.Path) : null
  const modifiedAt = stat?.mtime?.toISOString() || file.LastModified || ''
  const modifiedMs = modifiedAt ? new Date(modifiedAt).getTime() : 0
  const staticAllowed = allowedStaticFiles.has(file.File) || allowedStaticGroups.has(file.Group)
  const selfReport = refreshGeneratedFiles.has(file.File)
  let status = 'fresh'
  let reason = 'Modified during or after current refresh run.'
  if (!present) {
    status = 'missing'
    reason = 'Delivery index says this file is missing or path does not exist.'
  } else if (!refreshStartMs) {
    status = 'unknown'
    reason = 'No refresh start timestamp available.'
  } else if (modifiedMs < refreshStartMs && staticAllowed) {
    status = 'static_allowed'
    reason = 'Long-lived source/asset/migration artifact is allowed to predate refresh; delivery index still tracks its presence.'
  } else if (modifiedMs < refreshStartMs && selfReport) {
    status = 'self_report_current'
    reason = 'Refresh report was generated by this run and may predate the final delivery index while still describing the current run.'
  } else if (modifiedMs < refreshStartMs) {
    status = 'stale'
    reason = 'Refresh-scoped artifact predates current refresh run.'
  }
  return {
    Group: file.Group,
    File: file.File,
    Status: status,
    Present: present ? 'yes' : 'no',
    LastModified: modifiedAt,
    RefreshStartedAt: refreshStartedAt || '',
    Reason: reason,
    Path: file.Path
  }
})

const staleRows = rows.filter(row => row.Status === 'stale' || row.Status === 'missing' || row.Status === 'unknown')
const manifest = {
  generatedAt,
  ready: refresh.refreshReady === true && delivery.ready === true && staleRows.length === 0,
  note: 'Freshness validates refresh-scoped handoff artifacts against the current refresh run. Long-lived asset/source-generation and migration artifacts are explicitly allowed to predate the run but remain present in delivery_index.',
  refreshStartedAt,
  counts: {
    files: rows.length,
    fresh: rows.filter(row => row.Status === 'fresh').length,
    staticAllowed: rows.filter(row => row.Status === 'static_allowed').length,
    selfReportCurrent: rows.filter(row => row.Status === 'self_report_current').length,
    stale: rows.filter(row => row.Status === 'stale').length,
    missing: rows.filter(row => row.Status === 'missing').length,
    unknown: rows.filter(row => row.Status === 'unknown').length
  },
  rows,
  staleRows
}

writeFileSync(join(outRoot, 'handoff_artifact_freshness.json'), JSON.stringify(manifest, null, 2) + '\n', 'utf8')
writeFileSync(join(outRoot, 'handoff_artifact_freshness.csv'), csv(rows, ['Group', 'File', 'Status', 'Present', 'LastModified', 'RefreshStartedAt', 'Reason', 'Path']), 'utf8')
writeFileSync(join(outRoot, 'handoff_artifact_freshness.md'), [
  '# Handoff artifact freshness',
  '',
  `Generated: ${generatedAt}`,
  '',
  `Freshness ready: ${manifest.ready ? 'YES' : 'NO'}`,
  '',
  manifest.note,
  '',
  `Refresh started: ${refreshStartedAt || 'unknown'}`,
  `Files checked: ${manifest.counts.files}`,
  `Fresh: ${manifest.counts.fresh}`,
  `Static allowed: ${manifest.counts.staticAllowed}`,
  `Self-report current: ${manifest.counts.selfReportCurrent}`,
  `Stale: ${manifest.counts.stale}`,
  `Missing: ${manifest.counts.missing}`,
  `Unknown: ${manifest.counts.unknown}`,
  '',
  '## Blocking rows',
  ...(staleRows.length ? staleRows.map(row => `- ${row.Status}: ${row.Group} / ${row.File} - ${row.Reason}`) : ['- none']),
  '',
  '## Interpretation',
  'This gate proves the packet is not merely complete by file existence. It does not prove final handoff readiness; `verify:handoff` remains the final gate.',
  ''
].join('\n'), 'utf8')

console.log(`Handoff artifact freshness ready: ${manifest.ready}`)
console.log(`Fresh: ${manifest.counts.fresh}; static allowed: ${manifest.counts.staticAllowed}; stale: ${manifest.counts.stale}; missing: ${manifest.counts.missing}`)
if (!manifest.ready) process.exitCode = 1
