// scripts/mines-batamote-demo.mjs
//
// Full production diligence pipeline on the REAL Batamote dataset.
//
// Drives the same adapter functions the Mines agents call, against the real
// data/madarx.sqlite, using genuine figures from the Batamote NI 43-101
// Technical Report (Grupo Factor, effective 09 Jun 2025) plus live metal prices.
//
//   1. Approve folder as import root; create/reuse the Batamote project.
//   2. mines_data_import — catalog the full archive.
//   3. Load REAL structured data:
//        - 28 Teck drill-hole collars (Table 3)
//        - 19 Bata Sur trench assays (Table 2)
//        - 10 drill intercepts with Cu/Ag/Mo/Au/CuEq (Table 4), tied to holes
//   4. Record provenance-linked facts: deposit type, targets, comparables
//      (La Caridad/Cananea/La Colorada), work program, QA/QC, data verification.
//      NO resource_estimate (QP declares none — system must not invent tonnage).
//   5. Live Cu/Ag market snapshots.
//   6. Document extraction: real parser extraction if pdf-parse is installed;
//      otherwise seed verbatim report-text chunks so RAG narrative is real.
//   7. Operator conceptual exploration-target SCENARIO (CIM-labelled range) ->
//      mines_valuation_scenarios rows + a mines_valuation_scenario_report artifact.
//   8. Canonical mines_valuation_run -> readiness report (no fabricated $).
//   9. mines_report_generate: data_quality_report, interpretation_memo,
//      due_diligence_memo (the client deliverable).
//
// Idempotent. Run:
//   MADARX_DISABLE_INPROC_DAEMON=1 node scripts/mines-batamote-demo.mjs

process.env.MADARX_DISABLE_INPROC_DAEMON = '1';

const DATASET_ROOT = '/Users/kabdulghani/Downloads/Archivo/01-Batamote';
const REPORT_FILE = '43-101 Batamote.pdf';
const USER = 'local-user';
const ORG = 'personal-org';

const { getDb, newId, nowIso, resetDbHandle } = await import('../lib/db.js');
const { approveMinesImportRoot } = await import('../lib/mines/import-roots.js');
const { minesProjectCreate } = await import('../lib/tools/adapters/mines/project-crud.js');
const { minesDataImport } = await import('../lib/tools/adapters/mines/data-import.js');
const { minesDocumentExtract } = await import('../lib/tools/adapters/mines/document-extract.js');
const { minesAssumptionValidate } = await import('../lib/tools/adapters/mines/assumption-validate.js');
const { minesValuationRun } = await import('../lib/tools/adapters/mines/valuation-run.js');
const { minesReportGenerate } = await import('../lib/tools/adapters/mines/report-generate.js');
const { computeInSitu } = await import('../lib/mines/valuation/t1-insitu.js');

const db = getDb();

// ── Live metal prices (market snapshot 2026-05-28, Kitco / TradingEconomics) ──
const PRICE_SOURCE = 'market snapshot 2026-05-28 (Kitco/TradingEconomics)';
const PRICE_CU_USD_LB = 6.40;
const PRICE_AG_USD_OZ = 76.39;

