import { promises as fs } from 'fs';
import path from 'path';
import { readCodexMcpServers, formatMcpServersSection } from './codex-mcp.js';
import { initSystemRepoForUser } from './system-repo.js';
import { initSystemMcpRepoForUser } from './system-mcp-repo.js';

// Base data directory (configurable via env)
const DATA_DIR = process.env.DATA_DIR || path.join(process.cwd(), 'data');

// UUID validation regex to prevent path traversal attacks
const UUID_REGEX = /^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/i;

function validateUuid(userUuid) {
  if (!userUuid || !UUID_REGEX.test(userUuid)) {
    throw new Error('Invalid user UUID format');
  }
}

/**
 * Get paths for a user
 */
export function getUserPaths(userUuid) {
  validateUuid(userUuid);
  return {
    configDir: path.join(DATA_DIR, 'user-data', userUuid),
    claudeDir: path.join(DATA_DIR, 'user-data', userUuid, '.claude'),
    projectsDir: path.join(DATA_DIR, 'user-projects', userUuid),
    skillsDir: path.join(DATA_DIR, 'user-data', userUuid, '.claude', 'skills'),
    skillsImportDir: path.join(DATA_DIR, 'user-data', userUuid, 'skills-import'),
    skillsRepoDir: path.join(DATA_DIR, 'user-data', userUuid, 'skills-repo'),
    mcpRepoDir: path.join(DATA_DIR, 'user-data', userUuid, 'mcp-repo'),
    codexHomeDir: path.join(DATA_DIR, 'user-data', userUuid, 'codex-home'),
    geminiHomeDir: path.join(DATA_DIR, 'user-data', userUuid, 'gemini-home'),
  };
}

/**
 * Get public paths (shared across all users)
 */
export function getPublicPaths() {
  return {
    skillsRepoDir: path.join(DATA_DIR, 'skills-repo'),
    mcpRepoDir: path.join(DATA_DIR, 'mcp-repo'),
    agentRepoDir: path.join(DATA_DIR, 'agent-repo'),
    agentSubmissionsDir: path.join(DATA_DIR, 'agent-submissions'),
    gitRepoDir: path.join(DATA_DIR, 'git-repo'),
  };
}

/**
 * Initialize codex home directory and config files for a user.
 * Safe to call multiple times - only creates files that don't already exist.
 * Note: skillsDir must exist before calling this function (created in initUserDirectories).
 */
export async function initCodexDirectories(userUuid) {
  validateUuid(userUuid);
  const paths = getUserPaths(userUuid);
  const codexDir = path.join(paths.codexHomeDir, '.codex');

  await fs.mkdir(codexDir, { recursive: true });

  // Write config.toml (overwrite to keep in sync with env, but preserve mcp_servers)
  const baseUrl = process.env.OPENAI_BASE_URL || '';
  const configTomlPath = path.join(codexDir, 'config.toml');

  const existingMcpServers = await readCodexMcpServers(configTomlPath);
  let configToml = `model_provider = "custom"

[model_providers.custom]
name = "custom"
wire_api = "responses"
requires_openai_auth = true
base_url = "${baseUrl}"
`;
  if (Object.keys(existingMcpServers).length > 0) {
    configToml += '\n' + formatMcpServersSection(existingMcpServers) + '\n';
  }
  await fs.writeFile(configTomlPath, configToml, 'utf8');

  // Write auth.json only if it doesn't exist, to avoid overwriting user tokens
  const authJsonPath = path.join(codexDir, 'auth.json');
  try {
    await fs.access(authJsonPath);
    // File exists - skip to preserve any user-level overrides
  } catch {
    const apiKey = process.env.OPENAI_API_KEY || '';
    const authJson = JSON.stringify({ OPENAI_API_KEY: apiKey }, null, 2);
    await fs.writeFile(authJsonPath, authJson, 'utf8');
  }

  // Sync skills symlinks into .codex/skills/:
  // Mirror each enabled skill from .claude/skills/ into .codex/skills/<skill> (symlink).
  // The .codex/skills/.system/ directory (Codex built-in skills) is preserved unchanged.
  const codexSkillsDir = path.join(codexDir, 'skills');
  await fs.mkdir(codexSkillsDir, { recursive: true });

  try {
    // Add symlinks for newly enabled skills
    let claudeSkills = [];
    try {
      claudeSkills = await fs.readdir(paths.skillsDir);
    } catch {
      // skillsDir doesn't exist yet - nothing to sync
    }

    for (const skillName of claudeSkills) {
      const claudeSkillPath = path.join(paths.skillsDir, skillName);
      const codexSkillLink = path.join(codexSkillsDir, skillName);
      try {
        await fs.lstat(codexSkillLink);
        // Already exists - skip
      } catch {
        await fs.symlink(claudeSkillPath, codexSkillLink);
        console.log(`[Codex] Synced skill: ${skillName}`);
      }
    }

    // Remove stale symlinks (skills removed from .claude/skills/)
    const codexSkills = await fs.readdir(codexSkillsDir);
    for (const skillName of codexSkills) {
      if (skillName.startsWith('.')) continue; // preserve .system and other hidden dirs
      const codexSkillLink = path.join(codexSkillsDir, skillName);
      const claudeSkillPath = path.join(paths.skillsDir, skillName);
      try {
        await fs.access(claudeSkillPath);
      } catch {
        // No longer in .claude/skills/ - remove if it's a symlink
        try {
          const stat = await fs.lstat(codexSkillLink);
          if (stat.isSymbolicLink()) {
            await fs.unlink(codexSkillLink);
            console.log(`[Codex] Removed stale skill symlink: ${skillName}`);
          }
        } catch {}
      }
    }
  } catch (err) {
    console.log(`[Codex] Skills sync warning: ${err.message}`);
  }

  console.log(`[Codex] Initialized codex directories for user ${userUuid}`);
  return codexDir;
}

