const fs = require('fs');
const path = require('path');
const os = require('os');
const zlib = require('zlib');
const logger = require('../logger');

const MAX_FILE_SIZE = 2 * 1024 * 1024; // 2MB per file

/**
 * Get the HytaleSaves directory (game client logs)
 */
function getHytaleSavesDir() {
  const home = os.homedir();
  if (process.platform === 'win32') {
    return path.join(home, 'AppData', 'Local', 'HytaleSaves');
  } else if (process.platform === 'darwin') {
    return path.join(home, 'Library', 'Application Support', 'HytaleSaves');
  } else {
    return path.join(home, '.hytalesaves');
  }
}

/**
 * Read a log file, capping at MAX_FILE_SIZE (keeps tail/most recent lines)
 */
function readLogFile(filePath) {
  try {
    const stats = fs.statSync(filePath);
    if (stats.size <= MAX_FILE_SIZE) {
      return fs.readFileSync(filePath, 'utf8');
    }

    // Read only the last MAX_FILE_SIZE bytes
    const fd = fs.openSync(filePath, 'r');
    const buffer = Buffer.alloc(MAX_FILE_SIZE);
    fs.readSync(fd, buffer, 0, MAX_FILE_SIZE, stats.size - MAX_FILE_SIZE);
    fs.closeSync(fd);

    const content = buffer.toString('utf8');
    // Skip first partial line
    const firstNewline = content.indexOf('\n');
    const trimmed = firstNewline >= 0 ? content.substring(firstNewline + 1) : content;
    return `[... truncated ${stats.size - MAX_FILE_SIZE} bytes ...]\n` + trimmed;
  } catch (err) {
    return `[Error reading file: ${err.message}]`;
  }
}

/**
 * Get files matching a date pattern from a directory
 */
function getFilesForDate(dir, dateStr, pattern) {
  if (!fs.existsSync(dir)) return [];

  try {
    return fs.readdirSync(dir)
      .filter(f => f.includes(dateStr) && (pattern ? pattern.test(f) : true))
      .map(f => ({
        name: f,
        path: path.join(dir, f),
        mtime: fs.statSync(path.join(dir, f)).mtime
      }))
      .sort((a, b) => b.mtime - a.mtime);
  } catch (err) {
    return [];
  }
}

// ============================================================================
// ZIP BUILDER (pure Node.js, no dependencies)
// ============================================================================

/**
 * CRC32 lookup table
 */
const crc32Table = new Uint32Array(256);
for (let i = 0; i < 256; i++) {
  let c = i;
  for (let j = 0; j < 8; j++) {
    c = (c & 1) ? (0xEDB88320 ^ (c >>> 1)) : (c >>> 1);
  }
  crc32Table[i] = c;
}

function crc32(buf) {
  let crc = 0xFFFFFFFF;
  for (let i = 0; i < buf.length; i++) {
    crc = crc32Table[(crc ^ buf[i]) & 0xFF] ^ (crc >>> 8);
  }
  return (crc ^ 0xFFFFFFFF) >>> 0;
}

/**
 * Create a ZIP file from an array of {name, content} entries
 * Uses DEFLATE compression via built-in zlib
 */
