/**
 * Excel Products Module
 * 
 * Provides typed access to technical product data from Excel source files.
 * Reuses the parsing logic from the PDF datasheet generator.
 */

import * as fs from 'fs';
import * as path from 'path';
import * as XLSX from 'xlsx';

// Configuration
const EXCEL_SOURCE_FILES = [
  path.join(process.cwd(), 'data/excel/high-voltage.xlsx'),
  path.join(process.cwd(), 'data/excel/medium-voltage-KM.xlsx'),
  path.join(process.cwd(), 'data/excel/low-voltage-KM.xlsx'),
  path.join(process.cwd(), 'data/excel/solar-cables.xlsx'),
];

// Types
export type ExcelRow = Record<string, string | number | boolean | Date>;

export interface ExcelMatch {
  rows: ExcelRow[];
  units: Record<string, string>;
}

export interface TechnicalData {
  configurations: string[];
  attributes: Array<{
    name: string;
    options: string[];
  }>;
}

export interface ProductLookupParams {
  name?: string;
  slug?: string;
  sku?: string;
  translationKey?: string;
}

// Cache singleton
let EXCEL_INDEX: Map<string, ExcelMatch> | null = null;

/**
 * Normalize Excel key to match product identifiers
 * Examples:
 * - "NA2XS(FL)2Y" -> "NA2XSFL2Y"
 * - "na2xsfl2y-3" -> "NA2XSFL2Y"
 */
function normalizeExcelKey(value: string): string {
  return String(value || '')
    .toUpperCase()
    .replace(/-\d+$/g, '')
    .replace(/[^A-Z0-9]+/g, '');
}

/**
 * Normalize value (strip HTML, trim whitespace)
 */
function normalizeValue(value: string): string {
  if (!value) return '';
  return String(value)
    .replace(/<[^>]*>/g, '')
    .replace(/\s+/g, ' ')
    .trim();
}

/**
 * Check if value looks numeric
 */
function looksNumeric(value: string): boolean {
  const v = normalizeValue(value).replace(/,/g, '.');
  return /^-?\d+(?:\.\d+)?$/.test(v);
}

/**
 * Load Excel rows from a file using xlsx library
 */
function loadExcelRows(filePath: string): ExcelRow[] {
  if (!fs.existsSync(filePath)) {
    console.warn(`[excel-products] File not found: ${filePath}`);
    return [];
  }

  try {
    const workbook = XLSX.readFile(filePath, { 
      cellDates: false,
      cellNF: false,
      cellText: false
    });
    
    // Get the first sheet
    const sheetName = workbook.SheetNames[0];
    const worksheet = workbook.Sheets[sheetName];
    
    // Convert to JSON
    const rows = XLSX.utils.sheet_to_json(worksheet, {
      defval: '',
      raw: false
    }) as ExcelRow[];
    
    return rows;
  } catch (error) {
    console.error(`[excel-products] Error reading ${filePath}:`, error);
    return [];
  }
}

/**
 * Build the Excel index from all source files
 */
export function getExcelIndex(): Map<string, ExcelMatch> {
  if (EXCEL_INDEX) return EXCEL_INDEX;
  
  const idx = new Map<string, ExcelMatch>();
  
  for (const file of EXCEL_SOURCE_FILES) {
    const rows = loadExcelRows(file);
    if (rows.length === 0) continue;
    
    // Find units row (if present)
    const unitsRow = rows.find(r => r && r['Part Number'] === 'Units') || null;
    const units: Record<string, string> = {};
    
    if (unitsRow) {
      for (const [k, v] of Object.entries(unitsRow)) {
        if (k === 'Part Number') continue;
        const unit = normalizeValue(String(v ?? ''));
        if (unit) units[k] = unit;
      }
    }
    
    // Index rows by Part Number
    for (const r of rows) {
      const pn = r?.['Part Number'];
      if (!pn || pn === 'Units') continue;
      
      const key = normalizeExcelKey(String(pn));
      if (!key) continue;
      
      const cur = idx.get(key);
      if (!cur) {
        idx.set(key, { rows: [r], units });
      } else {
        cur.rows.push(r);
        // Keep the most comprehensive units
        if (Object.keys(cur.units).length < Object.keys(units).length) {
          cur.units = units;
        }
      }
    }
  }
  
  EXCEL_INDEX = idx;
  return idx;
}

/**
 * Find Excel match for a product using various identifiers
 */
function findExcelForProduct(params: ProductLookupParams): ExcelMatch | null {
  const idx = getExcelIndex();
  
  const candidates = [
    params.name,
    params.slug ? params.slug.replace(/-\d+$/g, '') : '',
    params.sku,
    params.translationKey,
  ].filter(Boolean) as string[];
  
  for (const c of candidates) {
    const key = normalizeExcelKey(c);
    const match = idx.get(key);
    if (match && match.rows.length) return match;
  }
  
  return null;
}

/**
 * Guess column key based on patterns
 */