/**
 * Initialize gemini home directory and skills for a user.
 * Safe to call multiple times - only creates files that don't already exist.
 * Note: skillsDir must exist before calling this function (created in initUserDirectories).
 */
export async function initGeminiDirectories(userUuid) {
  validateUuid(userUuid);
  const paths = getUserPaths(userUuid);
  const geminiDir = path.join(paths.geminiHomeDir, '.gemini');

  await fs.mkdir(geminiDir, { recursive: true });

  // Write .env file (overwrite to keep in sync with server env vars)
  // Gemini CLI reads GEMINI_API_KEY and GOOGLE_GEMINI_BASE_URL from GEMINI_CLI_HOME/.gemini/.env
  const geminiApiKey = process.env.GEMINI_API_KEY || '';
  const geminiBaseUrl = process.env.GOOGLE_GEMINI_BASE_URL || '';
  let envContent = '';
  if (geminiApiKey) envContent += `GEMINI_API_KEY=${geminiApiKey}\n`;
  if (geminiBaseUrl) envContent += `GOOGLE_GEMINI_BASE_URL=${geminiBaseUrl}\n`;
  await fs.writeFile(path.join(geminiDir, '.env'), envContent, 'utf8');

  // Ensure settings.json exists and has selectedType set
  const settingsJsonPath = path.join(geminiDir, 'settings.json');
  let geminiSettings = {};
  try {
    const settingsContent = await fs.readFile(settingsJsonPath, 'utf8');
    geminiSettings = JSON.parse(settingsContent);
  } catch {
    // File doesn't exist or is invalid JSON, start fresh
  }
  if (!geminiSettings?.security?.auth?.selectedType) {
    geminiSettings = {
      ...geminiSettings,
      security: {
        ...(geminiSettings.security || {}),
        auth: {
          ...(geminiSettings.security?.auth || {}),
          selectedType: 'gemini-api-key'
        }
      }
    };
    await fs.writeFile(settingsJsonPath, JSON.stringify(geminiSettings, null, 2), 'utf8');
  }

  // Sync skills symlinks into .gemini/skills/:
  // Mirror each enabled skill from .claude/skills/ into .gemini/skills/<skill> (symlink).
  const geminiSkillsDir = path.join(geminiDir, 'skills');
  await fs.mkdir(geminiSkillsDir, { recursive: true });

  try {
    // Add symlinks for newly enabled skills
    let claudeSkills = [];
    try {
      claudeSkills = await fs.readdir(paths.skillsDir);
    } catch {
      // skillsDir doesn't exist yet - nothing to sync
    }

    for (const skillName of claudeSkills) {
      const claudeSkillPath = path.join(paths.skillsDir, skillName);
      const geminiSkillLink = path.join(geminiSkillsDir, skillName);
      try {
        await fs.lstat(geminiSkillLink);
        // Already exists - skip
      } catch {
        await fs.symlink(claudeSkillPath, geminiSkillLink);
        console.log(`[Gemini] Synced skill: ${skillName}`);
      }
    }

    // Remove stale symlinks (skills removed from .claude/skills/)
    const geminiSkills = await fs.readdir(geminiSkillsDir);
    for (const skillName of geminiSkills) {
      if (skillName.startsWith('.')) continue; // preserve hidden dirs
      const geminiSkillLink = path.join(geminiSkillsDir, skillName);
      const claudeSkillPath = path.join(paths.skillsDir, skillName);
      try {
        await fs.access(claudeSkillPath);
      } catch {
        // No longer in .claude/skills/ - remove if it's a symlink
        try {
          const stat = await fs.lstat(geminiSkillLink);
          if (stat.isSymbolicLink()) {
            await fs.unlink(geminiSkillLink);
            console.log(`[Gemini] Removed stale skill symlink: ${skillName}`);
          }
        } catch {}
      }
    }
  } catch (err) {
    console.log(`[Gemini] Skills sync warning: ${err.message}`);
  }

  console.log(`[Gemini] Initialized gemini directories for user ${userUuid}`);
  return geminiDir;
}

