import { sveltekit } from '@sveltejs/kit/vite';
import { defineConfig } from 'vite';
import fs from 'fs';
import path from 'path';
import XLSX from 'xlsx';

function generateProjectManifest() {
  const staticDir = path.resolve('static');
  const projectesDir = path.join(staticDir, 'projectes');

  if (!fs.existsSync(projectesDir)) return;

  const MCM_REGEX = /SYSDL[_ ]+(MCM\d+)/i;
  const projects: { name: string; mcms: { name: string; excelPath: string; pdfPath: string | null }[] }[] = [];

  for (const projectName of fs.readdirSync(projectesDir)) {
    const projectDir = path.join(projectesDir, projectName);
    if (!fs.statSync(projectDir).isDirectory()) continue;

    const excelDir = path.join(projectDir, 'excel');
    const pdfDir = path.join(projectDir, 'pdf');
    const mcmMap = new Map<string, { excelPath: string; pdfPath: string | null }>();

    if (fs.existsSync(excelDir)) {
      for (const f of fs.readdirSync(excelDir)) {
        if (!f.endsWith('.xlsx')) continue;
        const match = f.match(MCM_REGEX);
        if (!match) continue;
        const mcm = match[1];
        mcmMap.set(mcm, {
          excelPath: `/projectes/${projectName}/excel/${f}`,
          pdfPath: null,
        });
      }
    }

    if (fs.existsSync(pdfDir)) {
      for (const f of fs.readdirSync(pdfDir)) {
        if (!f.endsWith('.pdf')) continue;
        const match = f.match(MCM_REGEX);
        if (!match) continue;
        const mcm = match[1];
        if (mcmMap.has(mcm)) {
          mcmMap.get(mcm)!.pdfPath = `/projectes/${projectName}/pdf/${f}`;
        } else {
          mcmMap.set(mcm, {
            excelPath: '',
            pdfPath: `/projectes/${projectName}/pdf/${f}`,
          });
        }
      }
    }

    if (mcmMap.size > 0) {
      const mcms = [...mcmMap.entries()]
        .map(([name, paths]) => ({ name, ...paths }))
        .sort((a, b) => a.name.localeCompare(b.name));
      projects.push({ name: projectName, mcms });
    }
  }

  projects.sort((a, b) => a.name.localeCompare(b.name));
  fs.writeFileSync(
    path.join(projectesDir, 'manifest.json'),
    JSON.stringify(projects, null, 2)
  );
}

/** Classify a device name to a symbol ID, or null to skip */
function classifyDevice(dev: string): string | null {
  // Skip
  if (/ENSH|ENW/.test(dev)) return null;
  if (/VFD_DI?SC/.test(dev)) return null;
  if (/_LT$/.test(dev)) return null;
  if (/EPC\d+_\d+/.test(dev)) return null;
  if (/BCN\d+_[HBWG]$/.test(dev)) return null;
  if (dev === 'SPARE') return null;
  if (/ESTOP/.test(dev)) return null;
  if (/SCALE/.test(dev)) return null;
  if (/_STO\d*$/.test(dev)) return null;
  if (/PRX\d*$/.test(dev)) return null;
  if (/LRPE/.test(dev)) return null;

  // Match
  if (/EPC\d*$/.test(dev)) return 'epc';
  if (/[TLJF]PE\d*$/.test(dev)) return 'photoeye';
  if (/BDS\d+_[RS]$/.test(dev)) return 'photoeye';
  if (/TS\d+_[RS]$/.test(dev)) return 'photoeye';
  if (/JR\d*_PB$/.test(dev)) return 'jam_reset';
  if (/SS\d+_S[TP]*PB$/.test(dev)) return 'start_stop';
  if (/S\d+_PB$/.test(dev) && !/^SS/.test(dev)) return 'start';
  if (/EN\d+_PB$/.test(dev)) return 'chute_enable';
  if (/PR\d+_PB$/.test(dev)) return 'package_release';
  if (/SOL\d*$/.test(dev)) return 'solenoid';
  if (/DIV\d+_LS/.test(dev)) return 'diverter';
  if (/BCN\d*$/.test(dev) || /BCN\d+_[AR]$/.test(dev)) return 'beacon';
  if (/^PDP\d+_CB/.test(dev)) return 'pdp';
  if (/^PDP\d+_PMM/.test(dev)) return 'pdp';
  if (/FIO[HM]?\d*$/.test(dev) || /SIO\d*$/.test(dev)) return 'fio_sio_fioh';
  if (/PS\d*$/.test(dev) && !/^PS\d/.test(dev)) return 'pressure_sensor';

  return null;
}

/** Extract zone prefix from device name, e.g. "NCP1_1_TPE1" -> "NCP1_1" */
function extractZone(dev: string): string {
  // PDP devices: PDP04_CB1 -> PDP04
  const pdpM = dev.match(/^(PDP\d+)/);
  if (pdpM) return pdpM[1];
  // Standard: ZONE_NUM_REST
  const m = dev.match(/^([A-Z]+\d*_\d+[A-Z]?)/);
  return m ? m[1] : 'OTHER';
}

