const fs = require('fs');
const path = require('path');
const os = require('os');

// =============================================================================
// UUID PERSISTENCE FIX - Atomic writes, backups, validation
// =============================================================================

// Default auth domain - can be overridden by env var or config
const DEFAULT_AUTH_DOMAIN = 'auth.sanasol.ws';

// Get auth domain from env, config, or default
function getAuthDomain() {
  // First check environment variable
  if (process.env.HYTALE_AUTH_DOMAIN) {
    return process.env.HYTALE_AUTH_DOMAIN;
  }
  // Then check config file
  const config = loadConfig();
  if (config.activeProfileId && config.profiles && config.profiles[config.activeProfileId]) {
    // Allow profile to override auth domain if ever needed
    // but for now stick to global or env
  }
  if (config.authDomain) {
    return config.authDomain;
  }
  // Fall back to default
  return DEFAULT_AUTH_DOMAIN;
}

// Get full auth server URL
// Domain already includes subdomain (auth.sanasol.ws), so use directly
function getAuthServerUrl() {
  const domain = getAuthDomain();
  return `https://${domain}`;
}

// Save auth domain to config
function saveAuthDomain(domain) {
  saveConfig({ authDomain: domain || DEFAULT_AUTH_DOMAIN });
}

function getAppDir() {
  const home = os.homedir();
  if (process.platform === 'win32') {
    return path.join(home, 'AppData', 'Local', 'HytaleF2P');
  } else if (process.platform === 'darwin') {
    return path.join(home, 'Library', 'Application Support', 'HytaleF2P');
  } else {
    return path.join(home, '.hytalef2p');
  }
}

const CONFIG_FILE = path.join(getAppDir(), 'config.json');
const CONFIG_BACKUP = path.join(getAppDir(), 'config.json.bak');
const CONFIG_TEMP = path.join(getAppDir(), 'config.json.tmp');
const UUID_STORE_FILE = path.join(getAppDir(), 'uuid-store.json');

// =============================================================================
// CONFIG VALIDATION
// =============================================================================

/**
 * Validate config structure - ensures critical data is intact
 */
function validateConfig(config) {
  if (!config || typeof config !== 'object') {
    return false;
  }
  // If userUuids exists, it must be an object
  if (config.userUuids !== undefined && typeof config.userUuids !== 'object') {
    return false;
  }
  // If username exists, it must be a non-empty string
  if (config.username !== undefined && (typeof config.username !== 'string')) {
    return false;
  }
  return true;
}

// =============================================================================
// CONFIG LOADING - With backup recovery
// =============================================================================

/**
 * Load config with automatic backup recovery
 * Never returns empty object silently if data existed before
 */
function loadConfig() {
  // Try primary config first
  try {
    if (fs.existsSync(CONFIG_FILE)) {
      const data = fs.readFileSync(CONFIG_FILE, 'utf8');
      if (data.trim()) {
        const config = JSON.parse(data);
        if (validateConfig(config)) {
          return config;
        }
        console.warn('[Config] Primary config invalid structure, trying backup...');
      }
    }
  } catch (err) {
    console.error('[Config] Failed to load primary config:', err.message);
  }

  // Try backup config
  try {
    if (fs.existsSync(CONFIG_BACKUP)) {
      const data = fs.readFileSync(CONFIG_BACKUP, 'utf8');
      if (data.trim()) {
        const config = JSON.parse(data);
        if (validateConfig(config)) {
          console.log('[Config] Recovered from backup successfully');
          // Restore primary from backup
          try {
            fs.writeFileSync(CONFIG_FILE, data, 'utf8');
            console.log('[Config] Primary config restored from backup');
          } catch (restoreErr) {
            console.error('[Config] Failed to restore primary from backup:', restoreErr.message);
          }
          return config;
        }
      }
    }
  } catch (err) {
    console.error('[Config] Failed to load backup config:', err.message);
  }

  // No valid config - return empty (fresh install)
  console.log('[Config] No valid config found - fresh install');
  return {};
}

// =============================================================================
// CONFIG SAVING - Atomic writes with backup
// =============================================================================

/**
 * Save config atomically with backup
 * Uses temp file + rename pattern to prevent corruption
 * Creates backup before overwriting
 */