// ── Table 3: Teck drillhole collars (UTM Zone 12N) ───────────────────────────
const DRILL_HOLES = [
  ['BA-11-01', 617925.7, 3372028, 1357.365, 450], ['BA-11-02', 617803, 3372570, 1469.092, 352],
  ['BA-11-03', 617263.4, 3372627, 1488.076, 354.4], ['BA-11-04', 617728.2, 3373021, 1555.764, 350],
  ['BA-11-05', 617741.9, 3373731, 1453.637, 367], ['BA-11-06', 617228.2, 3373127, 1395.109, 356],
  ['BA-11-07', 617134.6, 3374066, 1409.844, 383.1], ['BA-11-08', 617552.5, 3374138, 1532.034, 372.5],
  ['BA-11-09', 617810.1, 3373940, 1457.226, 485.8], ['BA-11-10', 618218.3, 3373521, 1502.509, 343.2],
  ['BA-11-11', 617123.4, 3372067, 1461.411, 215.8], ['BA-11-12', 616773.8, 3371762, 1389.016, 388.8],
  ['BA-12-01', 618307, 3369692, 1333, 450.7], ['BA-12-14', 618053, 3369784, 1376, 410.5],
  ['BA-12-15', 617858, 3369590, 1385, 353.5], ['BA-12-16', 619350, 3370965, 1359, 380.8],
  ['BA-12-17', 619202, 3370576, 1383, 402.5], ['BA-12-17B', 619215, 3370571, 1382, 50],
  ['BA-12-18', 619356, 3370972, 1359, 301.5], ['BA-12-19', 618686, 3370058, 1334, 557.5],
  ['BA-12-20', 617427, 3369435, 1332, 482.2], ['BA-12-21', 617114, 3369514, 1300, 389.5],
  ['BA-12-22', 616632, 3369714, 1266, 275.5], ['BA-12-23', 619397, 3371741, 1431, 335.5],
  ['BA-12-24', 619108, 3372280, 1420, 110.5], ['BA-12-25', 618576, 3372967, 1413, 350.5],
  ['BA-12-26', 617910, 3373553, 1466, 583.5], ['BA-12-27', 617910, 3373556, 1466, 451.5],
];

// ── Table 2: Bata Sur trench assays. id, width_m, Cu(ppm), Ag(ppm) ───────────
const TRENCH_ASSAYS = [
  ['Z-1', 64.0, 4034, 4.33], ['Z-2', 38.0, 1231, 0.98], ['Z-3', 42.0, 866, 2.55], ['Z-4', 52.0, 933, 2.92],
  ['Z-5', 38.0, 2206, 3.66], ['Z-6', 44.0, 3445, 5.30], ['Z-7', 58.0, 4142, 8.69], ['Z-8', 36.0, 869, 6.23],
  ['Z-9', 46.0, 2798, 22.10], ['Z-10', 20.0, 1767, 7.34], ['Z-11', 88.0, 1581, 8.28], ['Z-12', 72.0, 1916, 4.08],
  ['Z-13', 26.0, 3788, 22.90], ['Z-13A', 12.0, 1743, 3.94], ['Z-13B', 26.0, 904, 6.13], ['Z-14', 46.0, 1725, 5.02],
  ['Z-14A', 10.0, 2402, 2.00], ['Z-14B', 26.0, 1523, 5.10], ['Z-15', 64.0, 633, 3.27],
];

// ── Table 4: Select drill intercepts. hole, from, to, width, Cu%, Ag, Mo, Au ppb, CuEq%, area ──
const DRILL_INTERCEPTS = [
  ['BA-11-05', 152.7, 367.0, 214.3, 0.13, 1.1, 31, 24, 0.16, 'El Choclo'],
  ['BA-11-07', 133.0, 169.1, 36.1, 0.11, 1.0, 27, 21, 0.14, 'El Choclo'],
  ['BA-11-08', 65.8, 92.4, 26.6, 0.13, 0.9, 42, 65, 0.16, 'El Choclo'],
  ['BA-11-09', 92.9, 122.1, 29.2, 0.11, 0.7, 18, 36, 0.13, 'El Choclo'],
  ['BA-11-10', 37.2, 78.3, 41.1, 0.14, 1.2, 26, 21, 0.17, 'El Choclo'],
  ['BA-12-01', 61.8, 239.0, 177.2, 0.12, 3.6, 14, 12, 0.16, 'Bata Sur'],
  ['BA-12-14', 30.1, 228.5, 198.4, 0.20, 4.5, 14, 15, 0.25, 'Bata Sur'],
  ['BA-12-15', 68.8, 222.2, 153.4, 0.25, 4.0, 16, 12, 0.30, 'Bata Sur'],
  ['BA-12-16', 20.5, 380.8, 360.3, 0.04, 0.6, null, 113, 0.11, 'Arreadero'],
  ['BA-12-26', 146.6, 495.75, 349.2, 0.12, 1.1, 13, 20, 0.14, 'El Choclo'],
];

