import PocketBase from 'pocketbase';
import 'dotenv/config';

export type RowObject = Record<string, any>;

function requireEnv(name: string): string {
  const val = process.env[name];
  if (!val) throw new Error(`Missing required env: ${name}`);
  return val;
}

export function normalizeBoolean(val: any): boolean {
  if (val === true) return true;
  const s = String(val).toLowerCase();
  return s === 'true' || s === '1' || s === 'yes' || s === 'y';
}

function excelSerialToIso(num: number): string {
  // Excel serial date: days since 1899-12-30
  const ms = (num - 25569) * 86400 * 1000;
  return new Date(ms).toISOString();
}

function toUtcIso(val: any): string {
  if (val === undefined || val === null || val === '') return '';

  if (typeof val === 'number' && !Number.isNaN(val)) {
    return excelSerialToIso(val);
  }

  if (typeof val === 'string') {
    const trimmed = val.trim();
    const numeric = parseFloat(trimmed);
    if (!Number.isNaN(numeric) && /^\d+(\.\d+)?$/.test(trimmed)) {
      return excelSerialToIso(numeric);
    }
    const d = new Date(trimmed);
    if (!Number.isNaN(d.getTime())) {
      return new Date(Date.UTC(d.getUTCFullYear(), d.getUTCMonth(), d.getUTCDate(), d.getUTCHours(), d.getUTCMinutes(), d.getUTCSeconds())).toISOString();
    }
    return trimmed;
  }

  return String(val);
}

export function mapRowToObject(headers: string[], row: any[]): RowObject {
  const obj: RowObject = {};
  for (let i = 0; i < headers.length; i++) {
    const key = headers[i];
    const val = row[i];
    if (!key) continue;
    const lower = key.toLowerCase();
    if (lower.includes('date')) obj[key] = toUtcIso(val);
    else if (lower.startsWith('is_') || lower.endsWith('_flag') || lower === 'active' || lower.includes('tax_exempt')) obj[key] = normalizeBoolean(val);
    else obj[key] = val;
  }
  return obj;
}

export async function upsertPocketBaseByKey(pb: PocketBase, collection: string, keyName: string, record: RowObject, silent: boolean = false) {
  if (keyName.toLowerCase() !== 'job_number') {
    throw new Error('Primary key must be Job_Number (exact header name)');
  }
  const keyVal = record[keyName];
  if (!keyVal) throw new Error(`Missing key ${keyName} in record`);
  const filter = `${keyName}="${String(keyVal).replace(/"/g, '\\"')}"`;
  const found = await pb.collection(collection).getList(1, 1, { filter });
  if (found?.items?.length) {
    const existing = found.items[0];
    await pb.collection(collection).update(existing.id, record);
    if (!silent) console.log(`✓ Updated PB ${collection} id=${existing.id}`);
  } else {
    const created = await pb.collection(collection).create(record);
    if (!silent) console.log(`✓ Created PB ${collection} id=${created.id}`);
  }
}