function saveConfig(update) {
  const maxRetries = 3;
  let lastError;

  for (let attempt = 1; attempt <= maxRetries; attempt++) {
    try {
      const configDir = path.dirname(CONFIG_FILE);
      if (!fs.existsSync(configDir)) {
        fs.mkdirSync(configDir, { recursive: true });
      }

      // Load current config
      const currentConfig = loadConfig();

      // SAFETY: If config file exists on disk but loadConfig() returned empty,
      // something is wrong (file locked, corrupted, etc.). Refuse to save
      // because merging with {} would wipe all existing data (userUuids, username, etc.)
      if (Object.keys(currentConfig).length === 0 && fs.existsSync(CONFIG_FILE)) {
        const fileSize = fs.statSync(CONFIG_FILE).size;
        if (fileSize > 2) { // More than just "{}"
          console.error(`[Config] REFUSING to save — loaded empty but file exists (${fileSize} bytes). Retrying load...`);
          // Wait and retry the load
          const delay = attempt * 200;
          const start = Date.now();
          while (Date.now() - start < delay) { /* busy wait */ }
          continue;
        }
      }

      const newConfig = { ...currentConfig, ...update };
      const data = JSON.stringify(newConfig, null, 2);

      // 1. Write to temp file first
      fs.writeFileSync(CONFIG_TEMP, data, 'utf8');

      // 2. Verify temp file is valid JSON
      const verification = JSON.parse(fs.readFileSync(CONFIG_TEMP, 'utf8'));
      if (!validateConfig(verification)) {
        throw new Error('Config validation failed after write');
      }

      // 3. Backup current config (if exists and valid)
      if (fs.existsSync(CONFIG_FILE)) {
        try {
          const currentData = fs.readFileSync(CONFIG_FILE, 'utf8');
          if (currentData.trim()) {
            fs.writeFileSync(CONFIG_BACKUP, currentData, 'utf8');
          }
        } catch (backupErr) {
          console.warn('[Config] Could not create backup:', backupErr.message);
          // Continue anyway - saving new config is more important
        }
      }

      // 4. Atomic rename (this is the critical operation)
      fs.renameSync(CONFIG_TEMP, CONFIG_FILE);

      return true;
    } catch (err) {
      lastError = err;
      console.error(`[Config] Save attempt ${attempt}/${maxRetries} failed:`, err.message);

      // Clean up temp file on failure
      try {
        if (fs.existsSync(CONFIG_TEMP)) {
          fs.unlinkSync(CONFIG_TEMP);
        }
      } catch (cleanupErr) {
        // Ignore cleanup errors
      }

      if (attempt < maxRetries) {
        // Small delay before retry
        const delay = attempt * 100;
        const start = Date.now();
        while (Date.now() - start < delay) {
          // Busy wait (sync delay)
        }
      }
    }
  }

  // All retries failed - this is critical
  console.error('[Config] CRITICAL: Failed to save config after all retries:', lastError.message);
  throw new Error(`Failed to save config: ${lastError.message}`);
}

// =============================================================================
// USERNAME MANAGEMENT - No silent fallbacks
// =============================================================================

/**
 * Save username to config
 * When changing username, the UUID is preserved (rename, not new identity)
 * Validates username before saving
 */
function saveUsername(username) {
  if (!username || typeof username !== 'string') {
    throw new Error('Invalid username: must be a non-empty string');
  }
  const newName = username.trim();
  if (!newName) {
    throw new Error('Invalid username: cannot be empty or whitespace');
  }
  if (newName.length > 16) {
    throw new Error('Invalid username: must be 16 characters or less');
  }

  const config = loadConfig();
  const currentName = config.username ? config.username.trim() : null;
  const userUuids = config.userUuids || {};

  // Check if we're actually changing the username (case-insensitive comparison)
  const isRename = currentName && currentName.toLowerCase() !== newName.toLowerCase();

  // Also update UUID store (source of truth)
  migrateUuidStoreIfNeeded();
  const uuidStore = loadUuidStore();

  if (isRename) {
    // Find the UUID for the current username
    const currentKey = Object.keys(userUuids).find(
      k => k.toLowerCase() === currentName.toLowerCase()
    );
    const currentStoreKey = Object.keys(uuidStore).find(
      k => k.toLowerCase() === currentName.toLowerCase()
    );

    if (currentKey && userUuids[currentKey]) {
      // Check if target username already exists (would be a different identity)
      const targetKey = Object.keys(userUuids).find(
        k => k.toLowerCase() === newName.toLowerCase()
      );

      if (targetKey) {
        // Target username already exists - this is switching identity, not renaming
        console.log(`[Config] Switching to existing identity: "${newName}" (UUID already exists)`);
      } else {
        // Rename: move UUID from old name to new name
        const uuid = userUuids[currentKey];
        delete userUuids[currentKey];
        userUuids[newName] = uuid;
        // Same in UUID store
        if (currentStoreKey) delete uuidStore[currentStoreKey];
        uuidStore[newName] = uuid;
        console.log(`[Config] Renamed identity: "${currentKey}" → "${newName}" (UUID preserved: ${uuid})`);
      }
    }
  } else if (currentName && currentName !== newName) {
    // Case change only - update the key to preserve the new casing
    const currentKey = Object.keys(userUuids).find(
      k => k.toLowerCase() === currentName.toLowerCase()
    );
    if (currentKey && currentKey !== newName) {
      const uuid = userUuids[currentKey];
      delete userUuids[currentKey];
      userUuids[newName] = uuid;
      // Same in UUID store
      const storeKey = Object.keys(uuidStore).find(k => k.toLowerCase() === currentName.toLowerCase());
      if (storeKey) {
        delete uuidStore[storeKey];
        uuidStore[newName] = uuid;
      }
      console.log(`[Config] Updated username case: "${currentKey}" → "${newName}"`);
    }
  }

  // Save UUID store
  saveUuidStore(uuidStore);

  // Save both username and updated userUuids to config
  saveConfig({ username: newName, userUuids });
  console.log(`[Config] Username saved: "${newName}"`);
  return newName;
}

