/**
 * Compile routes — /api/compile, /api/compile/autofix, /api/vl-test, /api/validate
 */
import fs from 'fs';
import path from 'path';
import { execSync } from 'child_process';
import { PARSEVL_URL } from '../../data/versions.js';
import { getCookie } from '../helpers.js';

/** Package VL project files as base64 ZIP data URL */
function packageProjectFiles(workDir) {
  const zipPath = path.join(workDir, '__vl_compile.zip');
  try {
    execSync(
      `cd "${workDir}" && find . -type f \\( -name "*.vx" -o -name "*.sc" -o -name "*.cp" -o -name "*.vs" -o -name "*.vdb" -o -name "*.vth" \\) | zip -@ "${zipPath}"`,
      { timeout: 30000 }
    );
    const buf = fs.readFileSync(zipPath);
    return `data:application/zip;base64,${buf.toString('base64')}`;
  } finally {
    try { fs.unlinkSync(zipPath); } catch {}
  }
}

export function buildCompileRequest(workDir, { targetGid, action, download } = {}) {
  const body = {
    action: action || 'parsePjt',
    file: packageProjectFiles(workDir),
  };

  if (targetGid && !isNaN(Number(targetGid))) {
    body.targetGid = Number(targetGid);
  }
  if (download !== false) body.download = true;

  return {
    parseUrl: `${PARSEVL_URL}/edtfn/parsevl`,
    body,
  };
}

export function buildCompileDoneEvent(result, { success, errors } = {}) {
  const analysis = analyzeCompileResult(result);
  const normalizedErrors = Array.isArray(errors) ? errors : analysis.errList;
  return {
    type: 'compile_done',
    success: success ?? analysis.success,
    gid: analysis.gid,
    previewUrls: analysis.previewUrls,
    errCount: normalizedErrors.length,
    errors: normalizedErrors,
  };
}

function splitCompileDiagnostics(result) {
  const diagnostics = Array.isArray(result?.data?.errList)
    ? result.data.errList
    : Array.isArray(result?.errors)
      ? result.errors
      : [];
  const warnings = diagnostics.filter(item => String(item?.level || '').toLowerCase() === 'warning');
  const hardErrors = diagnostics.filter(item => String(item?.level || '').toLowerCase() !== 'warning');
  return { diagnostics, warnings, hardErrors };
}

function extractPreviewUrls(result) {
  if (result?.data?.appPreviewUrlMap && typeof result.data.appPreviewUrlMap === 'object') {
    return result.data.appPreviewUrlMap;
  }
  if (result?.data?.apps?.frontends && typeof result.data.apps.frontends === 'object') {
    return Object.fromEntries(
      Object.entries(result.data.apps.frontends).map(([appFile, info]) => [appFile, info?.previewUrl]).filter(([, url]) => !!url)
    );
  }
  return {};
}

export function analyzeCompileResult(result, { compilationStatus, action } = {}) {
  const { diagnostics, warnings, hardErrors } = splitCompileDiagnostics(result);
  const apiSucceeded = compilationStatus?.status === 'success' ||
    result?.code === 0 ||
    result?.code === 200 ||
    result?.success === true;

  return {
    success: !!apiSucceeded && hardErrors.length === 0,
    apiSucceeded: !!apiSucceeded,
    gid: result?.data?.gid || null,
    errCount: hardErrors.length,
    warningCount: warnings.length,
    errList: hardErrors,
    warningList: warnings,
    diagnostics,
    previewUrls: extractPreviewUrls(result),
    message: result?.message || '',
    action: action || 'parsePjt',
    compilerStatus: compilationStatus?.status || null,
  };
}

export function persistLastCompileReport(workDir, result, meta = {}) {
  const report = {
    timestamp: new Date().toISOString(),
    ...analyzeCompileResult(result, meta),
    rawMessage: result?.message || '',
  };
  const vlDir = path.join(workDir, '.vl-code');
  fs.mkdirSync(vlDir, { recursive: true });
  fs.writeFileSync(path.join(vlDir, 'last-compile.json'), JSON.stringify(report, null, 2), 'utf-8');
  return report;
}