function guessColumnKey(row: ExcelRow, patterns: RegExp[]): string | null {
  const keys = Object.keys(row || {});
  
  for (const re of patterns) {
    const k = keys.find(x => {
      const key = String(x);
      
      // Specific exclusions to prevent wrong matches
      if (re.test('conductor') && /ross section conductor/i.test(key)) return false;
      if (re.test('insulation thickness') && /Diameter over insulation/i.test(key)) return false;
      if (re.test('conductor') && !/^conductor$/i.test(key)) return false;
      if (re.test('insulation') && !/^insulation$/i.test(key)) return false;
      if (re.test('sheath') && !/^sheath$/i.test(key)) return false;
      if (re.test('norm') && !/^norm$/i.test(key)) return false;
      
      return re.test(key);
    });
    if (k) return k;
  }
  return null;
}

/**
 * Get unique non-empty values from an array
 */
function getUniqueNonEmpty(options: string[]): string[] {
  const uniq: string[] = [];
  const seen = new Set<string>();
  for (const v of options.map(normalizeValue).filter(Boolean)) {
    const k = v.toLowerCase();
    if (seen.has(k)) continue;
    seen.add(k);
    uniq.push(v);
  }
  return uniq;
}

/**
 * Get technical data for a product from Excel files
 */
export function getExcelTechnicalDataForProduct(params: ProductLookupParams): TechnicalData | null {
  const match = findExcelForProduct(params);
  if (!match || match.rows.length === 0) return null;
  
  const rows = match.rows;
  const sample = rows[0];
  
  // Find cross-section column
  const csKey = guessColumnKey(sample, [
    /number of cores and cross-section/i,
    /cross.?section/i,
    /ross section conductor/i,
  ]);
  
  if (!csKey) return null;
  
  // Extract configurations
  const voltageKey = guessColumnKey(sample, [/rated voltage/i, /voltage rating/i, /spannungs/i, /nennspannung/i]);
  
  const configurations = rows
    .map(r => {
      const cs = normalizeValue(String(r?.[csKey] ?? ''));
      const v = voltageKey ? normalizeValue(String(r?.[voltageKey] ?? '')) : '';
      if (!cs) return '';
      if (!v) return cs;
      const vHasUnit = /\bkv\b/i.test(v);
      const vText = vHasUnit ? v : `${v} kV`;
      return `${cs} - ${vText}`;
    })
    .filter(Boolean);
  
  if (configurations.length === 0) return null;
  
  // Extract technical attributes
  const attributes: Array<{ name: string; options: string[] }> = [];
  
  // Key technical columns
  const outerKey = guessColumnKey(sample, [/outer diameter\b/i, /outer diameter.*approx/i, /outer diameter of cable/i, /außen/i]);
  const weightKey = guessColumnKey(sample, [/weight\b/i, /gewicht/i, /cable weight/i]);
  const dcResKey = guessColumnKey(sample, [/dc resistance/i, /resistance conductor/i, /leiterwiderstand/i]);
  const ratedVoltKey = voltageKey;
  const testVoltKey = guessColumnKey(sample, [/test voltage/i, /prüfspannung/i]);
  const tempRangeKey = guessColumnKey(sample, [/operating temperature range/i, /temperature range/i, /temperaturbereich/i]);
  const conductorKey = guessColumnKey(sample, [/^conductor$/i]);
  const insulationKey = guessColumnKey(sample, [/^insulation$/i]);
  const sheathKey = guessColumnKey(sample, [/^sheath$/i]);
  const normKey = guessColumnKey(sample, [/^norm$/i, /^standard$/i]);
  const cprKey = guessColumnKey(sample, [/cpr class/i]);
  const packagingKey = guessColumnKey(sample, [/^packaging$/i]);
  const shapeKey = guessColumnKey(sample, [/shape of conductor/i]);
  const flameKey = guessColumnKey(sample, [/flame retardant/i]);
  const diamCondKey = guessColumnKey(sample, [/diameter conductor/i]);
  const diamInsKey = guessColumnKey(sample, [/diameter over insulation/i]);
  const diamScreenKey = guessColumnKey(sample, [/diameter over screen/i]);
  const metalScreenKey = guessColumnKey(sample, [/metallic screen/i]);
  const capacitanceKey = guessColumnKey(sample, [/capacitance/i]);
  const reactanceKey = guessColumnKey(sample, [/reactance/i]);
  const electricalStressKey = guessColumnKey(sample, [/electrical stress/i]);
  const pullingForceKey = guessColumnKey(sample, [/max\. pulling force/i, /pulling force/i]);
  const heatingTrefoilKey = guessColumnKey(sample, [/heating time constant.*trefoil/i]);
  const heatingFlatKey = guessColumnKey(sample, [/heating time constant.*flat/i]);
  const currentAirTrefoilKey = guessColumnKey(sample, [/current ratings in air.*trefoil/i]);
  const currentAirFlatKey = guessColumnKey(sample, [/current ratings in air.*flat/i]);
  const currentGroundTrefoilKey = guessColumnKey(sample, [/current ratings in ground.*trefoil/i]);
  const currentGroundFlatKey = guessColumnKey(sample, [/current ratings in ground.*flat/i]);
  const scCurrentCondKey = guessColumnKey(sample, [/conductor shortcircuit current/i]);
  const scCurrentScreenKey = guessColumnKey(sample, [/screen shortcircuit current/i]);
  const minLayKey = guessColumnKey(sample, [/minimal temperature for laying/i]);
  const minStoreKey = guessColumnKey(sample, [/minimal storage temperature/i]);
  const maxOpKey = guessColumnKey(sample, [/maximal operating conductor temperature/i, /max\. operating/i]);
  const maxScKey = guessColumnKey(sample, [/maximal short-circuit temperature/i, /short\s*circuit\s*temperature/i]);
  const insThkKey = guessColumnKey(sample, [/nominal insulation thickness/i, /insulation thickness/i]);
  const sheathThkKey = guessColumnKey(sample, [/nominal sheath thickness/i, /minimum sheath thickness/i]);
  const maxResKey = guessColumnKey(sample, [/maximum resistance of conductor/i]);
  const bendKey = guessColumnKey(sample, [/bending radius/i, /min\. bending radius/i]);
  
  // Helper to add attribute
  const addAttr = (name: string, key: string | null, unit?: string) => {
    if (!key) return;
    const options = rows
      .map(r => normalizeValue(String(r?.[key] ?? '')))
      .map(v => (unit && v && looksNumeric(v) ? `${v} ${unit}` : v))
      .filter(Boolean);
    
    if (options.length === 0) return;
    
    const uniqueOptions = getUniqueNonEmpty(options);
    attributes.push({ name, options: uniqueOptions });
  };
  
  // Add attributes
  addAttr('Outer diameter', outerKey, 'mm');
  addAttr('Weight', weightKey, 'kg/km');
  addAttr('DC resistance at 20 °C', dcResKey, 'Ω/km');
  addAttr('Rated voltage', ratedVoltKey, '');
  addAttr('Test voltage', testVoltKey, '');
  addAttr('Operating temperature range', tempRangeKey, '');
  addAttr('Minimal temperature for laying', minLayKey, '');
  addAttr('Minimal storage temperature', minStoreKey, '');
  addAttr('Maximal operating conductor temperature', maxOpKey, '');
  addAttr('Maximal short-circuit temperature', maxScKey, '');
  addAttr('Nominal insulation thickness', insThkKey, 'mm');
  addAttr('Nominal sheath thickness', sheathThkKey, 'mm');
  addAttr('Maximum resistance of conductor', maxResKey, 'Ω/km');
  addAttr('Conductor', conductorKey, '');
  addAttr('Insulation', insulationKey, '');
  addAttr('Sheath', sheathKey, '');
  addAttr('Standard', normKey, '');
  addAttr('Conductor diameter', diamCondKey, 'mm');
  addAttr('Insulation diameter', diamInsKey, 'mm');
  addAttr('Screen diameter', diamScreenKey, 'mm');
  addAttr('Metallic screen', metalScreenKey, '');
  addAttr('Max. pulling force', pullingForceKey, '');
  addAttr('Electrical stress conductor', electricalStressKey, '');
  addAttr('Electrical stress insulation', electricalStressKey, '');
  addAttr('Reactance', reactanceKey, '');
  addAttr('Heating time constant trefoil', heatingTrefoilKey, 's');
  addAttr('Heating time constant flat', heatingFlatKey, 's');
  addAttr('Flame retardant', flameKey, '');
  addAttr('CPR class', cprKey, '');
  addAttr('Packaging', packagingKey, '');
  addAttr('Bending radius', bendKey, 'mm');
  addAttr('Shape of conductor', shapeKey, '');
  addAttr('Capacitance', capacitanceKey, 'μF/km');
  addAttr('Current ratings in air, trefoil', currentAirTrefoilKey, 'A');
  addAttr('Current ratings in air, flat', currentAirFlatKey, 'A');
  addAttr('Current ratings in ground, trefoil', currentGroundTrefoilKey, 'A');
  addAttr('Current ratings in ground, flat', currentGroundFlatKey, 'A');
  addAttr('Conductor shortcircuit current', scCurrentCondKey, 'kA');
  addAttr('Screen shortcircuit current', scCurrentScreenKey, 'kA');
  
  return {
    configurations,
    attributes,
  };
}

/**
 * Get raw Excel rows for a product (for detailed inspection)
 */
export function getExcelRowsForProduct(params: ProductLookupParams): ExcelRow[] {
  const match = findExcelForProduct(params);
  return match?.rows || [];
}

/**
 * Clear the Excel index cache (useful for development)
 */
export function clearExcelCache(): void {
  EXCEL_INDEX = null;
}

/**
 * Preload Excel data on module initialization
 * This ensures the cache is built during build time
 */
export function preloadExcelData(): void {
  getExcelIndex();
}

// Preload when imported
if (require.main === module) {
  preloadExcelData();
}