/**
 * Load username from config
 * Returns null if no username set (caller must handle)
 */
function loadUsername() {
  const config = loadConfig();
  const username = config.username;
  if (username && typeof username === 'string' && username.trim()) {
    return username.trim();
  }
  return null; // No username set - caller must handle this
}

/**
 * Load username with fallback to 'Player'
 * Use this only for display purposes, NOT for UUID lookup
 */
function loadUsernameWithDefault() {
  return loadUsername() || 'Player';
}

/**
 * Check if username is configured
 */
function hasUsername() {
  return loadUsername() !== null;
}

// =============================================================================
// UUID MANAGEMENT - Persistent and safe
// Uses separate uuid-store.json as source of truth (survives config.json corruption)
// =============================================================================

/**
 * Normalize username for UUID lookup (case-insensitive, trimmed)
 */
function normalizeUsername(username) {
  if (!username || typeof username !== 'string') return null;
  return username.trim().toLowerCase();
}

/**
 * Load UUID store from separate file (independent of config.json)
 */
function loadUuidStore() {
  try {
    if (fs.existsSync(UUID_STORE_FILE)) {
      const data = fs.readFileSync(UUID_STORE_FILE, 'utf8');
      if (data.trim()) {
        return JSON.parse(data);
      }
    }
  } catch (err) {
    console.error('[UUID Store] Failed to load:', err.message);
  }
  return {};
}

/**
 * Save UUID store to separate file (atomic write)
 */
function saveUuidStore(store) {
  try {
    const dir = path.dirname(UUID_STORE_FILE);
    if (!fs.existsSync(dir)) fs.mkdirSync(dir, { recursive: true });
    const tmpFile = UUID_STORE_FILE + '.tmp';
    fs.writeFileSync(tmpFile, JSON.stringify(store, null, 2), 'utf8');
    fs.renameSync(tmpFile, UUID_STORE_FILE);
  } catch (err) {
    console.error('[UUID Store] Failed to save:', err.message);
  }
}

/**
 * One-time migration: copy userUuids from config.json to uuid-store.json
 */
function migrateUuidStoreIfNeeded() {
  if (fs.existsSync(UUID_STORE_FILE)) return; // Already migrated
  const config = loadConfig();
  if (config.userUuids && Object.keys(config.userUuids).length > 0) {
    console.log('[UUID Store] Migrating', Object.keys(config.userUuids).length, 'UUIDs from config.json');
    saveUuidStore(config.userUuids);
  }
}

/**
 * Get UUID for a username
 * Source of truth: uuid-store.json (separate from config.json)
 * Also writes to config.json for backward compatibility
 * Creates new UUID only if user doesn't exist in EITHER store
 */
