/**
 * Config migration system
 *
 * When config.json schema changes between versions, register a migration
 * function here. On load, the loader detects the stored version, runs all
 * migrations from that version forward, and writes the result back.
 *
 * Migration format:
 *   migrations[<toVersion>] = (rawConfig) => { ...mutations, _version: toVersion }
 *
 * Each migration is responsible for transforming the previous version's
 * shape into the next version's shape. They run sequentially, so v1→v2→v3
 * all execute in order.
 *
 * For first-time users with no config file, the loader creates a fresh
 * config with CURRENT_VERSION, so they start at the latest schema.
 */
const fs = require('fs');
const path = require('path');
const _platformPaths = require('../../platform-paths');

const CURRENT_VERSION = 2;

/**
 * Migrations: keys are the version they PRODUCE.
 * Each migration takes a raw config object and returns the next version.
 */
const migrations = {
  // v0 (unversioned) → v1: add _version field, normalize dns structure
  1: (raw) => {
    const migrated = { ...raw };
    if (!migrated._version) migrated._version = 1;
    // Normalize: older configs may have dns as a string IP, convert to object
    if (typeof migrated.dns === 'string') {
      migrated.dns = { ip: migrated.dns, port: 5380 };
    } else if (!migrated.dns) {
      migrated.dns = { ip: '', port: 5380 };
    }
    return migrated;
  },

  // v1 → v2: add dns.provider field (default: 'technitium' for backwards compat)
  2: (raw) => {
    const migrated = { ...raw };
    if (migrated.dns && !migrated.dns.provider) {
      migrated.dns.provider = 'technitium';
    }
    migrated._version = 2;
    return migrated;
  }
};

/**
 * Run all migrations from `fromVersion` (or detected) to CURRENT_VERSION.
 * @param {object} raw - The raw config object (may or may not have _version)
 * @returns {object} The migrated config
 */
function migrate(raw) {
  if (!raw || typeof raw !== 'object') {
    // First-time load: return minimal config at current version
    return { _version: CURRENT_VERSION };
  }

  const fromVersion = raw._version || 0;
  if (fromVersion > CURRENT_VERSION) {
    // Config from a future version — bail out, don't corrupt it
    // The validation step will catch any actual issues
    return raw;
  }

  let current = { ...raw };
  for (let v = fromVersion + 1; v <= CURRENT_VERSION; v++) {
    if (migrations[v]) {
      current = migrations[v](current);
    } else {
      // No migration defined for this version, just bump _version
      current._version = v;
    }
  }
  return current;
}

/**
 * Load config from disk, run migrations if needed, and write back the
 * migrated version. Safe to call on every startup.
 * @param {string} configFile - Absolute path to config.json
 * @param {object} log - Logger instance
 * @returns {object} The migrated config object
 */
function loadAndMigrate(configFile, log) {
  let raw = null;
  let fileExisted = false;

  if (fs.existsSync(configFile)) {
    fileExisted = true;
    try {
      raw = JSON.parse(fs.readFileSync(configFile, 'utf8'));
    } catch (e) {
      if (log && log.error) {
        log.error('config-migration', 'Failed to parse config.json, using defaults', { error: e.message });
      }
      raw = null;
    }
  }

  const fromVersion = raw && raw._version ? raw._version : 0;
  const migrated = migrate(raw);

  // Only write back to disk if:
  //   1. The file already existed (we don't create configs on fresh installs —
  //      the loader's defaults handle that case), AND
  //   2. The version actually changed (no point rewriting identical content)
  if (fileExisted && fromVersion < CURRENT_VERSION) {
    if (log && log.info) {
      log.info('config-migration', `Migrated config v${fromVersion} → v${CURRENT_VERSION}`, {
        from: fromVersion,
        to: CURRENT_VERSION,
        path: configFile
      });
    }
    // Write back the migrated config
    try {
      // Ensure parent dir exists
      const dir = path.dirname(configFile);
      if (!fs.existsSync(dir)) fs.mkdirSync(dir, { recursive: true });
      fs.writeFileSync(configFile, JSON.stringify(migrated, null, 2));
    } catch (e) {
      if (log && log.warn) {
        log.warn('config-migration', 'Failed to write migrated config back to disk', { error: e.message });
      }
    }
  }

  return migrated;
}

module.exports = {
  CURRENT_VERSION,
  migrations,
  migrate,
  loadAndMigrate
};
