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

const root = process.cwd()
const outRoot = join(root, 'content_population_exports')
const dataforseoRoot = join(root, 'data', 'sources', 'dataforseo')
const candidatePath = join(dataforseoRoot, 'asin-candidates.csv')
const asinMapPath = join(dataforseoRoot, 'asin-map.csv')
const confirmationPath = join(dataforseoRoot, 'asin-confirmations.csv')
mkdirSync(outRoot, { recursive: true })
mkdirSync(dataforseoRoot, { recursive: true })

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'
}

function parseCsv(text: string) {
  const rows: string[][] = []
  let row: string[] = []
  let cell = ''
  let quoted = false
  for (let index = 0; index < text.length; index += 1) {
    const char = text[index]
    const next = text[index + 1]
    if (quoted) {
      if (char === '"' && next === '"') { cell += '"'; index += 1 }
      else if (char === '"') quoted = false
      else cell += char
    } else if (char === '"') quoted = true
    else if (char === ',') { row.push(cell); cell = '' }
    else if (char === '\n') { row.push(cell); rows.push(row); row = []; cell = '' }
    else if (char !== '\r') cell += char
  }
  if (cell || row.length) { row.push(cell); rows.push(row) }
  const headers = rows.shift()?.map(header => header.trim()) || []
  return rows.filter(values => values.some(value => value.trim())).map(values => Object.fromEntries(headers.map((header, index) => [header, (values[index] || '').trim()])))
}

function amazonSearchUrl(product: string) {
  return `https://www.amazon.co.uk/s?k=${encodeURIComponent(`Just Vitamins ${product}`.replace(/\s+/g, ' ').trim())}`
}

const headers = ['SKU', 'Product', 'LookupStatus', 'AmazonSearchUrl', 'CandidateASIN', 'CandidateProductUrl', 'CandidateEvidenceUrl', 'AmazonConfirmedUrl', 'AmazonConfirmedTitle', 'BrandMatchesJustVitamins', 'ProductMatchesTarget', 'PackSizeMatchesTarget', 'Status', 'Confidence', 'ConfirmedBy', 'ConfirmedAt', 'EvidenceNotes']
const generatedAt = new Date().toISOString()
const asinRows = existsSync(asinMapPath) ? parseCsv(readFileSync(asinMapPath, 'utf8')) : []
const candidates = existsSync(candidatePath) ? parseCsv(readFileSync(candidatePath, 'utf8')) : []
const existing = existsSync(confirmationPath) ? parseCsv(readFileSync(confirmationPath, 'utf8')) : []
const candidateBySku = new Map(candidates.map((row: any) => [row.SKU, row]))
const existingBySku = new Map(existing.map((row: any) => [row.SKU, row]))

const confirmationRows = asinRows.map((target: any) => {
  const candidate: any = candidateBySku.get(target.SKU) || {}
  const prior: any = existingBySku.get(target.SKU) || {}
  const product = target.Product || candidate.Product || target.SKU
  return {
    SKU: target.SKU,
    Product: product,
    LookupStatus: candidate.CandidateASIN ? 'candidate_available' : 'needs_manual_search',
    AmazonSearchUrl: prior.AmazonSearchUrl || amazonSearchUrl(product),
    CandidateASIN: candidate.CandidateASIN || prior.CandidateASIN || '',
    CandidateProductUrl: candidate.CandidateProductUrl || prior.CandidateProductUrl || '',
    CandidateEvidenceUrl: candidate.EvidenceUrl || prior.CandidateEvidenceUrl || '',
    AmazonConfirmedUrl: prior.AmazonConfirmedUrl || '',
    AmazonConfirmedTitle: prior.AmazonConfirmedTitle || '',
    BrandMatchesJustVitamins: prior.BrandMatchesJustVitamins || 'no',
    ProductMatchesTarget: prior.ProductMatchesTarget || 'no',
    PackSizeMatchesTarget: prior.PackSizeMatchesTarget || 'unknown',
    Status: prior.Status || 'pending',
    Confidence: prior.Confidence || candidate.Confidence || 'unknown',
    ConfirmedBy: prior.ConfirmedBy || '',
    ConfirmedAt: prior.ConfirmedAt || '',
    EvidenceNotes: prior.EvidenceNotes || ''
  }
})
writeFileSync(confirmationPath, csv(confirmationRows, headers), 'utf8')