export function setupCompileRoutes(app, server) {
  // --- Validate ---
  app.post('/api/validate', async (req, res) => {
    try {
      if (!server.hasSelectedWorkspace()) return res.json({ error: 'No workspace selected' });
      if (req.body?.file_path === 'all') {
        await server.projectContext.scan();
        if (server.symbolIndex) await server.symbolIndex.build().catch(() => {});
      }
      const result = await server.toolRegistry.execute('VLValidate', req.body);
      res.json(result);
    } catch (e) {
      res.json({ error: e.message });
    }
  });

  // --- Compile (parsevl) ---
  app.post('/api/compile', async (req, res) => {
    res.writeHead(200, { 'Content-Type': 'text/event-stream', 'Cache-Control': 'no-cache', 'Connection': 'keep-alive' });
    const send = (event, data) => res.write(`event: ${event}\ndata: ${JSON.stringify(data)}\n\n`);

    try {
      if (!server.hasSelectedWorkspace()) {
        send('error', { message: 'No workspace selected' });
        res.end();
        return;
      }
      const { targetGid, action, download } = req.body;

      const cookie = getCookie(server.config);
      if (!cookie) { send('error', { message: 'Not logged in. Please login first.' }); res.end(); return; }

      const hasGid = targetGid && !isNaN(Number(targetGid));
      send('status', {
        status: 'compiling',
        message: hasGid
          ? `Packaging current VL files and compiling into project ${targetGid}...`
          : 'Packaging current VL files and compiling...',
      });

      let body;
      let parseUrl;
      try {
        const request = buildCompileRequest(server.config.workDir, { targetGid, action, download });
        body = request.body;
        parseUrl = server.config.vlParseApiUrl || request.parseUrl;
      } catch (zipErr) {
        send('error', { message: `Failed to package project files: ${zipErr.message}` });
        res.end(); return;
      }

      const resp = await fetch(parseUrl, {
        method: 'POST',
        headers: { 'Content-Type': 'application/json', 'Cookie': `ih5bearer=${cookie}` },
        body: JSON.stringify(body),
      });

      if (resp.status === 401) {
        send('error', { message: 'Auth expired. Please re-login.' });
        res.end(); return;
      }

      let data;
      const respText = await resp.text();
      try {
        data = JSON.parse(respText);
      } catch {
        send('error', { message: `Lambda returned non-JSON (HTTP ${resp.status}): ${respText.substring(0, 200)}` });
        res.end(); return;
      }
      server._lastCompileResult = data;

      // True success: API call succeeded AND no compile errors reported
      const analysis = analyzeCompileResult(data, { action });
      const apiOk = analysis.apiSucceeded;
      const hasErrors = analysis.errCount > 0 || (data.message && data.message.toLowerCase().includes('fail'));
      if (apiOk && !hasErrors) {
        // Save returned gid and previewUrls only on real build success
        const returnedGid = analysis.gid;
        if (returnedGid) {
          try {
            const profilePath = path.join(server.config.workDir, '.vl-code', 'project.json');
            const vlDir = path.dirname(profilePath);
            if (!fs.existsSync(vlDir)) fs.mkdirSync(vlDir, { recursive: true });
            let profile = {};
            try { profile = JSON.parse(fs.readFileSync(profilePath, 'utf-8')); } catch {}
            profile.groupId = returnedGid;
            if (Object.keys(analysis.previewUrls).length > 0) profile.previewUrls = analysis.previewUrls;
            fs.writeFileSync(profilePath, JSON.stringify(profile, null, 2));
          } catch {}
        }
        // Normalize response: expose appPreviewUrlMap as previewUrls for VLCompile tool
        const normalized = Object.assign({}, data);
        if (data.data?.appPreviewUrlMap && !data.data?.apps?.frontends) {
          normalized.data = Object.assign({}, data.data, {
            apps: { frontends: Object.fromEntries(
              Object.entries(data.data.appPreviewUrlMap).map(([k, v]) => [k, { previewUrl: v }])
            ) },
          });
        }
        persistLastCompileReport(server.config.workDir, normalized, { action, compilationStatus: { status: 'success' } });
        send('status', { status: 'success', message: 'Compilation successful' });
        send('result', normalized);
        server.broadcast(buildCompileDoneEvent(normalized, { success: true }));
      } else if (apiOk && hasErrors) {
        // API reached but compile reported errors — send result for inspection but not success
        persistLastCompileReport(server.config.workDir, data, { action, compilationStatus: { status: 'error' } });
        send('status', { status: 'error', message: `Compilation errors (${analysis.errCount || '?'})` });
        send('result', data);
        server.broadcast(buildCompileDoneEvent(data, { success: false, errors: analysis.errList }));
      } else {
        persistLastCompileReport(server.config.workDir, data, { action, compilationStatus: { status: 'error' } });
        send('status', { status: 'error', message: data.message || 'Compilation failed' });
        send('result', data);
        server.broadcast(buildCompileDoneEvent(data, { success: false, errors: analysis.errList }));
      }
    } catch (e) {
      send('error', { message: e.message });
    }
    res.end();
  });

  // --- Compile autofix (SSE) ---
  app.post('/api/compile/autofix', async (req, res) => {
    res.writeHead(200, { 'Content-Type': 'text/event-stream', 'Cache-Control': 'no-cache', 'Connection': 'keep-alive' });
    const send = (event, data) => res.write(`event: ${event}\ndata: ${JSON.stringify(data)}\n\n`);

    try {
      const { errors, maxRounds } = req.body;
      if (!errors?.length) { send('error', { message: 'No errors to fix' }); res.end(); return; }

      const session = server.getSession(req);
      const errorList = errors.map(e => `- ${e.path || ''}:${e.lineNumber || ''} ${e.message || e}`).join('\n');
      const prompt = `Fix the following VL compilation errors:\n${errorList}\n\nRead the relevant files, fix the issues, and write the corrected files.`;

      send('status', { message: 'Starting auto-fix...', errorCount: errors.length });

      await session.orchestrator.processUserMessageStreaming(prompt, {
        onText: (text) => send('text', { text }),
        onToolCall: (name, input) => send('tool_call', { name, input: JSON.stringify(input).substring(0, 200) }),
        onToolResult: (name, result) => send('tool_result', { name, preview: String(result).substring(0, 300) }),
        onDone: () => send('done', { message: 'Auto-fix complete' }),
        onError: (err) => send('error', { message: err }),
      });
    } catch (e) {
      send('error', { message: e.message });
    }
    res.end();
  });

  // --- Last compile errors ---
  app.get('/api/compile/errors', (req, res) => {
    res.json(server._lastCompileResult || { errors: [] });
  });

  // --- Browser testing ---
  app.post('/api/browser/screenshot', async (req, res) => {
    try {
      const result = await server.toolRegistry.execute('BrowserInspect', { action: 'screenshot', ...req.body });
      res.json(result);
    } catch (e) { res.json({ error: e.message }); }
  });

  app.post('/api/browser/console', async (req, res) => {
    try {
      const result = await server.toolRegistry.execute('BrowserInspect', { action: 'console', ...req.body });
      res.json(result);
    } catch (e) { res.json({ error: e.message }); }
  });

  app.post('/api/browser/evaluate', async (req, res) => {
    try {
      const result = await server.toolRegistry.execute('BrowserInspect', { action: 'evaluate', ...req.body });
      res.json(result);
    } catch (e) { res.json({ error: e.message }); }
  });

  app.get('/api/browser/screenshot/:name', (req, res) => {
    const p = path.join(server.config.workDir, '.vl-code', 'screenshots', req.params.name);
    if (fs.existsSync(p)) res.sendFile(p);
    else res.status(404).json({ error: 'Not found' });
  });

  // --- VL Component Test ---
  app.post('/api/vl-test', async (req, res) => {
    try {
      if (!server.hasSelectedWorkspace()) return res.json({ error: 'No workspace selected' });
      const result = await server.toolRegistry.execute('VLComponentTest', req.body);
      res.json(result);
    } catch (e) { res.json({ error: e.message }); }
  });

  // --- VL Syntax Reference ---
  app.post('/api/vl-syntax', async (req, res) => {
    try {
      if (!server.hasSelectedWorkspace()) return res.json({ error: 'No workspace selected' });
      const result = await server.toolRegistry.execute('VLSyntaxRef', req.body);
      res.json(result);
    } catch (e) { res.json({ error: e.message }); }
  });
}