function getUuidForUser(username) {
  const { v4: uuidv4 } = require('uuid');

  if (!username || typeof username !== 'string' || !username.trim()) {
    throw new Error('Cannot get UUID: username is required');
  }

  const displayName = username.trim();
  const normalizedLookup = displayName.toLowerCase();

  // Ensure UUID store exists (one-time migration from config.json)
  migrateUuidStoreIfNeeded();

  // 1. Check UUID store first (source of truth)
  const uuidStore = loadUuidStore();
  const storeKey = Object.keys(uuidStore).find(k => k.toLowerCase() === normalizedLookup);

  if (storeKey) {
    const existingUuid = uuidStore[storeKey];

    // Update case if needed
    if (storeKey !== displayName) {
      console.log(`[UUID Store] Updating username case: "${storeKey}" → "${displayName}"`);
      delete uuidStore[storeKey];
      uuidStore[displayName] = existingUuid;
      saveUuidStore(uuidStore);
    }

    // Sync to config.json (backward compat, non-critical)
    try {
      const config = loadConfig();
      const configUuids = config.userUuids || {};
      const configKey = Object.keys(configUuids).find(k => k.toLowerCase() === normalizedLookup);
      if (!configKey || configUuids[configKey] !== existingUuid) {
        if (configKey) delete configUuids[configKey];
        configUuids[displayName] = existingUuid;
        saveConfig({ userUuids: configUuids });
      }
    } catch (e) {
      // Non-critical — UUID store is the source of truth
    }

    console.log(`[UUID] ${displayName} → ${existingUuid} (from uuid-store)`);
    return existingUuid;
  }

  // 2. Fallback: check config.json (recovery if uuid-store.json was lost)
  const config = loadConfig();
  const userUuids = config.userUuids || {};
  const configKey = Object.keys(userUuids).find(k => k.toLowerCase() === normalizedLookup);

  if (configKey) {
    const recoveredUuid = userUuids[configKey];
    console.warn(`[UUID] RECOVERED "${displayName}" → ${recoveredUuid} from config.json (uuid-store was missing)`);

    // Save to UUID store
    uuidStore[displayName] = recoveredUuid;
    saveUuidStore(uuidStore);

    return recoveredUuid;
  }

  // 3. New user — generate UUID, save to BOTH stores
  const newUuid = uuidv4();
  console.log(`[UUID] NEW user "${displayName}" → ${newUuid}`);

  // Save to UUID store (source of truth)
  uuidStore[displayName] = newUuid;
  saveUuidStore(uuidStore);

  // Save to config.json (backward compat)
  userUuids[displayName] = newUuid;
  saveConfig({ userUuids });

  return newUuid;
}

/**
 * Get current user's UUID (based on saved username)
 */
function getCurrentUuid() {
  const username = loadUsername();
  if (!username) {
    throw new Error('Cannot get current UUID: no username configured');
  }
  return getUuidForUser(username);
}

/**
 * Get all UUID mappings (raw object)
 */
function getAllUuidMappings() {
  migrateUuidStoreIfNeeded();
  const uuidStore = loadUuidStore();
  // Fallback to config if uuid-store is empty
  if (Object.keys(uuidStore).length === 0) {
    const config = loadConfig();
    return config.userUuids || {};
  }
  return uuidStore;
}

/**
 * Get all UUID mappings as array with current user flag
 */
function getAllUuidMappingsArray() {
  const allMappings = getAllUuidMappings();
  const currentUsername = loadUsername();
  const normalizedCurrent = currentUsername ? currentUsername.toLowerCase() : null;

  return Object.entries(allMappings).map(([username, uuid]) => ({
    username,
    uuid,
    isCurrent: username.toLowerCase() === normalizedCurrent
  }));
}

/**
 * Set UUID for a specific user
 * Validates UUID format before saving
 * Preserves original case of username
 */
function setUuidForUser(username, uuid, { force = false } = {}) {
  const { validate: validateUuid } = require('uuid');

  if (!username || typeof username !== 'string' || !username.trim()) {
    throw new Error('Invalid username');
  }

  if (!validateUuid(uuid)) {
    throw new Error('Invalid UUID format');
  }

  const displayName = username.trim();
  const normalizedLookup = displayName.toLowerCase();

  // 1. Check for existing entries — reject overwrite unless forced
  migrateUuidStoreIfNeeded();
  const uuidStore = loadUuidStore();
  const storeKey = Object.keys(uuidStore).find(k => k.toLowerCase() === normalizedLookup);
  if (storeKey && uuidStore[storeKey] !== uuid && !force) {
    console.log(`[Config] Rejected UUID overwrite for "${displayName}": existing ${uuidStore[storeKey]}, attempted ${uuid}`);
    return { success: false, error: 'duplicate', existingUuid: uuidStore[storeKey] };
  }
  // Check if UUID already used by a different name
  if (!force) {
    const existingByUuid = Object.entries(uuidStore).find(([k, v]) => v.toLowerCase() === uuid.toLowerCase() && k.toLowerCase() !== normalizedLookup);
    if (existingByUuid) {
      console.log(`[Config] Rejected duplicate UUID for "${displayName}": UUID ${uuid} already used by "${existingByUuid[0]}"`);
      return { success: false, error: 'uuid_in_use', existingUsername: existingByUuid[0] };
    }
  }

  // 2. Update UUID store (source of truth)
  if (storeKey) delete uuidStore[storeKey];
  uuidStore[displayName] = uuid;
  saveUuidStore(uuidStore);

  // 3. Update config.json (backward compat)
  const config = loadConfig();
  const userUuids = config.userUuids || {};
  const existingKey = Object.keys(userUuids).find(k => k.toLowerCase() === normalizedLookup);
  if (existingKey) delete userUuids[existingKey];
  userUuids[displayName] = uuid;
  saveConfig({ userUuids });

  console.log(`[Config] UUID set for "${displayName}": ${uuid}`);
  return { success: true, uuid };
}

/**
 * Generate a new UUID (without saving)
 */
