/**
 * Server helpers — cookie loading, VL.md generation, project profile initialization
 */
import fs from 'fs';
import path from 'path';
import os from 'os';
import { getDefaultCoreDocIds, getDefaultDocIdOverrides, normalizeCoreDocIds, normalizeDocIdOverrides } from '../data/doc-paths.js';
import { VL_VERSION, VL_VERSION_HEADER } from '../data/versions.js';
import { DEFAULT_THEME_CONTENT } from '../data/default-theme.js';

export const VL_CODE_HOME = path.join(os.homedir(), '.vl-code');

export function ensureVLCodeHome() {
  if (!fs.existsSync(VL_CODE_HOME)) fs.mkdirSync(VL_CODE_HOME, { recursive: true });
}

// --- Cookie management ---

export function getCookie(config) {
  if (config.cookie) return config.cookie;
  // Global auth.json
  try {
    const globalAuth = path.join(VL_CODE_HOME, 'auth.json');
    if (fs.existsSync(globalAuth)) {
      const auth = JSON.parse(fs.readFileSync(globalAuth, 'utf-8'));
      if (auth.cookie) { config.cookie = auth.cookie; return auth.cookie; }
    }
  } catch {}
  // ~/.claude/vl-cookie.txt
  try {
    const cookieFile = path.join(os.homedir(), '.claude', 'vl-cookie.txt');
    if (fs.existsSync(cookieFile)) {
      const cookie = fs.readFileSync(cookieFile, 'utf-8').trim();
      if (cookie) { config.cookie = cookie; return cookie; }
    }
  } catch {}
  // ~/Documents/docs/auth/.doccenter_cookie
  try {
    const dcCookie = path.join(os.homedir(), 'Documents', 'docs', 'auth', '.doccenter_cookie');
    if (fs.existsSync(dcCookie)) {
      const cookie = fs.readFileSync(dcCookie, 'utf-8').trim();
      if (cookie) { config.cookie = cookie; return cookie; }
    }
  } catch {}
  return null;
}

export function clearCookie(config) {
  config.cookie = '';
  try {
    const profilePath = path.join(config.workDir, '.vl-code', 'project.json');
    if (fs.existsSync(profilePath)) {
      const profile = JSON.parse(fs.readFileSync(profilePath, 'utf-8'));
      if (profile.cookie) { delete profile.cookie; fs.writeFileSync(profilePath, JSON.stringify(profile, null, 2), 'utf-8'); }
    }
  } catch {}
  try {
    const globalAuth = path.join(VL_CODE_HOME, 'auth.json');
    if (fs.existsSync(globalAuth)) {
      const auth = JSON.parse(fs.readFileSync(globalAuth, 'utf-8'));
      if (auth.cookie) { delete auth.cookie; fs.writeFileSync(globalAuth, JSON.stringify(auth, null, 2), 'utf-8'); }
    }
  } catch {}
  try {
    const cookieFile = path.join(os.homedir(), '.claude', 'vl-cookie.txt');
    if (fs.existsSync(cookieFile)) fs.writeFileSync(cookieFile, '', 'utf-8');
  } catch {}
}

export function saveCookie(config, cookie) {
  config.cookie = cookie;
  // Save to global auth.json
  ensureVLCodeHome();
  const globalAuth = path.join(VL_CODE_HOME, 'auth.json');
  try {
    let auth = {};
    if (fs.existsSync(globalAuth)) auth = JSON.parse(fs.readFileSync(globalAuth, 'utf-8'));
    auth.cookie = cookie;
    auth.updatedAt = new Date().toISOString();
    fs.writeFileSync(globalAuth, JSON.stringify(auth, null, 2), 'utf-8');
  } catch {}
  // Save to project profile
  try {
    const profilePath = path.join(config.workDir, '.vl-code', 'project.json');
    if (fs.existsSync(profilePath)) {
      const profile = JSON.parse(fs.readFileSync(profilePath, 'utf-8'));
      profile.cookie = cookie;
      fs.writeFileSync(profilePath, JSON.stringify(profile, null, 2), 'utf-8');
    }
  } catch {}
}

// --- Instance registry (multi-window support) ---

const INSTANCES_FILE = path.join(VL_CODE_HOME, 'instances.json');

