const fs = require('fs');
const express = require('express');
const path = require('path');
const { scanProjects } = require('./lib/scanner');
const pm = require('./lib/process-manager');
const { takeScreenshot } = require('./lib/screenshotter');
const centro = require('./lib/centrifugo');

const app = express();
const PORT = 3000;

// Scan projects on startup
let projects = scanProjects();
const projectsBySlug = new Map(projects.map(p => [p.slug, p]));

console.log(`Found ${projects.length} projects:`);
projects.forEach(p => {
  const fe = p.frontend ? ` + frontend(${p.frontend.dir} :${p.frontend.port} ${p.frontend.packageManager})` : '';
  console.log(`  ${p.slug} :${p.port} (${p.portSource || 'assigned'})${fe}`);
});

// Publish status changes to Centrifugo
pm.events.on('status', (slug, subtype, status) => {
  centro.publishStatus(slug, status, { subtype });
});

// Static files
app.use(express.static(path.join(__dirname, 'public')));
app.use(express.json());

// Serve project docs folders with directory listing
app.use('/docs/:slug', (req, res, next) => {
  const project = projectsBySlug.get(req.params.slug);
  if (!project) return res.status(404).send('Project not found');

  const docsRoot = path.join(project.path, 'docs');
  const reqPath = decodeURIComponent(req.path).replace(/\.\./g, '');
  const fullPath = path.join(docsRoot, reqPath);

  if (!fullPath.startsWith(docsRoot)) return res.status(403).send('Forbidden');

  let stat;
  try { stat = fs.statSync(fullPath); } catch { return res.status(404).send('Not found'); }

  if (stat.isFile()) return res.sendFile(fullPath);

  // Directory listing
  const entries = fs.readdirSync(fullPath, { withFileTypes: true });
  const basePath = `/docs/${project.slug}${reqPath}`.replace(/\/$/, '');
  const dirs = entries.filter(e => e.isDirectory()).sort((a, b) => a.name.localeCompare(b.name));
  const files = entries.filter(e => e.isFile()).sort((a, b) => a.name.localeCompare(b.name));

  const items = [
    ...dirs.map(e => `<li><a href="${basePath}/${e.name}/">${e.name}/</a></li>`),
    ...files.map(e => `<li><a href="${basePath}/${e.name}">${e.name}</a></li>`),
  ].join('\n');

  res.send(`<!DOCTYPE html><html><head><meta charset="utf-8">
    <title>docs/${project.slug}${reqPath}</title>
    <style>body{font-family:system-ui;background:#0f1117;color:#e4e6ed;padding:2rem}
    a{color:#6c8cff;text-decoration:none}a:hover{text-decoration:underline}
    li{margin:4px 0;font-size:15px}h2{font-weight:500;margin-bottom:1rem}</style>
    </head><body><h2>docs/${project.slug}${reqPath}/</h2>
    ${reqPath !== '/' && reqPath !== '' ? '<a href="../">&larr; back</a>' : ''}
    <ul>${items}</ul></body></html>`);
});

// Centrifugo connection token
app.get('/api/centrifugo/token', (req, res) => {
  const token = centro.createJWT('projects-browser');
  res.json({ token });
});

// List all projects with live status
app.get('/api/projects', (req, res) => {
  const statuses = pm.getAllStatuses();
  const result = projects.map(p => {
    const s = statuses[p.slug];
    const hasFrontend = !!p.frontend;
    return {
      ...p,
      status: s ? pm.combinedStatus(p.slug, hasFrontend) : 'stopped',
      apiStatus: s?.api?.status || 'stopped',
      frontendStatus: hasFrontend ? (s?.frontend?.status || 'stopped') : null,
      pid: s?.api?.pid || null,
    };
  });
  res.json(result);
});

// Start a project
app.post('/api/projects/:slug/start', (req, res) => {
  const project = projectsBySlug.get(req.params.slug);
  if (!project) return res.status(404).json({ error: 'Project not found' });

  const result = pm.start(project.slug, project.path, project.port, project.frontend || null);
  res.json(result);
});

// Stop a project
app.post('/api/projects/:slug/stop', async (req, res) => {
  const project = projectsBySlug.get(req.params.slug);
  if (!project) return res.status(404).json({ error: 'Project not found' });

  const result = await pm.stop(project.slug);
  res.json(result);
});

// Screenshot a project
app.post('/api/projects/:slug/screenshot', async (req, res) => {
  const project = projectsBySlug.get(req.params.slug);
  if (!project) return res.status(404).json({ error: 'Project not found' });

  const s = pm.getStatus(project.slug);
  const hasFrontend = !!project.frontend;

  // For frontend projects, prefer frontend being up; otherwise check API
  if (hasFrontend) {
    if (s.frontend?.status !== 'running') {
      return res.status(400).json({ error: 'Frontend must be running to take screenshot' });
    }
  } else {
    if (s.api.status !== 'running') {
      return res.status(400).json({ error: 'Project must be running to take screenshot' });
    }
  }

  // Screenshot the frontend port if available, otherwise API port
  const screenshotPort = hasFrontend ? project.frontend.port : project.port;

  try {
    const imgPath = await takeScreenshot(project.slug, screenshotPort, hasFrontend);
    centro.publishScreenshot(project.slug);
    res.json({ screenshot: imgPath });
  } catch (err) {
    res.status(500).json({ error: err.message });
  }
});

// Screenshot all running projects
app.post('/api/screenshot-all', async (req, res) => {
  const statuses = pm.getAllStatuses();
  const results = {};

  for (const p of projects) {
    const s = statuses[p.slug];
    if (!s) continue;

    const hasFrontend = !!p.frontend;
    const combined = pm.combinedStatus(p.slug, hasFrontend);
    if (combined !== 'running') continue;

    const screenshotPort = hasFrontend ? p.frontend.port : p.port;
    try {
      results[p.slug] = await takeScreenshot(p.slug, screenshotPort, hasFrontend);
      centro.publishScreenshot(p.slug);
    } catch (err) {
      results[p.slug] = { error: err.message };
    }
  }
  res.json(results);
});

// Get logs for a project
app.get('/api/projects/:slug/logs', (req, res) => {
  const project = projectsBySlug.get(req.params.slug);
  if (!project) return res.status(404).json({ error: 'Project not found' });

  const s = pm.getStatus(project.slug);
  res.json({
    api: s.api.logs,
    frontend: s.frontend?.logs || [],
  });
});

// Rescan projects
app.post('/api/rescan', (req, res) => {
  projects = scanProjects();
  projectsBySlug.clear();
  projects.forEach(p => projectsBySlug.set(p.slug, p));
  res.json({ count: projects.length });
});

// Graceful shutdown
let shuttingDown = false;
function shutdown() {
  if (shuttingDown) return;
  shuttingDown = true;
  console.log('\nShutting down, stopping all child processes...');
  pm.killAllSync();
  console.log('All processes killed.');
  process.exit(0);
}

process.on('SIGINT', shutdown);
process.on('SIGTERM', shutdown);

app.listen(PORT, '0.0.0.0', () => {
  console.log(`Projects Browser running at http://0.0.0.0:${PORT}`);
});