function generateNewUuid() {
  const { v4: uuidv4 } = require('uuid');
  return uuidv4();
}

/**
 * Delete UUID for a specific user
 * Uses case-insensitive lookup
 */
function deleteUuidForUser(username) {
  if (!username || typeof username !== 'string') {
    throw new Error('Invalid username');
  }

  const normalizedLookup = username.trim().toLowerCase();
  let deleted = false;

  // 1. Delete from UUID store (source of truth)
  migrateUuidStoreIfNeeded();
  const uuidStore = loadUuidStore();
  const storeKey = Object.keys(uuidStore).find(k => k.toLowerCase() === normalizedLookup);
  if (storeKey) {
    delete uuidStore[storeKey];
    saveUuidStore(uuidStore);
    deleted = true;
  }

  // 2. Delete from config.json (backward compat)
  const config = loadConfig();
  const userUuids = config.userUuids || {};
  const existingKey = Object.keys(userUuids).find(k => k.toLowerCase() === normalizedLookup);
  if (existingKey) {
    delete userUuids[existingKey];
    saveConfig({ userUuids });
    deleted = true;
  }

  if (deleted) console.log(`[Config] UUID deleted for "${username}"`);
  return deleted;
}

/**
 * Reset current user's UUID (generates new one)
 */
function resetCurrentUserUuid() {
  const username = loadUsername();
  if (!username) {
    throw new Error('Cannot reset UUID: no username configured');
  }

  const { v4: uuidv4 } = require('uuid');
  const newUuid = uuidv4();

  return setUuidForUser(username, newUuid, { force: true });
}

// =============================================================================
// JAVA PATH MANAGEMENT
// =============================================================================

function saveJavaPath(javaPath) {
  const trimmed = (javaPath || '').trim();
  saveConfig({ javaPath: trimmed });
}

function loadJavaPath() {
  const config = loadConfig();

  // Prefer Active Profile's Java Path
  if (config.activeProfileId && config.profiles && config.profiles[config.activeProfileId]) {
    const profile = config.profiles[config.activeProfileId];
    if (profile.javaPath && profile.javaPath.trim().length > 0) {
      return profile.javaPath;
    }
  }

  // Fallback to global setting
  return config.javaPath || '';
}

// =============================================================================
// INSTALL PATH MANAGEMENT
// =============================================================================

function saveInstallPath(installPath) {
  const trimmed = (installPath || '').trim();
  saveConfig({ installPath: trimmed });
}

function loadInstallPath() {
  const config = loadConfig();
  return config.installPath || '';
}

// =============================================================================
// DISCORD RPC SETTINGS
// =============================================================================

function saveDiscordRPC(enabled) {
  saveConfig({ discordRPC: !!enabled });
}

function loadDiscordRPC() {
  const config = loadConfig();
  return config.discordRPC !== undefined ? config.discordRPC : true;
}

// =============================================================================
// LANGUAGE SETTINGS
// =============================================================================

function saveLanguage(language) {
  saveConfig({ language: language || 'en' });
}

function loadLanguage() {
  const config = loadConfig();
  return config.language || 'en';
}

// =============================================================================
// LAUNCHER SETTINGS
// =============================================================================

function saveCloseLauncherOnStart(enabled) {
  saveConfig({ closeLauncherOnStart: !!enabled });
}

function loadCloseLauncherOnStart() {
  const config = loadConfig();
  return config.closeLauncherOnStart !== undefined ? config.closeLauncherOnStart : false;
}

function saveLauncherHardwareAcceleration(enabled) {
  saveConfig({ launcherHardwareAcceleration: !!enabled });
}

function loadLauncherHardwareAcceleration() {
  const config = loadConfig();
  return config.launcherHardwareAcceleration !== undefined ? config.launcherHardwareAcceleration : true;
}

function saveAllowMultiInstance(enabled) {
  saveConfig({ allowMultiInstance: !!enabled });
}

function loadAllowMultiInstance() {
  const config = loadConfig();
  return config.allowMultiInstance !== undefined ? config.allowMultiInstance : false;
}

// =============================================================================
// MODS MANAGEMENT
// =============================================================================

function saveModsToConfig(mods) {
  try {
    const config = loadConfig();

    if (config.activeProfileId && config.profiles && config.profiles[config.activeProfileId]) {
      config.profiles[config.activeProfileId].mods = mods;
    } else {
      config.installedMods = mods;
    }

    // Use atomic save
    const configDir = path.dirname(CONFIG_FILE);
    if (!fs.existsSync(configDir)) {
      fs.mkdirSync(configDir, { recursive: true });
    }

    // Write atomically
    const data = JSON.stringify(config, null, 2);
    fs.writeFileSync(CONFIG_TEMP, data, 'utf8');
    if (fs.existsSync(CONFIG_FILE)) {
      fs.copyFileSync(CONFIG_FILE, CONFIG_BACKUP);
    }
    fs.renameSync(CONFIG_TEMP, CONFIG_FILE);

    console.log('[Config] Mods saved successfully');
  } catch (error) {
    console.error('[Config] Error saving mods:', error);
    throw error;
  }
}