// ── Facts (each cites the report). NO resource_estimate. ─────────────────────
const FACTS = [
  ['property', 'One mineral claim covering 3,998.7691 ha in Nacozari de García, Sonora, Mexico. Held by Recursos Millrock S.R.L. de C.V. (subsidiary of Grupo Factor S.A. de C.V.).', '§1.4, p.6'],
  ['asset_stage', 'Early-stage exploration property (CIM Standards). No mineral resources reported for any target to date.', '§1.1, p.5'],
  ['deposit_type', 'Porphyry copper system (Laramide Cu-Mo ±Au ±Ag) with hydrothermal breccias and supergene chalcocite-blanket potential; analogous to nearby La Caridad and Cananea.', '§8, p.29'],
  ['mineralization', 'In the Cananea–La Caridad porphyry belt, ~25 km NW of the La Caridad Cu-Mo deposit. Four targets: Bata Sur, El Choclo, Choclo NW, Arreadero.', '§1.5, p.6'],
  ['drilling_summary', 'Teck completed 27 diamond drillholes for 10,298 m (2011–2012) across Bata Sur, Arreadero and El Choclo. Best intersection 136 m @ 0.26% Cu; BA-12-15 returned 153.4 m @ 0.25% Cu (incl. 11.1 m @ 0.95% Cu).', '§1.7/§10, p.6/35'],
  ['trenching_summary', 'Bata Sur Trenching Program: 19 trenches, weighted average 2,060 ppm Cu (0.206% Cu) and 6.5 g/t Ag; peak trench 4,142 ppm Cu (Z-7).', 'Table 2, p.31'],
  ['geophysics', 'Ground gravity (Teck), CRIP/IP (Zonge), and airborne magnetic + radiometric (Aeroquest) surveys completed. Elevated IP >50 mrad on PDIP Lines 1, 2, 4.', '§9.3, p.31-34'],
  ['qa_qc', '8,382 primary samples with 385 blanks, 378 standards, 614 field duplicates (1 each per 20 samples); lab inserted 694 blanks, 203 duplicates, 540 replicates. Analyses by Bureau Veritas (Mexico).', '§11.4, p.38'],
  ['data_verification', 'Author spot-checked assays on PDF certificates vs database with no discrepancies; no independent verification samples taken (author did not visit site).', '§12, p.39'],
  ['work_program', 'QP recommendations: detailed mapping over targets, review of all geophysics post-drilling, clay-alteration (SWIR) analysis of drill core to vector targets; El Choclo NW prioritized for drilling. Factor seeking an incoming partner (NDA required).', '§14, p.44'],
  // Targets
  ['target_bata_sur', 'Two mineralized hydrothermal breccia bodies, a distal/peripheral feature of a porphyry centre. Drill-confirmed: BA-12-15 153.4 m @ 0.25% Cu.', '§14, p.44'],
  ['target_arreadero', '2.0 × 0.7 km Cu-bearing stockwork with potassic alteration along a NE inflection of the batholith contact. Alteration/mineralization vectoring warranted.', '§14, p.44'],
  ['target_el_choclo', 'Dike-like mineralized porphyry ≥1.0 × 0.5 km, open to the NNW; coincident with high-density veining, a Cu soil anomaly, and quartz-sericite ± sericite-chlorite alteration.', '§14, p.44'],
  ['target_el_choclo_nw', 'Potential mineralized body beneath leached capping with chalcocite-blanket potential. Proposed for drilling but untested before Teck withdrew — HIGH-PRIORITY untested target.', '§14, p.44'],
  // Comparables (S-K 1300 reports referenced in §15)
  ['comparable_la_caridad', 'La Caridad (~25 km SE): producing Cu-Mo porphyry. Reserves 2,100 Mt @ 0.21% Cu, 0.028% Mo (Probable mill, 9,737 Mlb Cu); ~98 Mtpy mined, ~169 Mlb Cu/yr. Additional resources 684 Mt @ 0.07% Cu (M+I) + 526 Mt @ 0.08% (Inferred).', 'Tables 5/6, p.40'],
  ['comparable_cananea', 'Cananea / Buenavista del Cobre: among the world\'s largest Cu mines. Reserves 2,413 Mt @ 0.44% Cu, 0.009% Mo, 0.58% Zn (Probable sulphide mill feed, 10,572 kt Cu); resources include 7,848 Mt @ 0.34% Cu (Inferred, 26,525 kt Cu).', 'Tables 7/8, p.42'],
  ['comparable_la_colorada', 'La Colorada breccia (Cananea district, historic): 7 Mt @ 6.0% Cu, 0.40% Mo — high-grade breccia-hosted analogue.', '§13.2, p.41'],
];

