import express from 'express';
import path from 'path';
import http from 'http';
import mime from 'mime-types';
import fs from 'fs';
import {
  scanPlugins,
  getPluginsConfig,
  getPluginsDir,
  savePluginsConfig,
  getPluginDir,
  resolvePluginAssetPath,
  installPluginFromGit,
  updatePluginFromGit,
  uninstallPlugin,
} from '../utils/plugin-loader.js';
import {
  startPluginServer,
  stopPluginServer,
  getPluginPort,
  isPluginRunning,
} from '../utils/plugin-process-manager.js';

const router = express.Router();

// GET / — List all installed plugins (includes server running status)
router.get('/', (req, res) => {
  try {
    const plugins = scanPlugins().map(p => ({
      ...p,
      serverRunning: p.server ? isPluginRunning(p.name) : false,
    }));
    res.json({ plugins });
  } catch (err) {
    res.status(500).json({ error: 'Failed to scan plugins', details: err.message });
  }
});

// GET /:name/manifest — Get single plugin manifest
router.get('/:name/manifest', (req, res) => {
  try {
    if (!/^[a-zA-Z0-9_-]+$/.test(req.params.name)) {
      return res.status(400).json({ error: 'Invalid plugin name' });
    }
    const plugins = scanPlugins();
    const plugin = plugins.find(p => p.name === req.params.name);
    if (!plugin) {
      return res.status(404).json({ error: 'Plugin not found' });
    }
    res.json(plugin);
  } catch (err) {
    res.status(500).json({ error: 'Failed to read plugin manifest', details: err.message });
  }
});

// GET /:name/assets/* — Serve plugin static files
router.get('/:name/assets/*', (req, res) => {
  const pluginName = req.params.name;
  if (!/^[a-zA-Z0-9_-]+$/.test(pluginName)) {
    return res.status(400).json({ error: 'Invalid plugin name' });
  }
  const assetPath = req.params[0];

  if (!assetPath) {
    return res.status(400).json({ error: 'No asset path specified' });
  }

  const resolvedPath = resolvePluginAssetPath(pluginName, assetPath);
  if (!resolvedPath) {
    return res.status(404).json({ error: 'Asset not found' });
  }

  try {
    const stat = fs.statSync(resolvedPath);
    if (!stat.isFile()) {
      return res.status(404).json({ error: 'Asset not found' });
    }
  } catch {
    return res.status(404).json({ error: 'Asset not found' });
  }

  const contentType = mime.lookup(resolvedPath) || 'application/octet-stream';
  res.setHeader('Content-Type', contentType);
  // Prevent CDN/proxy caching of plugin assets so updates take effect immediately
  res.setHeader('Cache-Control', 'no-store, no-cache, must-revalidate');
  res.setHeader('Pragma', 'no-cache');
  res.setHeader('Expires', '0');
  const stream = fs.createReadStream(resolvedPath);
  stream.on('error', () => {
    if (!res.headersSent) {
      res.status(500).json({ error: 'Failed to read asset' });
    } else {
      res.end();
    }
  });
  stream.pipe(res);
});

// PUT /:name/enable — Toggle plugin enabled/disabled (starts/stops server if applicable)
router.put('/:name/enable', async (req, res) => {
  try {
    const { enabled } = req.body;
    if (typeof enabled !== 'boolean') {
      return res.status(400).json({ error: '"enabled" must be a boolean' });
    }

    const plugins = scanPlugins();
    const plugin = plugins.find(p => p.name === req.params.name);
    if (!plugin) {
      return res.status(404).json({ error: 'Plugin not found' });
    }

    const config = getPluginsConfig();
    config[req.params.name] = { ...config[req.params.name], enabled };
    savePluginsConfig(config);

    // Start or stop the plugin server as needed
    if (plugin.server) {
      if (enabled && !isPluginRunning(plugin.name)) {
        const pluginDir = getPluginDir(plugin.name);
        if (pluginDir) {
          try {
            await startPluginServer(plugin.name, pluginDir, plugin.server);
          } catch (err) {
            console.error(`[Plugins] Failed to start server for "${plugin.name}":`, err.message);
          }
        }
      } else if (!enabled && isPluginRunning(plugin.name)) {
        await stopPluginServer(plugin.name);
      }
    }

    res.json({ success: true, name: req.params.name, enabled });
  } catch (err) {
    res.status(500).json({ error: 'Failed to update plugin', details: err.message });
  }
});

// POST /install — Install plugin from git URL
router.post('/install', async (req, res) => {
  try {
    const { url } = req.body;
    if (!url || typeof url !== 'string') {
      return res.status(400).json({ error: '"url" is required and must be a string' });
    }

    // Basic URL validation
    if (!url.startsWith('https://') && !url.startsWith('git@')) {
      return res.status(400).json({ error: 'URL must start with https:// or git@' });
    }

    const manifest = await installPluginFromGit(url);

    // Auto-start the server if the plugin has one (enabled by default)
    if (manifest.server) {
      const pluginDir = getPluginDir(manifest.name);
      if (pluginDir) {
        try {
          await startPluginServer(manifest.name, pluginDir, manifest.server);
        } catch (err) {
          console.error(`[Plugins] Failed to start server for "${manifest.name}":`, err.message);
        }
      }
    }

    res.json({ success: true, plugin: manifest });
  } catch (err) {
    res.status(400).json({ error: 'Failed to install plugin', details: err.message });
  }
});

