// lib/mines/portfolio.js
//
// Data-access layer for mines_portfolio_entries and mines_projects.
// Used by API routes AND server-rendered pages — keep this import-safe
// (no side effects, no HTTP, no process.env reads outside function bodies).

import { getDb } from '../db.js';

// ── Constants ────────────────────────────────────────────────────────────────

const MAX_LIMIT = 200;
const DEFAULT_LIMIT = 50;

// data_completeness bucket thresholds:
//   'full'    → value >= 0.8  (80 %+ of expected data is present)
//   'partial' → 0.4 <= value < 0.8
//   'low'     → value < 0.4  (or null)
// Pass any other string / number to use as a raw WHERE filter (not currently
// exposed in the API schema, reserved for future use).
const COMPLETENESS_BUCKETS = {
  full:    [0.8, 1.0],   // [min, max] — inclusive on both ends
  partial: [0.4, 0.8],   // [min, max-exclusive)
  low:     [0.0, 0.4],   // [min, max-exclusive), includes null rows
};

// ── listPortfolioEntries ──────────────────────────────────────────────────────

/**
 * List portfolio entries across ALL portfolios with optional filters and
 * cursor-style pagination.
 *
 * @param {object} opts
 * @param {string}        [opts.commodity]         Exact match, case-insensitive.
 *                                                  Supports comma-separated multi-value.
 * @param {string}        [opts.deposit_type]      Exact match, case-insensitive.
 * @param {boolean|string}[opts.has_drilling]       Truthy → WHERE has_drilling = 1.
 * @param {string|number} [opts.data_completeness]  Bucket string OR raw number.
 * @param {string}        [opts.sort]               Column: 'name'|'commodity'|'data_completeness'
 * @param {number}        [opts.limit]              Cap: 200. Default: 50.
 * @param {number}        [opts.offset]             Zero-based. Default: 0.
 *
 * @returns {{ data: object[], meta: { total: number, filtered: number, limit: number, offset: number } }}
 */
export function listPortfolioEntries({
  commodity,
  deposit_type,
  has_drilling,
  data_completeness,
  sort = 'name',
  limit = DEFAULT_LIMIT,
  offset = 0,
} = {}) {
  const db = getDb();

  // Clamp limit.
  const safeLimit = Math.max(1, Math.min(MAX_LIMIT, Number(limit) || DEFAULT_LIMIT));
  const safeOffset = Math.max(0, Number(offset) || 0);

  // Build WHERE clauses + params for the filtered query.
  // All conditions are parameterized — no string interpolation.
  const whereClauses = [];
  const params = [];

  // commodity — exact, case-insensitive, multi-value (comma-separated)
  if (commodity) {
    const values = String(commodity).split(',').map(v => v.trim().toLowerCase()).filter(Boolean);
    if (values.length === 1) {
      whereClauses.push('LOWER(e.commodity) = ?');
      params.push(values[0]);
    } else if (values.length > 1) {
      const placeholders = values.map(() => '?').join(', ');
      whereClauses.push(`LOWER(e.commodity) IN (${placeholders})`);
      params.push(...values);
    }
  }

  // deposit_type — exact, case-insensitive
  if (deposit_type) {
    whereClauses.push('LOWER(e.deposit_type) = ?');
    params.push(String(deposit_type).trim().toLowerCase());
  }

  // has_drilling — truthy → only rows with drilling
  if (has_drilling && has_drilling !== 'false' && has_drilling !== '0') {
    whereClauses.push('e.has_drilling = 1');
  }

  // data_completeness bucket filter
  if (data_completeness !== undefined && data_completeness !== null && data_completeness !== '') {
    const bucket = COMPLETENESS_BUCKETS[String(data_completeness).toLowerCase()];
    if (bucket) {
      if (data_completeness === 'low') {
        // low bucket includes NULL rows (NULL data means we have even less info)
        whereClauses.push('(e.data_completeness IS NULL OR (e.data_completeness >= ? AND e.data_completeness < ?))');
        params.push(bucket[0], bucket[1]);
      } else if (data_completeness === 'full') {
        whereClauses.push('(e.data_completeness >= ? AND e.data_completeness <= ?)');
        params.push(bucket[0], bucket[1]);
      } else {
        // partial: [0.4, 0.8)
        whereClauses.push('(e.data_completeness >= ? AND e.data_completeness < ?)');
        params.push(bucket[0], bucket[1]);
      }
    }
    // Unknown bucket strings are silently ignored (defensive).
  }

  const whereSQL = whereClauses.length ? `WHERE ${whereClauses.join(' AND ')}` : '';

  // Validate sort column against allowlist to prevent SQL injection.
  const ALLOWED_SORT = new Set(['name', 'commodity', 'data_completeness']);
  const sortCol = ALLOWED_SORT.has(sort) ? sort : 'name';
  // For data_completeness, put NULLs last.
  const orderSQL = sortCol === 'data_completeness'
    ? 'ORDER BY e.data_completeness IS NULL ASC, e.data_completeness ASC, e.name ASC'
    : `ORDER BY e.${sortCol} COLLATE NOCASE ASC`;

  // total = all entries (no filter)
  const totalRow = db.prepare('SELECT COUNT(*) AS n FROM mines_portfolio_entries e').get();
  const total = totalRow?.n ?? 0;

  // filtered = count after filters, before pagination
  const filteredRow = db.prepare(
    `SELECT COUNT(*) AS n FROM mines_portfolio_entries e ${whereSQL}`
  ).get(...params);
  const filtered = filteredRow?.n ?? 0;

  // Paginated result
  const rows = db.prepare(
    `SELECT e.id, e.project_id, e.name, e.commodity, e.deposit_type,
            e.owner, e.available_information, e.has_drilling,
            e.deal_terms, e.data_completeness, e.notes
     FROM mines_portfolio_entries e
     ${whereSQL}
     ${orderSQL}
     LIMIT ? OFFSET ?`
  ).all(...params, safeLimit, safeOffset);

  // Derive 1-based display ordinal from result position + offset.
  const data = rows.map((row, idx) => ({
    id: row.id,
    project_id: row.project_id ?? null,
    consecutive: safeOffset + idx + 1,
    name: row.name,
    commodity: row.commodity ?? null,
    deposit_type: row.deposit_type ?? null,
    owner: row.owner ?? null,
    available_information: row.available_information ?? null,
    has_drilling: row.has_drilling === 1 ? 1 : 0,
    deal_terms: row.deal_terms ?? null,
    data_completeness: row.data_completeness ?? null,
    notes: row.notes ?? null,
  }));

  return {
    data,
    meta: { total, filtered, limit: safeLimit, offset: safeOffset },
  };
}