// ── Operator conceptual exploration-target scenario (NOT a resource) ──────────
const SCENARIOS = [
  { name: 'operator_low', tonnes: 100e6, cu: 0.20, ag: 3.0 },
  { name: 'operator_base', tonnes: 250e6, cu: 0.25, ag: 4.0 },
  { name: 'operator_high', tonnes: 500e6, cu: 0.30, ag: 5.0 },
];

const CHUNKS = [
  [5, 'The Batamote project consists of a mining claim held by Recursos Millrock S.R.L. de C.V., a wholly owned subsidiary of Grupo Factor S.A. de C.V. This report summarizes the exploration work conducted to date and evaluates its potential for copper (and secondary silver) mineralization. The project includes the Bata Sur, Choclo, Choclo NW and Arreadero targets. No mineral resources have been reported for any of the targets to date, however, the author considers it a property of merit that justifies further exploration programs.'],
  [6, 'The Project is located within the north-northwest trending Cananea–La Caridad porphyry belt in northern Sonora, Mexico, within the world-class Sonora-Arizona-New Mexico Laramide Porphyry Province. It is approximately 25km northwest of the La Caridad porphyry Cu-Mo deposit.'],
  [6, "Teck's drilling was carried out in two phases in 2011 and 2012 for a total of 10,298m in 27 diamond drillholes concentrated in the Bata Sur, Arreadero and El Choclo zones. The best intersection was 136 meters at 0.26% Cu."],
  [29, 'The principal style of mineralization is related to porphyry copper systems, with associated hydrothermal breccias and possible development of a supergene enrichment in the form of an oxidized "chalcocite blanket". This style is similar to that seen at the nearby La Caridad and Cananea deposits.'],
  [44, 'The El Choclo NW target represents potential for an additional mineralized body under a leached capping which has the potential to contain a chalcocite blanket below the higher hills. The area was proposed for drilling but this was not completed before Teck withdrew from the project, making it a high priority untested target.'],
];

// ── helpers ───────────────────────────────────────────────────────────────────
function log(step, msg) { console.log(`\n[${step}] ${msg}`); }
function fmtUsd(n) { if (n == null) return 'n/a'; const v = Number(n); if (v >= 1e9) return `$${(v / 1e9).toFixed(2)}B`; if (v >= 1e6) return `$${(v / 1e6).toFixed(1)}M`; return `$${Math.round(v)}`; }
function esc(s) { return String(s ?? '').replaceAll('&', '&amp;').replaceAll('<', '&lt;').replaceAll('>', '&gt;'); }