function _isProcessRunning(pid) {
  try { process.kill(pid, 0); return true; } catch { return false; }
}

export function loadInstances() {
  try {
    if (fs.existsSync(INSTANCES_FILE)) return JSON.parse(fs.readFileSync(INSTANCES_FILE, 'utf-8'));
  } catch {}
  return [];
}

export function cleanInstances() {
  ensureVLCodeHome();
  const live = loadInstances().filter(i => _isProcessRunning(i.pid));
  try { fs.writeFileSync(INSTANCES_FILE, JSON.stringify(live, null, 2)); } catch {}
  return live;
}

export function registerInstance(port, workDir, pid) {
  ensureVLCodeHome();
  const list = cleanInstances().filter(i => i.port !== port && i.workDir !== workDir);
  list.push({ port, workDir, pid, startedAt: new Date().toISOString() });
  fs.writeFileSync(INSTANCES_FILE, JSON.stringify(list, null, 2));
}

export function deregisterInstance(port) {
  try {
    const list = loadInstances().filter(i => i.port !== port);
    fs.writeFileSync(INSTANCES_FILE, JSON.stringify(list, null, 2));
  } catch {}
}

export async function findFreePort(startPort = 4000, maxPort = 4099) {
  const net = (await import('net')).default;
  const usedPorts = new Set(cleanInstances().map(i => i.port));
  for (let port = startPort; port <= maxPort; port++) {
    if (usedPorts.has(port)) continue;
    const free = await new Promise(resolve => {
      const s = net.createServer();
      s.listen({ port, exclusive: true }, () => s.close(() => resolve(true)));
      s.on('error', () => resolve(false));
    });
    if (free) return port;
  }
  throw new Error(`No free ports in range ${startPort}-${maxPort}`);
}

// --- Workspace management ---

const WORKSPACES_FILE = path.join(VL_CODE_HOME, 'workspaces.json');

export function loadWorkspaces() {
  ensureVLCodeHome();
  try { return JSON.parse(fs.readFileSync(WORKSPACES_FILE, 'utf-8')); }
  catch { return []; }
}

export function saveWorkspaces(list) {
  ensureVLCodeHome();
  fs.writeFileSync(WORKSPACES_FILE, JSON.stringify(list, null, 2), 'utf-8');
}

// --- VL Bible ---

export function ensureVLBible(dataDir) {
  const globalBible = path.join(VL_CODE_HOME, 'vl-syntax.md');
  const bundledBible = path.join(dataDir, 'vl-syntax.md');
  if (!fs.existsSync(bundledBible)) return;
  if (!fs.existsSync(globalBible)) {
    fs.copyFileSync(bundledBible, globalBible);
    return;
  }
  try {
    const bundledFirst = fs.readFileSync(bundledBible, 'utf-8').split('\n')[0];
    const globalFirst = fs.readFileSync(globalBible, 'utf-8').split('\n')[0];
    const bundledVer = bundledFirst.match(/VL_SYNTAX_VERSION:([\d.]+)/)?.[1];
    const globalVer = globalFirst.match(/VL_SYNTAX_VERSION:([\d.]+)/)?.[1];
    if (bundledVer && (!globalVer || bundledVer > globalVer)) {
      fs.copyFileSync(bundledBible, globalBible);
    }
  } catch {}
}

// --- Project profile ---

export function hasVLFiles(workDir) {
  const vlDirs = ['Apps', 'Sections', 'ExtComponents', 'Services', 'Database', 'Theme'];
  if (vlDirs.some(d => fs.existsSync(path.join(workDir, d)))) return true;
  try {
    const entries = fs.readdirSync(workDir);
    if (entries.some(f => f.endsWith('.vx') || f.endsWith('.sc') || f.endsWith('.cp') || f.endsWith('.vs') || f.endsWith('.vdb'))) return true;
  } catch {}
  return false;
}

function readJsonFileSafe(filePath) {
  try {
    return JSON.parse(fs.readFileSync(filePath, 'utf-8'));
  } catch {
    return null;
  }
}

export function getProjectProfilePath(workDir) {
  return path.join(workDir, '.vl-code', 'project.json');
}

export function readProjectProfile(workDir) {
  if (!workDir) return null;
  return readJsonFileSafe(getProjectProfilePath(workDir));
}