// POST /:name/update — Pull latest from git (restarts server if running)
router.post('/:name/update', async (req, res) => {
  try {
    const pluginName = req.params.name;

    if (!/^[a-zA-Z0-9_-]+$/.test(pluginName)) {
      return res.status(400).json({ error: 'Invalid plugin name' });
    }

    const wasRunning = isPluginRunning(pluginName);
    if (wasRunning) {
      await stopPluginServer(pluginName);
    }

    const manifest = await updatePluginFromGit(pluginName);

    // Restart server if it was running before the update
    if (wasRunning && manifest.server) {
      const pluginDir = getPluginDir(pluginName);
      if (pluginDir) {
        try {
          await startPluginServer(pluginName, pluginDir, manifest.server);
        } catch (err) {
          console.error(`[Plugins] Failed to restart server for "${pluginName}":`, err.message);
        }
      }
    }

    res.json({ success: true, plugin: manifest });
  } catch (err) {
    res.status(400).json({ error: 'Failed to update plugin', details: err.message });
  }
});

// ALL /:name/rpc/* — Proxy requests to plugin's server subprocess
router.all('/:name/rpc/*', async (req, res) => {
  const pluginName = req.params.name;
  const rpcPath = req.params[0] || '';

  if (!/^[a-zA-Z0-9_-]+$/.test(pluginName)) {
    return res.status(400).json({ error: 'Invalid plugin name' });
  }

  let port = getPluginPort(pluginName);
  if (!port) {
    // Lazily start the plugin server if it exists and is enabled
    const plugins = scanPlugins();
    const plugin = plugins.find(p => p.name === pluginName);
    if (!plugin || !plugin.server) {
      return res.status(503).json({ error: 'Plugin server is not running' });
    }
    if (!plugin.enabled) {
      return res.status(503).json({ error: 'Plugin is disabled' });
    }
    const pluginDir = path.join(getPluginsDir(), plugin.dirName);
    try {
      port = await startPluginServer(pluginName, pluginDir, plugin.server);
    } catch (err) {
      return res.status(503).json({ error: 'Plugin server failed to start', details: err.message });
    }
  }

  // Inject configured secrets as headers
  const config = getPluginsConfig();
  const pluginConfig = config[pluginName] || {};
  const secrets = pluginConfig.secrets || {};

  const headers = {
    'content-type': req.headers['content-type'] || 'application/json',
  };

  // Add per-plugin user-configured secrets as X-Plugin-Secret-* headers
  for (const [key, value] of Object.entries(secrets)) {
    headers[`x-plugin-secret-${key.toLowerCase()}`] = String(value);
  }

  // Reconstruct query string
  const qs = req.url.includes('?') ? '?' + req.url.split('?').slice(1).join('?') : '';

  const options = {
    hostname: '127.0.0.1',
    port,
    path: `/${rpcPath}${qs}`,
    method: req.method,
    headers,
  };

  const proxyReq = http.request(options, (proxyRes) => {
    res.writeHead(proxyRes.statusCode, proxyRes.headers);
    proxyRes.pipe(res);
  });

  proxyReq.on('error', (err) => {
    if (!res.headersSent) {
      res.status(502).json({ error: 'Plugin server error', details: err.message });
    } else {
      res.end();
    }
  });

  // Forward body (already parsed by express JSON middleware, so re-stringify).
  // Check content-length to detect whether a body was actually sent, since
  // req.body can be falsy for valid payloads like 0, false, null, or {}.
  const hasBody = req.headers['content-length'] && parseInt(req.headers['content-length'], 10) > 0;
  if (hasBody && req.body !== undefined) {
    const bodyStr = JSON.stringify(req.body);
    proxyReq.setHeader('content-length', Buffer.byteLength(bodyStr));
    proxyReq.write(bodyStr);
  }

  proxyReq.end();
});

// DELETE /:name — Uninstall plugin (stops server first)
router.delete('/:name', async (req, res) => {
  try {
    const pluginName = req.params.name;

    // Validate name format to prevent path traversal
    if (!/^[a-zA-Z0-9_-]+$/.test(pluginName)) {
      return res.status(400).json({ error: 'Invalid plugin name' });
    }

    // Stop server and wait for the process to fully exit before deleting files
    if (isPluginRunning(pluginName)) {
      await stopPluginServer(pluginName);
    }

    await uninstallPlugin(pluginName);
    res.json({ success: true, name: pluginName });
  } catch (err) {
    res.status(400).json({ error: 'Failed to uninstall plugin', details: err.message });
  }
});

export default router;