function scenarioArtifactHtml(project, rows) {
  const trs = rows.map(r => `<tr><td><strong>${esc(r.label)}</strong></td><td>${(r.tonnes / 1e6).toLocaleString()} Mt</td><td>${r.cu.toFixed(2)}% Cu</td><td>${r.ag.toFixed(1)} g/t Ag</td><td>${(r.containedCuLb / 1e9).toFixed(2)} B lb</td><td><strong>${fmtUsd(r.value)}</strong></td></tr>`).join('');
  return `<!doctype html><html lang="en"><head><meta charset="utf-8"><meta name="viewport" content="width=device-width, initial-scale=1"><title>Operator scenario — ${esc(project.name)}</title>
<style>body{margin:0;background:#fff;color:#111318;font-family:Inter,system-ui,-apple-system,sans-serif;line-height:1.6;padding:clamp(20px,4vw,52px)}main{max-width:920px;margin:0 auto}.k{color:#c2452a;font-family:ui-monospace,Menlo,monospace;font-size:11px;font-weight:600;letter-spacing:.16em;text-transform:uppercase}h1{font-size:clamp(2rem,5vw,3rem);font-weight:300;letter-spacing:-.04em;margin:10px 0 6px}p{color:#374049}.warn{margin:22px 0;background:#fffdf6;border:1px solid rgba(154,106,18,.3);border-left:4px solid #9a6a12;border-radius:14px;padding:16px 18px;font-size:13.5px}table{width:100%;border-collapse:collapse;margin:18px 0;font-size:13px}th,td{border-bottom:1px solid rgba(17,19,24,.08);padding:11px 12px;text-align:left}th{font-size:10px;letter-spacing:.1em;text-transform:uppercase;color:#8a8f99;font-weight:800}.small{color:#8a8f99;font-size:12px}</style></head>
<body><main><div class="k">MadarX · Mines · Operator scenario</div>
<h1>Conceptual exploration-target scenario</h1>
<p class="small">${esc(project.name)} · ${esc(project.asset_stage)} · prices: Cu ${fmtUsd(PRICE_CU_USD_LB)}/lb · Ag ${fmtUsd(PRICE_AG_USD_OZ)}/oz · in-situ factor 3%</p>
<div class="warn"><strong>Conceptual exploration target — not a mineral resource.</strong> The tonnage and grade below are an operator-supplied conceptual exploration target expressed as a range. They are conceptual in nature; there has been insufficient exploration to define a mineral resource; and it is uncertain whether further exploration will result in delineation of a mineral resource. These figures must not be construed as a mineral resource or reserve. In-situ values are an order-of-magnitude indication of exploration optionality only.</div>
<table><thead><tr><th>Scenario</th><th>Target tonnage</th><th>Cu grade</th><th>Ag grade</th><th>Contained Cu</th><th>Indicative in-situ value</th></tr></thead><tbody>${trs}</tbody></table>
<p class="small">In-situ value = contained recoverable metal × price × 3% in-situ factor (exploration-stage). Basis: target footprints — El Choclo porphyry ≥1.0×0.5 km, Arreadero stockwork 2.0×0.7 km. Generated by MadarX Mines.</p>
</main></body></html>`;
}