export function writeProjectProfile(workDir, profile) {
  if (!workDir) return null;
  const profilePath = getProjectProfilePath(workDir);
  fs.mkdirSync(path.dirname(profilePath), { recursive: true });
  fs.writeFileSync(profilePath, JSON.stringify(profile, null, 2), 'utf-8');
  return profile;
}

export function mergeProjectProfile(workDir, partial = {}) {
  const current = readProjectProfile(workDir) || {};
  const next = { ...current, ...partial };
  if (Object.prototype.hasOwnProperty.call(partial, 'coreDocIds')) {
    next.coreDocIds = normalizeCoreDocIds(partial.coreDocIds, { includeDefaults: true });
  }
  if (Object.prototype.hasOwnProperty.call(partial, 'docIdOverrides')) {
    next.docIdOverrides = normalizeDocIdOverrides(partial.docIdOverrides, { includeDefaults: true });
  }
  return writeProjectProfile(workDir, next);
}

export function applyProjectProfileToConfig(config) {
  const profile = readProjectProfile(config?.workDir) || {};
  config.coreDocIds = normalizeCoreDocIds(profile.coreDocIds, { includeDefaults: true });
  config.docIdOverrides = normalizeDocIdOverrides(profile.docIdOverrides, { includeDefaults: true });
  if (!config.cookie && profile.cookie) config.cookie = profile.cookie;
  return profile;
}

function hasWorkflowVar(workflow, varName) {
  return Array.isArray(workflow?.registry?.vars)
    && workflow.registry.vars.some((entry) => String(entry).includes(varName));
}

function isLegacyAddPageWorkflow(workflow) {
  if (!workflow || workflow.name !== 'AddPage') return false;
  const docs = workflow?.registry?.docs || {};
  const updateAppStep = Array.isArray(workflow.steps)
    ? workflow.steps.find((step) => step?.id === 'LLM_050_UpdateApp')
    : null;
  const hasLegacyDocMap = ['60', '70', '80', '90'].some((key) => Object.prototype.hasOwnProperty.call(docs, key));
  const hasTargetApp = hasWorkflowVar(workflow, '$targetApp');
  const hasTargetAppFilePath = hasWorkflowVar(workflow, '$targetAppFilePath');
  const hasReadFiles = Array.isArray(updateAppStep?.in?.readFiles) && updateAppStep.in.readFiles.length > 0;
  const hasAppOutput = updateAppStep?.out && Object.keys(updateAppStep.out).length > 0;
  return hasLegacyDocMap || !hasTargetApp || !hasTargetAppFilePath || !hasReadFiles || !hasAppOutput;
}

function shouldRefreshSeedWorkflow(fileName, existingWorkflow, seedWorkflow) {
  if (!existingWorkflow || !seedWorkflow || existingWorkflow.managed === false) return false;
  if (fileName === 'add-page.json') {
    return isLegacyAddPageWorkflow(existingWorkflow) && !isLegacyAddPageWorkflow(seedWorkflow);
  }
  return false;
}