function createZipBuffer(files) {
  const localHeaders = [];
  const centralEntries = [];
  let offset = 0;

  for (const file of files) {
    const nameBytes = Buffer.from(file.name, 'utf8');
    const contentBytes = Buffer.from(file.content, 'utf8');
    const compressed = zlib.deflateRawSync(contentBytes);
    const crcVal = crc32(contentBytes);

    // Local file header (30 bytes + filename)
    const local = Buffer.alloc(30);
    local.writeUInt32LE(0x04034b50, 0);   // signature
    local.writeUInt16LE(20, 4);            // version needed
    local.writeUInt16LE(0, 6);             // flags
    local.writeUInt16LE(8, 8);             // compression: DEFLATE
    local.writeUInt16LE(0, 10);            // mod time
    local.writeUInt16LE(0, 12);            // mod date
    local.writeUInt32LE(crcVal, 14);       // crc32
    local.writeUInt32LE(compressed.length, 18); // compressed size
    local.writeUInt32LE(contentBytes.length, 22); // uncompressed size
    local.writeUInt16LE(nameBytes.length, 26);    // filename length
    local.writeUInt16LE(0, 28);            // extra field length

    localHeaders.push(local, nameBytes, compressed);

    // Central directory entry (46 bytes + filename)
    const central = Buffer.alloc(46);
    central.writeUInt32LE(0x02014b50, 0);  // signature
    central.writeUInt16LE(20, 4);           // version made by
    central.writeUInt16LE(20, 6);           // version needed
    central.writeUInt16LE(0, 8);            // flags
    central.writeUInt16LE(8, 10);           // compression
    central.writeUInt16LE(0, 12);           // mod time
    central.writeUInt16LE(0, 14);           // mod date
    central.writeUInt32LE(crcVal, 16);      // crc32
    central.writeUInt32LE(compressed.length, 20); // compressed size
    central.writeUInt32LE(contentBytes.length, 24); // uncompressed size
    central.writeUInt16LE(nameBytes.length, 28);    // filename length
    central.writeUInt16LE(0, 30);           // extra field length
    central.writeUInt16LE(0, 32);           // comment length
    central.writeUInt16LE(0, 34);           // disk number
    central.writeUInt16LE(0, 36);           // internal attrs
    central.writeUInt32LE(0, 38);           // external attrs
    central.writeUInt32LE(offset, 42);      // local header offset

    centralEntries.push(central, nameBytes);
    offset += 30 + nameBytes.length + compressed.length;
  }

  const centralDirBuf = Buffer.concat(centralEntries);

  // End of central directory (22 bytes)
  const eocd = Buffer.alloc(22);
  eocd.writeUInt32LE(0x06054b50, 0);       // signature
  eocd.writeUInt16LE(0, 4);                // disk number
  eocd.writeUInt16LE(0, 6);                // cd disk number
  eocd.writeUInt16LE(files.length, 8);     // entries on disk
  eocd.writeUInt16LE(files.length, 10);    // total entries
  eocd.writeUInt32LE(centralDirBuf.length, 12); // cd size
  eocd.writeUInt32LE(offset, 16);          // cd offset
  eocd.writeUInt16LE(0, 20);              // comment length

  return Buffer.concat([...localHeaders, centralDirBuf, eocd]);
}

// ============================================================================

/**
 * Collect all relevant logs for submission
 * Returns { files: [{name, content}], meta: {username, platform, version} }
 */
function collectLogs() {
  const files = [];
  const today = new Date();
  const todayStr = today.toISOString().split('T')[0]; // YYYY-MM-DD

  const yesterday = new Date(today);
  yesterday.setDate(yesterday.getDate() - 1);
  const yesterdayStr = yesterday.toISOString().split('T')[0];

  // 1. Launcher logs
  const launcherLogDir = logger.getLogDirectory();
  if (launcherLogDir && fs.existsSync(launcherLogDir)) {
    // Today's launcher logs
    const todayLogs = getFilesForDate(launcherLogDir, todayStr, /^launcher-.*\.log$/);
    for (const f of todayLogs) {
      files.push({ name: `launcher/${f.name}`, content: readLogFile(f.path) });
    }

    // Most recent from yesterday (just one)
    const yesterdayLogs = getFilesForDate(launcherLogDir, yesterdayStr, /^launcher-.*\.log$/);
    if (yesterdayLogs.length > 0) {
      files.push({ name: `launcher/${yesterdayLogs[0].name}`, content: readLogFile(yesterdayLogs[0].path) });
    }
  }

  // 2. Game client logs
  const savesDir = getHytaleSavesDir();
  const clientLogDir = path.join(savesDir, 'Logs');
  if (fs.existsSync(clientLogDir)) {
    const clientLogs = getFilesForDate(clientLogDir, todayStr, /client\.log$/);
    for (const f of clientLogs) {
      files.push({ name: `client/${f.name}`, content: readLogFile(f.path) });
    }
  }

  // 3. Config snapshot
  const appDir = logger.getAppDir ? logger.getAppDir() : logger.getInstallPath();
  const configPath = path.join(appDir, 'config.json');
  if (fs.existsSync(configPath)) {
    try {
      const configContent = fs.readFileSync(configPath, 'utf8');
      files.push({ name: 'config.json', content: configContent });
    } catch (err) {
      files.push({ name: 'config.json', content: `[Error reading config: ${err.message}]` });
    }
  }

  // Build metadata
  const { app } = require('electron');
  let username = 'unknown';
  try {
    const configFile = path.join(appDir, 'config.json');
    if (fs.existsSync(configFile)) {
      const cfg = JSON.parse(fs.readFileSync(configFile, 'utf8'));
      username = cfg.username || cfg.playerName || 'unknown';
    }
  } catch (err) {}

  return {
    files,
    meta: {
      username,
      platform: `${process.platform}-${process.arch}`,
      version: app.getVersion()
    }
  };
}

module.exports = { collectLogs, createZipBuffer };