// ── orchestration ──────────────────────────────────────────────────────────────
async function main() {
  const out = { steps: {} };

  approveMinesImportRoot(DATASET_ROOT, { label: 'Batamote dataset', user_id: USER, org_id: ORG });

  let project = db.prepare("SELECT * FROM mines_projects WHERE slug = 'batamote'").get();
  if (!project) {
    const created = await minesProjectCreate(
      { name: 'Batamote', slug: 'batamote', asset_stage: 'exploration', commodity: 'Cu', deposit_type: 'Porphyry copper (±Ag)' },
      { owner_trusted: true, user_id: USER, acting_on_behalf_of_user_id: USER });
    if (created.status !== 'ok') throw new Error('project create failed: ' + JSON.stringify(created));
    project = db.prepare('SELECT * FROM mines_projects WHERE id = ?').get(created.project_id);
  }
  log('project', `${project.id} (os ${project.os_project_id})`);

  // Idempotent regen: clear prior artifacts + valuation scenarios for this project.
  db.prepare('DELETE FROM artifacts WHERE project_id IN (?, ?)').run(project.id, project.os_project_id || '');
  db.prepare('DELETE FROM mines_valuation_scenarios WHERE project_id = ?').run(project.id);

  const imported = await minesDataImport({ project_id: project.id, directory_root: DATASET_ROOT }, { org_id: ORG, user_id: USER, acting_on_behalf_of_user_id: USER });
  log('import', `files_found=${imported.files_found} queued=${imported.queued} catalog_only=${imported.catalog_only}`);

  const reportDoc = db.prepare('SELECT id, document_category FROM mines_source_documents WHERE project_id = ? AND file_name = ?').get(project.id, REPORT_FILE);
  if (!reportDoc) throw new Error('43-101 source document not found');
  const docId = reportDoc.id;

  // Drill collars.
  const insDrill = db.prepare(`INSERT OR IGNORE INTO mines_drill_holes (id, project_id, hole_id, collar_easting, collar_northing, collar_elevation, total_depth_m, crs, source_document_id, confidence, extracted_by_agent_id, created_at, updated_at) VALUES (?, ?, ?, ?, ?, ?, ?, 'UTM Zone 12N', ?, 1.0, 'mines-geo-analyst', ?, ?)`);
  for (const [h, e, n, rl, d] of DRILL_HOLES) insDrill.run(newId('mdh'), project.id, h, e, n, rl, d, docId, nowIso(), nowIso());

  // Trench assays.
  const insAssay = db.prepare(`INSERT OR IGNORE INTO mines_assay_samples (id, project_id, sample_id, sample_type, width_m, cu_pct, ag_ppm, source_document_id, confidence, extracted_by_agent_id, qa_qc_status, created_at, updated_at) VALUES (?, ?, ?, 'trench_channel', ?, ?, ?, ?, 1.0, 'mines-geo-analyst', 'reported', ?, ?)`);
  for (const [s, w, cu, ag] of TRENCH_ASSAYS) insAssay.run(newId('mas'), project.id, s, w, cu / 10000, ag, docId, nowIso(), nowIso());

  // Drill intercepts (tied to holes).
  const holeRow = db.prepare('SELECT id FROM mines_drill_holes WHERE project_id = ? AND hole_id = ?');
  const insIntercept = db.prepare(`INSERT OR IGNORE INTO mines_assay_samples (id, project_id, drillhole_id, sample_id, sample_type, width_m, cu_pct, ag_ppm, other_grades_json, source_document_id, confidence, extracted_by_agent_id, qa_qc_status, created_at, updated_at) VALUES (?, ?, ?, ?, 'drill_core', ?, ?, ?, ?, ?, 1.0, 'mines-geo-analyst', 'reported', ?, ?)`);
  for (const [hole, from, to, width, cu, ag, mo, au, cueq, area] of DRILL_INTERCEPTS) {
    const dh = holeRow.get(project.id, hole);
    const other = JSON.stringify({ from_m: from, to_m: to, mo_ppm: mo, au_ppb: au, cueq_pct: cueq, area });
    insIntercept.run(newId('mas'), project.id, dh?.id || null, `${hole}_${from}-${to}`, width, cu, ag, other, docId, nowIso(), nowIso());
  }
  log('data', `${DRILL_HOLES.length} collars · ${TRENCH_ASSAYS.length} trench assays · ${DRILL_INTERCEPTS.length} intercepts`);

  // Facts.
  const insFact = db.prepare(`INSERT INTO mines_extracted_facts (id, project_id, fact_type, value_text, confidence, source_document_id, page_or_location, extracted_by_agent_id, created_at, updated_at) VALUES (?, ?, ?, ?, 0.9, ?, ?, 'mines-geo-analyst', ?, ?)`);
  let factN = 0;
  for (const [type, text, loc] of FACTS) {
    if (db.prepare('SELECT 1 FROM mines_extracted_facts WHERE project_id = ? AND fact_type = ?').get(project.id, type)) continue;
    insFact.run(newId('mef'), project.id, type, text, docId, loc, nowIso(), nowIso());
    factN++;
  }
  log('facts', `${factN} new facts (total ${db.prepare('SELECT COUNT(*) n FROM mines_extracted_facts WHERE project_id=?').get(project.id).n})`);

  // Live prices (replace prior demo snapshots).
  db.prepare("DELETE FROM mines_market_snapshots WHERE commodity IN ('Cu','Ag') AND source LIKE '%demo%'").run();
  const insSnap = db.prepare(`INSERT INTO mines_market_snapshots (id, commodity, price_usd, price_unit, source, fetched_at) VALUES (?, ?, ?, ?, ?, ?)`);
  for (const [c, p, u] of [['Cu', PRICE_CU_USD_LB, 'usd_per_lb'], ['Ag', PRICE_AG_USD_OZ, 'usd_per_oz']]) {
    insSnap.run(newId('mms'), c, p, u, PRICE_SOURCE, nowIso());
  }
  log('prices', `Cu $${PRICE_CU_USD_LB}/lb · Ag $${PRICE_AG_USD_OZ}/oz`);

  // Document extraction: real parser run if available, else seed verbatim chunks.
  let parsers = false;
  try { await import('pdf-parse'); parsers = true; } catch {}
  if (parsers) {
    // Always (re)extract the 43-101 for real, plus a batch of other docs.
    // pdf/docx first (officeparser pptx is slower), report prioritized.
    const report = db.prepare('SELECT id FROM mines_source_documents WHERE project_id = ? AND file_name = ?').get(project.id, REPORT_FILE);
    const others = db.prepare(`SELECT id FROM mines_source_documents WHERE project_id = ? AND id != ? AND file_type IN ('pdf','docx','pptx') ORDER BY CASE file_type WHEN 'pdf' THEN 0 WHEN 'docx' THEN 1 ELSE 2 END LIMIT ?`).all(project.id, report?.id || '', Number(process.env.EXTRACT_LIMIT) || 120);
    const list = [report, ...others].filter(Boolean);
    let ok = 0, chunks = 0, other = 0;
    for (const dDoc of list) {
      try {
        const r = await minesDocumentExtract({ source_document_id: dDoc.id });
        // 'complete' needs a local embedder; without one extraction still yields
        // chunks with status 'embedding_failed' (text-search RAG). Count both.
        if (r.status === 'complete' || r.status === 'embedding_failed' || (r.chunks_created || 0) > 0) { ok++; chunks += r.chunks_created || 0; } else other++;
      } catch { other++; }
    }
    log('extract', `REAL parser extraction: ${ok} docs extracted (${chunks} chunks), ${other} skipped/failed, of ${list.length}`);
  } else {
    db.prepare('DELETE FROM mines_document_chunks WHERE source_document_id = ?').run(docId);
    const insChunk = db.prepare(`INSERT INTO mines_document_chunks (id, source_document_id, project_id, document_category, page, chunk_index, text, language, embedding, token_count, created_at) VALUES (?, ?, ?, ?, ?, ?, ?, 'en', NULL, ?, ?)`);
    CHUNKS.forEach(([page, text], idx) => insChunk.run(newId('mdc'), docId, project.id, reportDoc.document_category, page, idx, text, Math.ceil(text.length / 4), nowIso()));
    db.prepare("UPDATE mines_source_documents SET extraction_status = 'complete', language = 'en', page_count = COALESCE(page_count, 45), updated_at = ? WHERE id = ?").run(nowIso(), docId);
    log('extract', `parsers absent — seeded ${CHUNKS.length} verbatim report chunks`);
  }

  // Operator conceptual scenario.
  db.prepare("DELETE FROM mines_valuation_scenarios WHERE project_id = ? AND scenario LIKE 'operator_%'").run(project.id);
  const scenRows = [];
  for (const s of SCENARIOS) {
    const result = computeInSitu({
      resource_tonnes: s.tonnes, grade_cu_pct: s.cu, grade_ag_gpt: s.ag,
      recovery_cu: 0.85, recovery_ag: 0.75, in_situ_factor: 0.03, asset_stage: 'exploration',
      price_cu_usd_lb: PRICE_CU_USD_LB, price_ag_usd_oz: PRICE_AG_USD_OZ,
    });
    const assumptions = { resource_tonnes: s.tonnes, grade_cu_pct: s.cu, grade_ag_gpt: s.ag, price_cu_usd_lb: PRICE_CU_USD_LB, price_ag_usd_oz: PRICE_AG_USD_OZ, in_situ_factor: 0.03, source: 'operator_conceptual_target' };
    db.prepare(`INSERT INTO mines_valuation_scenarios (id, project_id, valuation_tier, scenario, assumptions_json, result_json, tier_rationale, confidence, created_by_agent_id, created_at) VALUES (?, ?, 't1_in_situ', ?, ?, ?, ?, 0.2, 'mines-valuation-analyst', ?)`).run(
      newId('mines-val'), project.id, s.name, JSON.stringify(assumptions), JSON.stringify(result),
      'Conceptual exploration-target scenario — NOT a mineral resource (CIM). Order-of-magnitude exploration optionality only.', nowIso());
    scenRows.push({ label: s.name.replace('operator_', ''), tonnes: s.tonnes, cu: s.cu, ag: s.ag, containedCuLb: result.contained_cu_lb || 0, value: result.in_situ_value_usd });
  }
  // Scenario artifact.
  const scenArtId = newId('artifact');
  db.prepare(`INSERT INTO artifacts (id, run_id, agent_id, task_id, path, kind, title, content, source_md, created_at, project_id) VALUES (?, NULL, 'mines-valuation-analyst', NULL, ?, 'mines_valuation_scenario_report', ?, ?, ?, ?, ?)`).run(
    scenArtId, `workspace/mines/${project.id}/${scenArtId}.html`, `Operator scenario — ${project.name}`,
    scenarioArtifactHtml(project, scenRows), JSON.stringify(scenRows, null, 2), nowIso(), project.id);
  log('scenario', `base in-situ ${fmtUsd(scenRows[1].value)} · artifact ${scenArtId}`);
  out.scenario = scenRows;

  // Canonical readiness valuation (no fabricated $).
  const valuation = await minesValuationRun({ project_id: project.id, commodity_prices_json: { Cu: PRICE_CU_USD_LB, Ag: PRICE_AG_USD_OZ } });
  log('valuation', `tier=${valuation.tier} t2_blocked=${valuation.t2_blocked} artifact=${valuation.artifact_id}`);

  const validate = await minesAssumptionValidate({ project_id: project.id });
  log('validate', `recommended_tier=${validate.recommended_tier} t1_ready=${validate.t1_ready} t2_ready=${validate.t2_ready}`);

  // Reports.
  const dq = await minesReportGenerate({ project_id: project.id, report_kind: 'data_quality_report' }, { agent_id: 'mines-geo-analyst', user_id: USER });
  const memo = await minesReportGenerate({ project_id: project.id, report_kind: 'interpretation_memo' }, { agent_id: 'mines-geo-analyst', user_id: USER });
  const dd = await minesReportGenerate({ project_id: project.id, report_kind: 'due_diligence_memo' }, { agent_id: 'mines-geo-analyst', user_id: USER });
  log('reports', `data_quality=${dq.artifact_id} memo=${memo.artifact_id} due_diligence=${dd.artifact_id} (facts_cited=${dd.facts_cited}, comps=${dd.comparables}, targets=${dd.targets}, scen=${dd.scenario_rows})`);

  db.prepare('UPDATE mines_projects SET has_technical_report = 1, updated_at = ? WHERE id = ?').run(nowIso(), project.id);

  const artifacts = db.prepare("SELECT id, kind, title FROM artifacts WHERE project_id IN (?, ?) ORDER BY created_at DESC").all(project.id, project.os_project_id);
  console.log('\n========== PRODUCTION PIPELINE COMPLETE ==========');
  console.log('Mines project:', project.id, '| slug:', project.slug);
  console.log('Drill holes:', db.prepare('SELECT COUNT(*) n FROM mines_drill_holes WHERE project_id=?').get(project.id).n,
    '| Assays:', db.prepare('SELECT COUNT(*) n FROM mines_assay_samples WHERE project_id=?').get(project.id).n,
    '| Facts:', db.prepare('SELECT COUNT(*) n FROM mines_extracted_facts WHERE project_id=?').get(project.id).n);
  console.log('Operator scenario (base):', fmtUsd(scenRows[1].value), 'in-situ at', `Cu $${PRICE_CU_USD_LB}/lb`);
  console.log('\nArtifacts:');
  for (const a of artifacts) console.log(`  - [${a.kind}] ${a.title}  (${a.id})`);
  console.log('\nView:  http://127.0.0.1:8787/mines/' + project.id + '/results');
}

main().then(() => { resetDbHandle(); process.exit(0); }).catch((err) => { console.error('FAILED:', err); resetDbHandle(); process.exit(1); });
