/**
 * Config Schema Validation for DashCaddy
 * Validates config.json structure to catch typos and invalid values early.
 */

const VALID_THEMES = ['dark', 'light', 'blue'];
const VALID_ROUTING_MODES = ['subdomain', 'subdirectory'];
const VALID_DNS_PROVIDERS = ['technitium', 'cloudflare', 'rfc2136', 'manual'];

const KNOWN_KEYS = [
  'tld', 'caName', 'dns', 'dnsServers', 'dashboardHost', 'timezone', 'theme',
  'updatedAt', 'timestamp', 'logo', 'logoPosition', 'favicon', 'weather',
  'setupComplete', 'setupCompleted', 'setupMode', 'onboardingCompleted',
  'configurationType', 'defaults', 'customLogo', 'customFavicon',
  'dashboardTitle', 'tailscale', 'license', 'skipped',
  'routingMode', 'domain', 'email', 'defaultIP', 'pylon',
  'customLogoDark', 'customLogoLight', 'language',
  // license-manager.js persists the last activation to config.licenseBackup
  // (restore-on-restart path); src/config/migrations.js stamps _version.
  // Both are first-party writes — see DC-091.
  'licenseBackup', '_version'
];

/**
 * @param {string[]} arr
 * @param {string} val
 * @returns {boolean}
 */
function isInArray(arr, val) {
  return arr.includes(val);
}

/**
 * @param {{errors:string[], warnings:string[]}} ctx
 * @param {object} config
 */
function validateTld(ctx, config) {
  if (config.tld === undefined) return;
  if (typeof config.tld !== 'string') {
    ctx.errors.push('tld must be a string');
    return;
  }
  const tld = config.tld.startsWith('.') ? config.tld : '.' + config.tld;
  if (!/^\.[a-z0-9][a-z0-9-]*$/.test(tld)) {
    ctx.errors.push(`tld "${config.tld}" contains invalid characters (use lowercase alphanumeric)`);
  }
  if (tld.length > 20) {
    ctx.warnings.push(`tld "${config.tld}" is unusually long`);
  }
}

/**
 * @param {{errors:string[], warnings:string[]}} ctx
 * @param {object} config
 */
function validateDns(ctx, config) {
  if (config.dns === undefined) return;
  if (typeof config.dns !== 'object' || config.dns === null) {
    ctx.errors.push('dns must be an object');
    return;
  }
  if (config.dns.ip !== undefined && typeof config.dns.ip !== 'string') {
    ctx.errors.push('dns.ip must be a string');
  }
  if (config.dns.ip && !/^[\d.]+$/.test(config.dns.ip) && !/^[a-zA-Z0-9.-]+$/.test(config.dns.ip)) {
    ctx.errors.push(`dns.ip "${config.dns.ip}" is not a valid IP address or hostname`);
  }
  if (config.dns.port !== undefined) {
    const port = parseInt(config.dns.port, 10);
    if (isNaN(port) || port < 1 || port > 65535) {
      ctx.errors.push(`dns.port "${config.dns.port}" is not a valid port number (1-65535)`);
    }
  }
  if (config.dns.servers !== undefined) {
    if (typeof config.dns.servers !== 'object' || config.dns.servers === null) {
      ctx.errors.push('dns.servers must be an object');
    }
  }
  if (config.dns.provider !== undefined) {
    if (typeof config.dns.provider !== 'string') {
      ctx.errors.push('dns.provider must be a string');
    } else if (!isInArray(VALID_DNS_PROVIDERS, config.dns.provider)) {
      ctx.warnings.push(`dns.provider "${config.dns.provider}" is not one of: ${VALID_DNS_PROVIDERS.join(', ')}. It may still work if a custom adapter is installed.`);
    }
  }
}

/**
 * @param {{errors:string[], warnings:string[]}} ctx
 * @param {object} config
 */
function validateDashboardHost(ctx, config) {
  if (config.dashboardHost === undefined) return;
  if (typeof config.dashboardHost !== 'string') {
    ctx.errors.push('dashboardHost must be a string');
  } else if (config.dashboardHost && !/^[a-zA-Z0-9][a-zA-Z0-9.-]*$/.test(config.dashboardHost)) {
    ctx.errors.push(`dashboardHost "${config.dashboardHost}" contains invalid characters`);
  }
}

/**
 * @param {{errors:string[], warnings:string[]}} ctx
 * @param {object} config
 */
function validateTimezone(ctx, config) {
  if (config.timezone === undefined) return;
  if (typeof config.timezone !== 'string') {
    ctx.errors.push('timezone must be a string');
  } else if (config.timezone) {
    try {
      Intl.DateTimeFormat(undefined, { timeZone: config.timezone });
    } catch {
      ctx.errors.push(`timezone "${config.timezone}" is not a recognized IANA timezone`);
    }
  }
}

/**
 * @param {{errors:string[], warnings:string[]}} ctx
 * @param {object} config
 */
function validateTheme(ctx, config) {
  if (config.theme === undefined) return;
  if (!isInArray(VALID_THEMES, config.theme)) {
    ctx.warnings.push(`theme "${config.theme}" is not one of: ${VALID_THEMES.join(', ')}`);
  }
}

/**
 * @param {{errors:string[], warnings:string[]}} ctx
 * @param {object} config
 */
function validateRoutingMode(ctx, config) {
  if (config.routingMode === undefined) return;
  if (!isInArray(VALID_ROUTING_MODES, config.routingMode)) {
    ctx.errors.push(`routingMode "${config.routingMode}" is not one of: ${VALID_ROUTING_MODES.join(', ')}`);
  }
}

/**
 * @param {{errors:string[], warnings:string[]}} ctx
 * @param {object} config
 */
function validateDomain(ctx, config) {
  if (config.domain === undefined) return;
  if (typeof config.domain !== 'string') {
    ctx.errors.push('domain must be a string');
  } else if (config.domain && !/^[a-z0-9][a-z0-9.-]*\.[a-z]{2,}$/i.test(config.domain)) {
    ctx.warnings.push(`domain "${config.domain}" may not be a valid domain name`);
  }
}

/**
 * @param {{warnings:string[]}} ctx
 * @param {object} config
 */
function validateKnownKeys(ctx, config) {
  for (const key of Object.keys(config)) {
    if (!isInArray(KNOWN_KEYS, key)) {
      ctx.warnings.push(`Unknown config key "${key}" — possible typo?`);
    }
  }
}

/**
 * Validate a config object and return errors/warnings.
 * @param {object} config - The config object to validate
 * @returns {{ valid: boolean, errors: string[], warnings: string[] }}
 */
function validateConfig(config) {
  const errors = [];
  const warnings = [];
  const ctx = { errors, warnings };

  if (!config || typeof config !== 'object') {
    return { valid: false, errors: ['Config must be a non-null object'], warnings };
  }

  validateTld(ctx, config);
  validateDns(ctx, config);
  validateDashboardHost(ctx, config);
  validateTimezone(ctx, config);
  validateTheme(ctx, config);
  validateRoutingMode(ctx, config);
  validateDomain(ctx, config);
  validateKnownKeys(ctx, config);

  return { valid: errors.length === 0, errors, warnings };
}

module.exports = { validateConfig };