function generateDeviceManifest() {
  const projectesDir = path.resolve('..', 'projectes');
  const staticDir = path.resolve('static', 'projectes');
  if (!fs.existsSync(projectesDir)) return;

  const manifest: Record<string, { id: string; svg: string; zone: string }[]> = {};

  for (const projectName of fs.readdirSync(projectesDir)) {
    const projectDir = path.join(projectesDir, projectName);
    if (!fs.statSync(projectDir).isDirectory()) continue;
    const excelDir = path.join(projectDir, 'excel');
    if (!fs.existsSync(excelDir)) continue;

    // Find device IO file and IP addresses file
    const files = fs.readdirSync(excelDir).filter(f => f.endsWith('.xlsx'));
    const ioFile = files.find(f => /Devices?\s*IO/i.test(f));
    const ipFile = files.find(f => /IP\s*Address/i.test(f));
    if (!ioFile) continue;

    const ioWb = XLSX.readFile(path.join(excelDir, ioFile));

    // Also load IP addresses for VFDs, FIOMs, DPMs
    let ipWb: XLSX.WorkBook | null = null;
    if (ipFile) {
      try { ipWb = XLSX.readFile(path.join(excelDir, ipFile)); } catch {}
    }

    for (const sheetName of ioWb.SheetNames) {
      const mcmMatch = sheetName.match(/^(MCM\d+)$/);
      if (!mcmMatch) continue;
      const mcm = mcmMatch[1];

      const seen = new Set<string>();
      const devices: { id: string; svg: string; zone: string }[] = [];

      // Parse IO sheet
      const ws = ioWb.Sheets[sheetName];
      const rows = XLSX.utils.sheet_to_json(ws, { header: 1, defval: '' }) as string[][];

      // Extract FIO/SIO controllers from column 0 (controller name)
      for (let i = 1; i < rows.length; i++) {
        const ctrl = String(rows[i][0] || '').trim();
        if (!ctrl || seen.has(ctrl)) continue;
        if (/FIO|SIO/i.test(ctrl)) {
          seen.add(ctrl);
          devices.push({ id: ctrl, svg: 'fio_sio_fioh', zone: extractZone(ctrl) });
        }
      }

      // Extract assigned devices from column 3
      for (let i = 1; i < rows.length; i++) {
        const dev = String(rows[i][3] || '').trim();
        if (!dev || seen.has(dev)) continue;
        seen.add(dev);

        const svg = classifyDevice(dev);
        if (!svg) continue;

        const zone = extractZone(dev);

        // Consolidate PDP: only add once per PDP number
        if (svg === 'pdp') {
          const pdpId = zone; // e.g. PDP04
          if (devices.some(d => d.id === pdpId)) continue;
          devices.push({ id: pdpId, svg, zone });
          continue;
        }

        // Consolidate beacon: BCN1_A + BCN1_H -> one BCN1 per zone
        if (svg === 'beacon') {
          const bcnBase = dev.replace(/_[ARHBWG]$/, '');
          if (devices.some(d => d.id === bcnBase)) continue;
          devices.push({ id: bcnBase, svg, zone });
          continue;
        }

        devices.push({ id: dev, svg, zone });
      }

      // Parse IP/network sheet for VFDs, FIOMs, DPMs
      if (ipWb) {
        const ipSheets = ipWb.SheetNames.filter(s => s.toUpperCase().includes(mcm));
        for (const ipSheet of ipSheets) {
          const ipWs = ipWb.Sheets[ipSheet];
          const ipRows = XLSX.utils.sheet_to_json(ipWs, { header: 1, defval: '' }) as string[][];
          for (const row of ipRows) {
            for (const cell of row) {
              const val = String(cell || '').trim();
              if (!val || val.startsWith('11.') || seen.has(val)) continue;
              if (/^[A-Z]+\d*_\d+.*VFD$/.test(val)) {
                seen.add(val);
                devices.push({ id: val, svg: 'conveyor', zone: extractZone(val) });
              } else if (/^[A-Z]+\d*_\d+.*FIOM\d*$/.test(val)) {
                seen.add(val);
                devices.push({ id: val, svg: 'fio_sio_fioh', zone: extractZone(val) });
              } else if (/^[A-Z]+\d*_\d+.*DPM\d*$/.test(val) && !/_P\d+$/.test(val)) {
                seen.add(val);
                devices.push({ id: val, svg: 'dpm', zone: extractZone(val) });
              }
            }
          }
        }
      }

      // Always include the MCM symbol itself
      if (!devices.some(d => d.id === mcm)) {
        devices.push({ id: mcm, svg: 'mcm', zone: mcm });
      }

      // Sort by zone then id
      devices.sort((a, b) => a.zone.localeCompare(b.zone) || a.id.localeCompare(b.id));
      manifest[mcm] = devices;
    }
  }

  fs.writeFileSync(
    path.join(staticDir, 'devices-manifest.json'),
    JSON.stringify(manifest, null, 2)
  );
}

export default defineConfig({
  plugins: [
    {
      name: 'generate-project-manifest',
      buildStart() {
        generateProjectManifest();
        generateDeviceManifest();
      },
      configureServer(server) {
        generateProjectManifest();
        generateDeviceManifest();
        // Re-generate on file changes in static/projectes
        server.watcher.add(path.resolve('static/projectes'));
        server.watcher.on('add', (p) => {
          if (p.includes('projectes') && !p.endsWith('manifest.json')) {
            generateProjectManifest();
          }
        });
      },
    },
    sveltekit(),
  ],
});