export function ensureProjectProfile(workDir, seedWorkflowDir) {
  const vlDir = path.join(workDir, '.vl-code');
  if (!fs.existsSync(vlDir) && !hasVLFiles(workDir)) return;
  const wfDir = path.join(vlDir, 'workflows');
  if (!fs.existsSync(vlDir)) fs.mkdirSync(vlDir, { recursive: true });
  if (!fs.existsSync(wfDir)) fs.mkdirSync(wfDir, { recursive: true });

  // project.json
  const profilePath = path.join(vlDir, 'project.json');
  if (!fs.existsSync(profilePath)) {
    fs.writeFileSync(profilePath, JSON.stringify({
      projectName: path.basename(workDir),
      vlVersion: VL_VERSION,
      description: '',
      createdAt: new Date().toISOString(),
      activeWorkflow: null,
      coreDocIds: getDefaultCoreDocIds(),
      docIdOverrides: getDefaultDocIdOverrides(),
    }, null, 2), 'utf-8');
  }

  // conventions.json
  const convPath = path.join(vlDir, 'conventions.json');
  if (!fs.existsSync(convPath)) {
    fs.writeFileSync(convPath, JSON.stringify({
      naming: {
        files: 'PascalCase', variables: 'camelCase with $ prefix',
        methods: 'camelCase', events: 'camelCase with @ prefix',
        localVars: '_prefix', themeTokens: '--prefix',
      },
      indentation: 'dash (-) symbols, NOT spaces',
      version: `${VL_VERSION_HEADER} (required header)`,
    }, null, 2), 'utf-8');
  }

  // VL.md — project root (not .vl-code/)
  const vlMdPath = path.join(workDir, 'VL.md');
  if (!fs.existsSync(vlMdPath)) {
    fs.writeFileSync(vlMdPath, generateVLMd(path.basename(workDir)), 'utf-8');
  }

  // Default theme — Theme/Theme.vth (Theme 6.5 seed)
  const themeDir = path.join(workDir, 'Theme');
  const themePath = path.join(themeDir, 'Theme.vth');
  const hasExistingThemeFile = fs.existsSync(themeDir) &&
    fs.readdirSync(themeDir).some((name) => name.toLowerCase().endsWith('.vth'));
  if (!hasExistingThemeFile && !fs.existsSync(themePath)) {
    if (!fs.existsSync(themeDir)) fs.mkdirSync(themeDir, { recursive: true });
    let seeded = false;
    if (seedWorkflowDir) {
      const seedThemeDir = path.join(path.dirname(seedWorkflowDir), 'seed-theme');
      const preferredSeed = path.join(seedThemeDir, 'Theme.vth');
      const legacySeed = path.join(seedThemeDir, 'Enterprise.vth');
      const seedThemePath = fs.existsSync(preferredSeed) ? preferredSeed : legacySeed;
      if (fs.existsSync(seedThemePath)) {
        fs.copyFileSync(seedThemePath, themePath);
        seeded = true;
      }
    }
    if (!seeded) {
      fs.writeFileSync(themePath, DEFAULT_THEME_CONTENT, 'utf-8');
    }
  }

  // Seed workflows
  const wfFiles = fs.readdirSync(wfDir).filter(f => f.endsWith('.json'));
  if (seedWorkflowDir && fs.existsSync(seedWorkflowDir)) {
    if (wfFiles.length === 0) {
      for (const f of fs.readdirSync(seedWorkflowDir).filter(f => f.endsWith('.json'))) {
        try { fs.copyFileSync(path.join(seedWorkflowDir, f), path.join(wfDir, f)); } catch {}
      }
    } else {
      for (const f of fs.readdirSync(seedWorkflowDir).filter(f => f.endsWith('.json'))) {
        const seedPath = path.join(seedWorkflowDir, f);
        const targetPath = path.join(wfDir, f);
        if (!fs.existsSync(targetPath)) continue;
        const existingWorkflow = readJsonFileSafe(targetPath);
        const seedWorkflow = readJsonFileSafe(seedPath);
        if (shouldRefreshSeedWorkflow(f, existingWorkflow, seedWorkflow)) {
          try { fs.copyFileSync(seedPath, targetPath); } catch {}
        }
      }
    }
  }
}

function generateVLMd(projName) {
  return `# ${projName} — VL Project

## Project Description
${projName} — Application project based on VL (Visual Language) v${VL_VERSION}.

## VL Syntax Essentials
- Version Declaration: \`${VL_VERSION_HEADER}\` (required first line)
- File Types: .vx (App), .sc (Section), .cp (Component), .vs (Service), .vdb (Database), .vth (Theme)
- Indentation: consecutive hyphens (-), NOT spaces

## File Naming Conventions
- Theme file is always \`Theme/Theme.vth\` and uses the default Theme 6.5 seed unless the user explicitly asks for theme customization
- Database file is always \`Database/${projName}.vdb\`
- Service files: \`Services/<DomainId>.vs\`
- Component files: \`ExtComponents/<ComponentId>.cp\`
- Section files: \`Sections/<SectionId>.sc\`
- App files: \`Apps/<AppId>.vx\`

## Generation Workflow Rules
- After metadata/spec files are ready, .vs/.cp/.sc/.vx generation should be parallel and file-level decoupled
- Every metadata object must include its final \`filePath\`; downstream agents must not infer names ad hoc
- Do not create extra theme variants or use \`main.vdb\` / \`Theme-Default.vth\`

## Project Custom Instructions
[Add project-specific AI instructions here]
`;
}