/**
 * Initialize directories for a new user
 */
export async function initUserDirectories(userUuid) {
  validateUuid(userUuid);
  const paths = getUserPaths(userUuid);

  // Create directories
  await fs.mkdir(paths.claudeDir, { recursive: true });
  await fs.mkdir(paths.projectsDir, { recursive: true });

  // Create projects directory for Claude session files
  const projectsDir = path.join(paths.claudeDir, 'projects');
  await fs.mkdir(projectsDir, { recursive: true });

  // Create skills directories (must be before initCodexDirectories to allow skills symlink)
  await fs.mkdir(paths.skillsDir, { recursive: true });
  await fs.mkdir(paths.skillsImportDir, { recursive: true });
  await fs.mkdir(paths.skillsRepoDir, { recursive: true });

  // Create mcp-repo directory for the user
  await fs.mkdir(paths.mcpRepoDir, { recursive: true });
  // Ensure shared public directories exist
  const publicPaths = getPublicPaths();
  await fs.mkdir(publicPaths.mcpRepoDir, { recursive: true });
  await fs.mkdir(publicPaths.agentRepoDir, { recursive: true });
  await fs.mkdir(publicPaths.agentSubmissionsDir, { recursive: true });

  // Initialize codex home directory with config files (after skillsDir exists for symlink)
  await initCodexDirectories(userUuid);

  // Initialize gemini home directory with skills symlinks
  await initGeminiDirectories(userUuid);

  // Create .claude.json with hasCompletedOnboarding=true
  const claudeJsonPath = path.join(paths.claudeDir, '.claude.json');
  const claudeConfig = {
    hasCompletedOnboarding: true
  };
  await fs.writeFile(claudeJsonPath, JSON.stringify(claudeConfig, null, 2));
  console.log(`Created .claude.json for user ${userUuid}`);

  // Create settings.json with proxy configuration
  const destSettings = path.join(paths.claudeDir, 'settings.json');
  const settingsConfig = {
    env: {
      ANTHROPIC_AUTH_TOKEN: process.env.ANTHROPIC_AUTH_TOKEN || '',
      ANTHROPIC_BASE_URL: process.env.ANTHROPIC_BASE_URL || ''
    }
  };
  await fs.writeFile(destSettings, JSON.stringify(settingsConfig, null, 2));
  console.log(`Created settings.json for user ${userUuid}`);

  // Create usage scan state file (tracks last scanned position)
  const usageScanStatePath = path.join(paths.claudeDir, '.usage-scan-state.json');
  const scanState = {
    lastScanTime: null,
    scannedSessions: {}
  };
  await fs.writeFile(usageScanStatePath, JSON.stringify(scanState, null, 2));
  console.log(`Created .usage-scan-state.json for user ${userUuid}`);

  // Initialize system repo (built-in skill repository)
  await initSystemRepoForUser(userUuid);

  // Initialize system MCP repo (built-in MCP repository)
  await initSystemMcpRepoForUser(userUuid);

  return paths;
}

/**
 * Delete all directories for a user
 */
export async function deleteUserDirectories(userUuid) {
  validateUuid(userUuid);
  const paths = getUserPaths(userUuid);

  try {
    await fs.rm(paths.configDir, { recursive: true, force: true });
    await fs.rm(paths.projectsDir, { recursive: true, force: true });
    console.log(`Deleted directories for user ${userUuid}`);
  } catch (error) {
    console.error(`Error deleting directories for user ${userUuid}:`, error);
    throw error;
  }
}

/**
 * Check if user directories exist
 */
export async function userDirectoriesExist(userUuid) {
  validateUuid(userUuid);
  const paths = getUserPaths(userUuid);
  try {
    await fs.access(paths.configDir);
    await fs.access(paths.projectsDir);
    return true;
  } catch {
    return false;
  }
}

export { DATA_DIR };