function loadModsFromConfig() {
  try {
    const config = loadConfig();

    if (config.activeProfileId && config.profiles && config.profiles[config.activeProfileId]) {
      return config.profiles[config.activeProfileId].mods || [];
    }

    return config.installedMods || [];
  } catch (error) {
    console.error('[Config] Error loading mods:', error);
    return [];
  }
}

// =============================================================================
// FIRST LAUNCH DETECTION - FIXED
// =============================================================================

/**
 * Check if this is the first launch
 * FIXED: Was always returning true due to bug
 */
function isFirstLaunch() {
  const config = loadConfig();

  // If explicitly marked, use that
  if ('hasLaunchedBefore' in config) {
    return !config.hasLaunchedBefore;
  }

  // Check for any existing user data
  const hasUserData = config.installPath || config.username || config.javaPath ||
    config.chatUsername || config.userUuids ||
    Object.keys(config).length > 0;

  if (!hasUserData) {
    return true;
  }

  // FIXED: Was returning true here, should be false
  return false;
}

function markAsLaunched() {
  saveConfig({ hasLaunchedBefore: true, firstLaunchDate: new Date().toISOString() });
}

// =============================================================================
// GPU PREFERENCE
// =============================================================================

function saveGpuPreference(gpuPreference) {
  saveConfig({ gpuPreference: gpuPreference || 'auto' });
}

function loadGpuPreference() {
  const config = loadConfig();
  return config.gpuPreference || 'auto';
}

// =============================================================================
// VERSION MANAGEMENT
// =============================================================================

function saveVersionClient(versionClient) {
  saveConfig({ version_client: versionClient });
}

function loadVersionClient() {
  const config = loadConfig();
  return config.version_client !== undefined ? config.version_client : null;
}

function saveVersionBranch(versionBranch) {
  const branch = versionBranch || 'release';
  if (branch !== 'release' && branch !== 'pre-release') {
    console.warn(`[Config] Invalid branch "${branch}", defaulting to "release"`);
    saveConfig({ version_branch: 'release' });
  } else {
    saveConfig({ version_branch: branch });
  }
}

function loadVersionBranch() {
  const config = loadConfig();
  return config.version_branch || 'release';
}

// =============================================================================
// READY STATE - For UI to check before allowing launch
// =============================================================================

/**
 * Check if launcher is ready to launch game
 * Returns object with ready state and any issues
 */
function checkLaunchReady() {
  const issues = [];

  const username = loadUsername();
  if (!username) {
    issues.push('No username configured');
  } else if (username === 'Player') {
    issues.push('Using default username "Player"');
  }

  return {
    ready: issues.length === 0,
    hasUsername: !!username,
    username: username,
    issues: issues
  };
}

// =============================================================================
// JAVA WRAPPER CONFIGURATION (Structured)
// =============================================================================

const DEFAULT_WRAPPER_CONFIG = {
  stripFlags: [],
  injectArgs: [
    { arg: '--disable-sentry', condition: 'server' }
  ]
};

function getDefaultWrapperConfig() {
  return JSON.parse(JSON.stringify(DEFAULT_WRAPPER_CONFIG));
}

function loadWrapperConfig() {
  const config = loadConfig();
  if (config.javaWrapperConfig && typeof config.javaWrapperConfig === 'object') {
    const wc = config.javaWrapperConfig;
    if (Array.isArray(wc.stripFlags) && Array.isArray(wc.injectArgs)) {
      const loaded = JSON.parse(JSON.stringify(wc));
      // Normalize entries: ensure every injectArg has a valid condition
      for (const entry of loaded.injectArgs) {
        if (!['server', 'always'].includes(entry.condition)) {
          entry.condition = 'always';
        }
      }
      return loaded;
    }
  }
  return getDefaultWrapperConfig();
}

function saveWrapperConfig(wrapperConfig) {
  if (!wrapperConfig || typeof wrapperConfig !== 'object') {
    throw new Error('Invalid wrapper config');
  }
  if (!Array.isArray(wrapperConfig.stripFlags) || !Array.isArray(wrapperConfig.injectArgs)) {
    throw new Error('Invalid wrapper config structure');
  }
  // Validate injectArgs entries
  for (const entry of wrapperConfig.injectArgs) {
    if (!entry.arg || typeof entry.arg !== 'string') {
      throw new Error('Each inject arg must have a string "arg" property');
    }
    if (!['server', 'always'].includes(entry.condition)) {
      throw new Error('Inject arg condition must be "server" or "always"');
    }
  }
  saveConfig({ javaWrapperConfig: wrapperConfig });
  console.log('[Config] Wrapper config saved');
}