// ── getPortfolioEntryDetail ───────────────────────────────────────────────────

/**
 * Fetch a single portfolio entry with its linked mines_project (if any)
 * and aggregated document/sample/drill-hole counts.
 *
 * @param {string} id  — mines_portfolio_entries.id
 * @returns {{ entry: object, project: object|null, counts: object }|null}
 */
export function getPortfolioEntryDetail(id) {
  const db = getDb();

  const entryRow = db.prepare(
    `SELECT id, project_id, name, commodity, deposit_type, owner,
            available_information, has_drilling, deal_terms, data_completeness, notes
     FROM mines_portfolio_entries
     WHERE id = ?`
  ).get(id);

  if (!entryRow) return null;

  const entry = {
    id: entryRow.id,
    project_id: entryRow.project_id ?? null,
    name: entryRow.name,
    commodity: entryRow.commodity ?? null,
    deposit_type: entryRow.deposit_type ?? null,
    owner: entryRow.owner ?? null,
    available_information: entryRow.available_information ?? null,
    has_drilling: entryRow.has_drilling === 1 ? 1 : 0,
    deal_terms: entryRow.deal_terms ?? null,
    data_completeness: entryRow.data_completeness ?? null,
    notes: entryRow.notes ?? null,
  };

  // Linked mines_project (if portfolio entry has been promoted)
  let project = null;
  if (entryRow.project_id) {
    const pRow = db.prepare(
      `SELECT id, name, slug, asset_stage, commodity, deposit_type, country,
              state_province, has_technical_report, data_completeness, created_at, updated_at
       FROM mines_projects
       WHERE id = ?`
    ).get(entryRow.project_id);
    if (pRow) {
      project = {
        id: pRow.id,
        name: pRow.name,
        slug: pRow.slug,
        asset_stage: pRow.asset_stage,
        commodity: pRow.commodity ?? null,
        deposit_type: pRow.deposit_type ?? null,
        country: pRow.country,
        state_province: pRow.state_province ?? null,
        has_technical_report: pRow.has_technical_report === 1 ? 1 : 0,
        data_completeness: pRow.data_completeness ?? null,
        created_at: pRow.created_at,
        updated_at: pRow.updated_at,
      };
    }
  }

  // Counts — use project_id from mines_projects if linked; otherwise 0.
  let counts = { documents: 0, assays: 0, drill_holes: 0, last_import_at: null };
  if (entryRow.project_id) {
    const docsRow = db.prepare(
      'SELECT COUNT(*) AS n, MAX(created_at) AS last_at FROM mines_source_documents WHERE project_id = ?'
    ).get(entryRow.project_id);
    const assaysRow = db.prepare(
      'SELECT COUNT(*) AS n FROM mines_assay_samples WHERE project_id = ?'
    ).get(entryRow.project_id);
    const drillRow = db.prepare(
      'SELECT COUNT(*) AS n FROM mines_drill_holes WHERE project_id = ?'
    ).get(entryRow.project_id);

    counts = {
      documents: docsRow?.n ?? 0,
      assays: assaysRow?.n ?? 0,
      drill_holes: drillRow?.n ?? 0,
      last_import_at: docsRow?.last_at ?? null,
    };
  }

  return { entry, project, counts };
}

// ── listMineProjects ──────────────────────────────────────────────────────────

/**
 * Basic metadata list of all mines_projects rows (no pagination needed yet —
 * catalog is small). Used by GET /api/mines/projects.
 *
 * @returns {object[]}
 */
export function listMineProjects() {
  const db = getDb();
  const rows = db.prepare(
    `SELECT id, os_project_id, name, slug, asset_stage, commodity, deposit_type,
            country, state_province, has_technical_report, data_completeness,
            created_at, updated_at
     FROM mines_projects
     ORDER BY name ASC COLLATE NOCASE`
  ).all();
  return rows.map(r => ({
    id: r.id,
    os_project_id: r.os_project_id ?? null,
    name: r.name,
    slug: r.slug,
    asset_stage: r.asset_stage,
    commodity: r.commodity ?? null,
    deposit_type: r.deposit_type ?? null,
    country: r.country,
    state_province: r.state_province ?? null,
    has_technical_report: r.has_technical_report === 1 ? 1 : 0,
    data_completeness: r.data_completeness ?? null,
    created_at: r.created_at,
    updated_at: r.updated_at,
  }));
}
