// lib/mines/spatial-reader.js
//
// Minimal, dependency-free GIS/CAD metadata reader (engineering-design §8C).
// Reads file HEADERS only — no geometry rendering. This is the swap seam: when
// full geometry analysis is needed, replace with node-gdal-async / PostGIS.
//
//  - .prj : plain-text WKT CRS string (readFileSync).
//  - .shp : bounding box from the 100-byte main file header. Per the ESRI
//           Shapefile spec the bbox doubles (Xmin@36, Ymin@44, Xmax@52,
//           Ymax@60) are stored LITTLE-endian (only bytes 0–27 are big-endian).
//  - .gdb/.mxd/.dxf/.dwg/raster : kind recorded from extension; crs/bbox null.

import fs from 'node:fs';
import path from 'node:path';

const EXT_TO_KIND = {
  '.shp': 'shp',
  '.shx': 'shp',
  '.dbf': 'shp',
  '.prj': 'prj',
  '.gdb': 'gdb',
  '.mxd': 'mxd',
  '.dxf': 'dxf',
  '.dwg': 'dwg',
  '.adf': 'adf',
  '.tif': 'raster',
  '.tiff': 'raster',
  '.img': 'raster',
  '.ecw': 'raster',
};

/** Map a file extension to a mines_spatial_assets.kind value. */
export function kindForExtension(ext) {
  return EXT_TO_KIND[String(ext || '').toLowerCase()] || 'other_spatial';
}

/** Read a .prj WKT CRS string. Returns null on any error. */
export function readPrjCrs(filePath) {
  try {
    const txt = fs.readFileSync(filePath, 'utf8').trim();
    return txt || null;
  } catch {
    return null;
  }
}

/**
 * Read the bounding box from a .shp main file header.
 * @returns {{min_x, min_y, max_x, max_y}|null}
 */
export function readShapefileBbox(filePath) {
  try {
    const fd = fs.openSync(filePath, 'r');
    try {
      const buf = Buffer.alloc(100);
      const read = fs.readSync(fd, buf, 0, 100, 0);
      if (read < 68) return null;
      const min_x = buf.readDoubleLE(36);
      const min_y = buf.readDoubleLE(44);
      const max_x = buf.readDoubleLE(52);
      const max_y = buf.readDoubleLE(60);
      const allFinite = [min_x, min_y, max_x, max_y].every(Number.isFinite);
      if (!allFinite) return null;
      return { min_x, min_y, max_x, max_y };
    } finally {
      fs.closeSync(fd);
    }
  } catch {
    return null;
  }
}

/**
 * Read spatial metadata for a file by extension.
 * @returns {{ kind: string, crs: string|null, bbox: object|null, file_size_bytes: number|null }}
 */
export function readSpatialMetadata(filePath) {
  const ext = path.extname(filePath).toLowerCase();
  const kind = kindForExtension(ext);

  let file_size_bytes = null;
  try { file_size_bytes = fs.statSync(filePath).size; } catch { /* ignore */ }

  let crs = null;
  let bbox = null;

  if (ext === '.prj') {
    crs = readPrjCrs(filePath);
  } else if (ext === '.shp') {
    bbox = readShapefileBbox(filePath);
    // A sibling .prj carries the CRS for the shapefile set.
    const sidecar = filePath.replace(/\.shp$/i, '.prj');
    if (sidecar !== filePath && fs.existsSync(sidecar)) crs = readPrjCrs(sidecar);
  }

  return { kind, crs, bbox, file_size_bytes };
}