function resetWrapperConfig() {
  const config = loadConfig();
  delete config.javaWrapperConfig;
  delete config.javaWrapperScripts; // Clean up legacy key if present

  // Write the cleaned config using the same atomic pattern as saveConfig.
  // We cannot use saveConfig() here because it merges (spread) which cannot remove keys.
  const data = JSON.stringify(config, null, 2);
  fs.writeFileSync(CONFIG_TEMP, data, 'utf8');
  if (fs.existsSync(CONFIG_FILE)) {
    fs.copyFileSync(CONFIG_FILE, CONFIG_BACKUP);
  }
  fs.renameSync(CONFIG_TEMP, CONFIG_FILE);
  console.log('[Config] Wrapper config reset to default');
  return getDefaultWrapperConfig();
}

/**
 * Generate a platform-specific wrapper script from structured config
 * @param {Object} config - { stripFlags: string[], injectArgs: {arg, condition}[] }
 * @param {string} platform - 'darwin', 'win32', or 'linux'
 * @param {string|null} javaBin - Path to real java binary (required for darwin/linux)
 * @returns {string} Generated script content
 */
function generateWrapperScript(config, platform, javaBin) {
  const { stripFlags, injectArgs } = config;
  const alwaysArgs = injectArgs.filter(a => a.condition === 'always');
  const serverArgs = injectArgs.filter(a => a.condition === 'server');

  if (platform === 'win32') {
    return _generateWindowsWrapper(stripFlags, alwaysArgs, serverArgs);
  } else {
    return _generateUnixWrapper(stripFlags, alwaysArgs, serverArgs, javaBin);
  }
}

function _generateUnixWrapper(stripFlags, alwaysArgs, serverArgs, javaBin) {
  const lines = [
    '#!/bin/bash',
    '# Java wrapper - generated by HytaleF2P launcher',
    `REAL_JAVA="${javaBin || '${JAVA_BIN}'}"`,
    'ARGS=("$@")',
    ''
  ];

  // Strip flags
  if (stripFlags.length > 0) {
    lines.push('# Strip JVM flags');
    lines.push('FILTERED_ARGS=()');
    lines.push('for arg in "${ARGS[@]}"; do');
    lines.push('  case "$arg" in');
    for (const flag of stripFlags) {
      lines.push(`    "${flag}") echo "[Wrapper] Stripped: $arg" ;;`);
    }
    lines.push('    *) FILTERED_ARGS+=("$arg") ;;');
    lines.push('  esac');
    lines.push('done');
  } else {
    lines.push('FILTERED_ARGS=("${ARGS[@]}")');
  }
  lines.push('');

  // Always-inject args
  if (alwaysArgs.length > 0) {
    lines.push('# Inject args (always)');
    for (const a of alwaysArgs) {
      lines.push(`FILTERED_ARGS+=("${a.arg}")`);
      lines.push(`echo "[Wrapper] Injected ${a.arg}"`);
    }
    lines.push('');
  }

  // Server-conditional args (appended after HytaleServer.jar if present)
  if (serverArgs.length > 0) {
    lines.push('# Inject args (server only)');
    lines.push('IS_SERVER=false');
    lines.push('for arg in "${FILTERED_ARGS[@]}"; do');
    lines.push('  if [[ "$arg" == *"HytaleServer.jar"* ]]; then');
    lines.push('    IS_SERVER=true');
    lines.push('    break');
    lines.push('  fi');
    lines.push('done');
    lines.push('if [ "$IS_SERVER" = true ]; then');
    for (const a of serverArgs) {
      lines.push(`  FILTERED_ARGS+=("${a.arg}")`);
      lines.push(`  echo "[Wrapper] Injected ${a.arg}"`);
    }
    lines.push('fi');
    lines.push('');
  }

  lines.push('echo "[Wrapper] Executing: $REAL_JAVA ${FILTERED_ARGS[*]}"');
  lines.push('exec "$REAL_JAVA" "${FILTERED_ARGS[@]}"');
  lines.push('');

  return lines.join('\n');
}