const reportRows = confirmationRows.map((row: any) => {
  const asin = String(row.CandidateASIN || '').trim().toUpperCase()
  const canonicalUrl = asin ? `https://www.amazon.co.uk/dp/${asin}` : ''
  const urlOk = Boolean(asin) && (row.AmazonConfirmedUrl === canonicalUrl || row.CandidateProductUrl === canonicalUrl)
  const confirmed = row.Status === 'confirmed'
  const high = row.Confidence === 'high'
  const brand = row.BrandMatchesJustVitamins === 'yes'
  const product = row.ProductMatchesTarget === 'yes'
  const pack = row.PackSizeMatchesTarget === 'yes' || row.PackSizeMatchesTarget === 'not_applicable'
  const hasReviewer = Boolean(row.ConfirmedBy && row.ConfirmedAt)
  const hasTitle = Boolean(row.AmazonConfirmedTitle)
  const pass = confirmed && high && brand && product && pack && hasReviewer && hasTitle && urlOk
  const blockers = []
  if (!row.CandidateASIN) blockers.push('no candidate ASIN; manual Amazon search required')
  if (!confirmed) blockers.push(`Status is ${row.Status}`)
  if (!high) blockers.push(`Confidence is ${row.Confidence}`)
  if (!brand) blockers.push('brand not confirmed')
  if (!product) blockers.push('target product not confirmed')
  if (!pack) blockers.push(`pack size match is ${row.PackSizeMatchesTarget}`)
  if (!hasTitle) blockers.push('AmazonConfirmedTitle missing')
  if (!hasReviewer) blockers.push('ConfirmedBy/ConfirmedAt missing')
  if (row.CandidateASIN && !urlOk) blockers.push('canonical Amazon URL not confirmed')
  return { ...row, CanonicalAmazonUrl: canonicalUrl, ReadyForPromotion: pass ? 'yes' : 'no', Blockers: blockers.join(' | ') }
})

const manifest = {
  generatedAt,
  ready: reportRows.some((row: any) => row.ReadyForPromotion === 'yes'),
  counts: {
    confirmations: reportRows.length,
    candidateRows: reportRows.filter((row: any) => row.LookupStatus === 'candidate_available').length,
    manualSearchRows: reportRows.filter((row: any) => row.LookupStatus === 'needs_manual_search').length,
    readyForPromotion: reportRows.filter((row: any) => row.ReadyForPromotion === 'yes').length,
    pending: reportRows.filter((row: any) => row.Status === 'pending').length,
    rejected: reportRows.filter((row: any) => row.Status === 'rejected').length,
    blocked: reportRows.filter((row: any) => row.ReadyForPromotion !== 'yes').length
  },
  files: {
    confirmationCsv: 'data/sources/dataforseo/asin-confirmations.csv',
    reportCsv: 'content_population_exports/dataforseo_asin_confirmation_report.csv',
    reportJson: 'content_population_exports/dataforseo_asin_confirmation_report.json',
    reportMd: 'content_population_exports/dataforseo_asin_confirmation_report.md'
  },
  guardrails: [
    'Open the Amazon UK product page before setting Status=confirmed.',
    'Rows without candidates still need manual Amazon search using AmazonSearchUrl.',
    'Record the visible Amazon title and reviewer/timestamp before promotion.',
    'BrandMatchesJustVitamins and ProductMatchesTarget must both be yes.',
    'PackSizeMatchesTarget must be yes or not_applicable.',
    'This gate still does not call Amazon, DataForSEO, or Apify.'
  ],
  confirmations: reportRows
}

writeFileSync(join(outRoot, 'dataforseo_asin_confirmation_report.json'), JSON.stringify(manifest, null, 2) + '\n', 'utf8')
writeFileSync(join(outRoot, 'dataforseo_asin_confirmation_report.csv'), csv(reportRows, [...headers, 'CanonicalAmazonUrl', 'ReadyForPromotion', 'Blockers']), 'utf8')
writeFileSync(join(outRoot, 'dataforseo_asin_confirmation_report.md'), [
  '# DataForSEO ASIN confirmation report',
  '',
  `Generated: ${generatedAt}`,
  '',
  'This is the manual evidence gate between candidate/manual ASIN lookup and promotion into the DataForSEO ASIN map.',
  '',
  `- Confirmation rows: ${manifest.counts.confirmations}`,
  `- Candidate rows: ${manifest.counts.candidateRows}`,
  `- Manual-search rows: ${manifest.counts.manualSearchRows}`,
  `- Ready for promotion: ${manifest.counts.readyForPromotion}`,
  `- Pending: ${manifest.counts.pending}`,
  `- Rejected: ${manifest.counts.rejected}`,
  '',
  '## Guardrails',
  ...manifest.guardrails.map(item => `- ${item}`),
  '',
  '## Rows',
  ...reportRows.map((row: any) => `- [${row.ReadyForPromotion === 'yes' ? 'ready' : 'blocked'}] ${row.SKU} — ${row.CandidateASIN || 'manual search'} — ${row.Blockers || 'all checks passed'}`),
  ''
].join('\n'), 'utf8')

console.log(`DataForSEO ASIN confirmations ready: ${manifest.ready}`)
console.log(`Confirmation rows: ${manifest.counts.confirmations}`)
console.log(`Ready for promotion: ${manifest.counts.readyForPromotion}`)
console.log(`Blocked: ${manifest.counts.blocked}`)
