'use strict';
const fs = require('fs');
const path = require('path');
const readline = require('readline');
const { parseTargetFilename, parseResultFilename, parseTimestamp, parseElapsedTime, parseTputRssi } = require('./parser');
const { upsertTest, markCompleted, clearTests } = require('./db');

/**
 * Full rebuild: clears the DB, walks target dir to build the test list,
 * then walks results dir to mark completions.
 */
async function fullScan(targetDir, resultsDir) {
  clearTests();
  if (!targetDir || !resultsDir) return;

  console.log(`[scanner] target dir : ${targetDir}`);
  console.log(`[scanner] results dir: ${resultsDir}`);

  await scanTargets(targetDir);
  await scanResults(resultsDir);
}

async function scanTargets(targetDir) {
  // Walk the target directory, find all TC_WIFI_*.ini files, parse their names,
  // and upsert them into the DB. Ignore files without a valid test_id.
  let parentEntries;
  try {
    parentEntries = fs.readdirSync(targetDir, { withFileTypes: true }).filter(d => d.isDirectory());
  } catch (e) {
    console.error('[scanner] Cannot read target dir:', e.message);
    return;
  }

  console.log(`[scanner] subdirectories found: ${parentEntries.length}`);

  for (const parentEntry of parentEntries) {
    const parentDirPath = path.join(targetDir, parentEntry.name);
    let files;
    try {
      files = fs.readdirSync(parentDirPath, { withFileTypes: true })
        .filter(f => f.isFile() && !f.name.startsWith('GLOBAL') && f.name.endsWith('.ini'));
    } catch (e) {
      console.error(`[scanner] Cannot read parent dir ${parentEntry.name}:`, e.message);
      continue;
    }

    console.log(`[scanner]   ${parentEntry.name} → ${files.length} target test file(s)`);

    for (const file of files) {
      const parsed = parseTargetFilename(file.name, parentEntry.name);
      if (!parsed.test_id) {
        console.log(`[scanner] skip (no test_id): ${parentEntry.name}/${file.name}`);
        continue;
      }
      console.log(`[scanner] found: ${parentEntry.name}/${file.name}  →  test_id=${parsed.test_id}`);
      upsertTest({
        id:           parsed.id,
        test_id:      parsed.test_id,
        parent_dir:   parentEntry.name,
        filename:     file.name,
        interference: parsed.interference,
        device:       parsed.device,
        rotation:     parsed.rotation,
        test_point:   parsed.test_point,
        station:      parsed.station,
        band:         parsed.band,
        channel:      parsed.channel,
        bandwidth:    parsed.bandwidth,
        rssi:         parsed.rssi,
        direction:    parsed.direction,
      });
    }
  }

}

/**
 * Scan the results directory and mark all matched tests as completed.
 */
async function scanResults(resultsDir) {
  let entries;
  try {
    entries = fs.readdirSync(resultsDir, { withFileTypes: true }).filter(d => d.isDirectory());
  } catch (e) {
    console.error('[scanner] Cannot read results dir:', e.message);
    return;
  }
  console.log(`[scanner] results: ${entries.length} result dir(s) found`);
  for (const entry of entries) {
    await processResultDir(resultsDir, entry.name);
  }
}

/**
 * Process one result directory: extract test_id, find the latest matching
 * log file, parse its elapsed-time line, and mark the test as completed.
 */
async function processResultDir(resultsDir, resultDirName) {
  const { test_id, device } = parseResultFilename(resultDirName);
  //console.log(`[scanner] result dir: ${resultDirName}  →  test_id=${test_id}  device=${device}`);
  if (!test_id || !device) return;

  const resultDirPath = path.join(resultsDir, resultDirName);

  let logFiles;
  try {
    logFiles = fs.readdirSync(resultDirPath, { withFileTypes: true })
      .filter(f => f.isFile() && f.name.endsWith('.txt') && f.name.includes(test_id))
      .map(f => f.name);
  } catch (e) {
    console.error(`[scanner] Cannot read result dir ${resultDirName}:`, e.message);
    return;
  }

  if (logFiles.length === 0) {
    // Result directory exists but log not written yet — mark complete without timing
    console.log(`[scanner] completed (no log yet): ${test_id}`);
    markCompleted(test_id, device, null, null);
    return;
  }

  // For re-runs, the latest log file has the latest timestamp in its name
  const latestLog = logFiles.sort().at(-1);
  const logPath   = path.join(resultDirPath, latestLog);

  const completed_at = parseTimestamp(latestLog);
  const { duration_seconds, tputResults } = await extractLogData(logPath);

  console.log(`[scanner] completed: ${test_id}  device=${device}  duration=${duration_seconds}s  stations=${tputResults.length}  at=${completed_at}`);
  markCompleted(test_id, device, completed_at, duration_seconds, tputResults);
}

/**
 * Single-pass log file reader: extracts elapsed time and all station tput/rssi entries.
 */
function extractLogData(logFilePath) {
  const ELAPSED_REGEX = /\[.*?INFO\]\s+Elapsed time\s*:\s*([\d]+:[\d]{2}:[\d]{2}\.[\d]+)/;
  const TPUT_RSSI_REGEX = /\[.*?INFO\]\s+STA(\d+) over angles >> AVG IxChariot TPUT: (\d+) Mbps, AVG DL RSSI: (-?\d+) dBm, AVG UL RSSI: (-?\d+) dBm/ 

  return new Promise((resolve) => {
    const result = { duration_seconds: null, tputResults: [] };
    let stream;
    try {
      stream = fs.createReadStream(logFilePath, { encoding: 'utf8' });
    } catch (e) {
      console.error(`[scanner] Cannot read log file ${logFilePath}:`, e.message);
      return resolve(result);
    }

    const rl = readline.createInterface({ input: stream, crlfDelay: Infinity });

    rl.on('line', (line) => {
      const timeMatch = line.match(ELAPSED_REGEX);
      const tputMatch = line.match(TPUT_RSSI_REGEX);

      if (timeMatch) result.duration_seconds = parseElapsedTime(timeMatch[1]);
      else if (tputMatch) {
        parsed = parseTputRssi(line);
        if (parsed) result.tputResults.push(parsed);
      }
    });

    rl.on('close', () => resolve(result));
    rl.on('error', () => resolve(result));
    stream.on('error', () => resolve(result));
  });
}

module.exports = { fullScan, scanResults, processResultDir };