function _generateWindowsWrapper(stripFlags, alwaysArgs, serverArgs) {
  const lines = [
    '@echo off',
    'setlocal EnableDelayedExpansion',
    '',
    'REM Java wrapper - generated by HytaleF2P launcher',
    'set "REAL_JAVA=%~dp0java-original.exe"',
    'set "ARGS=%*"',
    ''
  ];

  // Strip flags using string replacement
  if (stripFlags.length > 0) {
    lines.push('REM Strip JVM flags');
    for (const flag of stripFlags) {
      lines.push(`set "ARGS=!ARGS:${flag}=!"`);
    }
    lines.push('');
  }

  // Always-inject args
  const alwaysExtra = alwaysArgs.map(a => a.arg).join(' ');

  // Server-conditional args
  if (serverArgs.length > 0) {
    const serverExtra = serverArgs.map(a => a.arg).join(' ');
    lines.push('REM Check if running HytaleServer.jar and inject server args');
    lines.push('echo !ARGS! | findstr /i "HytaleServer.jar" >nul 2>&1');
    lines.push('if "!ERRORLEVEL!"=="0" (');
    if (alwaysExtra) {
      lines.push(`    echo [Wrapper] Injected ${alwaysExtra} ${serverExtra}`);
      lines.push(`    "%REAL_JAVA%" !ARGS! ${alwaysExtra} ${serverExtra}`);
    } else {
      lines.push(`    echo [Wrapper] Injected ${serverExtra}`);
      lines.push(`    "%REAL_JAVA%" !ARGS! ${serverExtra}`);
    }
    lines.push(') else (');
    if (alwaysExtra) {
      lines.push(`    "%REAL_JAVA%" !ARGS! ${alwaysExtra}`);
    } else {
      lines.push('    "%REAL_JAVA%" !ARGS!');
    }
    lines.push(')');
  } else if (alwaysExtra) {
    lines.push(`"%REAL_JAVA%" !ARGS! ${alwaysExtra}`);
  } else {
    lines.push('"%REAL_JAVA%" !ARGS!');
  }

  lines.push('exit /b !ERRORLEVEL!');
  lines.push('');

  return lines.join('\r\n');
}

// =============================================================================
// MATCHA SOCIAL AUTH
// =============================================================================

function saveMatchaToken(token) {
  saveConfig({ matchaToken: token || null });
}

function loadMatchaToken() {
  const config = loadConfig();
  return config.matchaToken || null;
}

function saveMatchaHandle(handle) {
  saveConfig({ matchaHandle: handle || null });
}

function loadMatchaHandle() {
  const config = loadConfig();
  return config.matchaHandle || null;
}

function saveMatchaUserId(id) {
  saveConfig({ matchaUserId: id || null });
}

function loadMatchaUserId() {
  const config = loadConfig();
  return config.matchaUserId || null;
}

function clearMatchaAuth() {
  const config = loadConfig();
  delete config.matchaToken;
  delete config.matchaUserId;
  delete config.matchaHandle;
  const data = JSON.stringify(config, null, 2);
  fs.writeFileSync(CONFIG_TEMP, data, 'utf8');
  if (fs.existsSync(CONFIG_FILE)) fs.copyFileSync(CONFIG_FILE, CONFIG_BACKUP);
  fs.renameSync(CONFIG_TEMP, CONFIG_FILE);
}

// =============================================================================
// EXPORTS
// =============================================================================

module.exports = {
  // Core config
  loadConfig,
  saveConfig,
  validateConfig,

  // Username (no silent fallbacks)
  saveUsername,
  loadUsername,
  loadUsernameWithDefault,
  hasUsername,

  // UUID management
  getUuidForUser,
  getCurrentUuid,
  getAllUuidMappings,
  getAllUuidMappingsArray,
  setUuidForUser,
  generateNewUuid,
  deleteUuidForUser,
  resetCurrentUserUuid,

  // Java/Install paths
  saveJavaPath,
  loadJavaPath,
  saveInstallPath,
  loadInstallPath,

  // Settings
  saveDiscordRPC,
  loadDiscordRPC,
  saveLanguage,
  loadLanguage,
  saveCloseLauncherOnStart,
  loadCloseLauncherOnStart,
  saveLauncherHardwareAcceleration,
  loadLauncherHardwareAcceleration,
  saveAllowMultiInstance,
  loadAllowMultiInstance,

  // Mods
  saveModsToConfig,
  loadModsFromConfig,

  // Launch state
  isFirstLaunch,
  markAsLaunched,
  checkLaunchReady,

  // Auth server
  getAuthServerUrl,
  getAuthDomain,
  saveAuthDomain,

  // GPU
  saveGpuPreference,
  loadGpuPreference,

  // Version
  saveVersionClient,
  loadVersionClient,
  saveVersionBranch,
  loadVersionBranch,

  // Java Wrapper Config
  getDefaultWrapperConfig,
  loadWrapperConfig,
  saveWrapperConfig,
  resetWrapperConfig,
  generateWrapperScript,

  // Matcha Social
  saveMatchaToken,
  loadMatchaToken,
  saveMatchaHandle,
  loadMatchaHandle,
  saveMatchaUserId,
  loadMatchaUserId,
  clearMatchaAuth,

  // Constants
  CONFIG_FILE,
  UUID_STORE_FILE
};
