const express = require('express');
const http = require('http');
const { ok, errorResponse, notFound, conflict } = require('../src/utils/responses');

/**
 * OpenClaw management routes
 * Proxies gateway API calls through DashCaddy so the token never leaves the server.
 *
 * GET  /openclaw/status          → container info + gateway health
 * POST /openclaw/deploy          → deploy OpenClaw container
 * GET  /openclaw/proxy/*         → proxy GET to gateway
 * POST /openclaw/proxy/*        → proxy POST to gateway
 * DELETE /openclaw               → remove container
 */
module.exports = function openClawRoutes(ctx) {
  const router = express.Router();
  const docker = ctx.docker;
  const asyncHandler = ctx.asyncHandler;
  const ok = ctx.ok;
  const log = ctx.log || console;

  // ── helpers ──────────────────────────────────────────────────────────────

  async function findOpenClawContainer() {
    const containers = await docker.client.listContainers({ all: true });
    return containers.find(function(c) {
      return c.Image === 'ghcr.io/nousresearch/openclaw:latest' ||
        (c.Labels && c.Labels['dashcaddy.managed'] === 'true' &&
         c.Names.some(function(n) { return n.includes('openclaw'); }));
    }) || null;
  }

  async function getGatewayToken(containerId) {
    try {
      const info = await docker.client.containerInfo(containerId);
      const entry = (info.Config.Env || []).find(function(e) {
        return e.startsWith('OPENCLAW_GATEWAY_TOKEN=');
      });
      return entry ? entry.split('=')[1] : null;
    } catch(err) {
      return null;
    }
  }

  async function getContainerPort(containerId) {
    try {
      const containers = await docker.client.listContainers({ all: true });
      const c = containers.find(function(x) {
        return x.Id === containerId || x.Id.startsWith(containerId);
      });
      if (c && c.Ports) {
        const p = c.Ports.find(function(x) { return x.PrivatePort === 18792; });
        if (p && p.PublicPort) return String(p.PublicPort);
      }
      return '18792';
    } catch(err) {
      return '18792';
    }
  }

  async function gatewayHealth(baseUrl, token) {
    return new Promise(function(resolve) {
      const headers = {};
      if (token) headers['Authorization'] = 'Bearer ' + token;
      const req = http.get(baseUrl + '/health', { headers: headers }, function(res) {
        let data = '';
        res.on('data', function(d) { data += d; });
        res.on('end', function() {
          try { resolve({ ok: true, data: JSON.parse(data) }); }
          catch(e) { resolve({ ok: true, data: data }); }
        });
      });
      req.on('error', function(e) { resolve({ ok: false, error: e.message }); });
      req.setTimeout(5000, function() { req.destroy(); resolve({ ok: false, error: 'timeout' }); });
    });
  }

  function proxyRequest(req, res, targetBase, path, token) {
    const headers = {};
    if (token) headers['Authorization'] = 'Bearer ' + token;
    headers['X-Forwarded-For'] = req.ip;
    headers['X-Forwarded-Proto'] = req.protocol;

    const url = targetBase + '/' + path;
    const method = req.method;

    if (['POST', 'PUT', 'PATCH'].includes(method)) {
      const body = JSON.stringify(req.body);
      headers['Content-Type'] = 'application/json';
      headers['Content-Length'] = Buffer.byteLength(body);

      const proxyReq = http.request(url, { method: method, headers: headers }, function(proxyRes) {
        res.set(proxyRes.headers);
        res.status(proxyRes.statusCode);
        proxyRes.on('data', function(d) { res.write(d); });
        proxyRes.on('end', function() { res.end(); });
      });
      proxyReq.on('error', function(e) { errorResponse(res, 502, e.message); });
      proxyReq.setTimeout(15000, function() { proxyReq.destroy(); errorResponse(res, 504, 'gateway timeout'); });
      proxyReq.write(body);
      proxyReq.end();
    } else {
      const proxyReq = http.get(url, { headers: headers }, function(proxyRes) {
        res.set(proxyRes.headers);
        res.status(proxyRes.statusCode);
        proxyRes.on('data', function(d) { res.write(d); });
        proxyRes.on('end', function() { res.end(); });
      });
      proxyReq.on('error', function(e) { errorResponse(res, 502, e.message); });
      proxyReq.setTimeout(15000, function() { proxyReq.destroy(); errorResponse(res, 504, 'gateway timeout'); });
    }
  }

  // ── GET /openclaw/status ────────────────────────────────────────────────

  router.get('/status', asyncHandler(async function(req, res) {
    const container = await findOpenClawContainer();

    if (!container) {
      return ok(res, { deployed: false });
    }

    const token = await getGatewayToken(container.Id);
    const port = await getContainerPort(container.Id);
    const baseUrl = 'http://localhost:' + port;
    const health = await gatewayHealth(baseUrl, token);

    ok(res, {
      deployed: true,
      container: {
        id: container.Id.slice(0, 12),
        name: container.Name,
        state: container.State,
        status: container.Status,
        created: container.Created,
        image: container.Image
      },
      gateway: {
        url: baseUrl,
        port: port,
        healthy: health.ok,
        healthData: health.data || null,
        tokenSet: !!token
      }
    });
  }));

  // ── POST /openclaw/deploy ───────────────────────────────────────────────

  router.post('/deploy', asyncHandler(async function(req, res) {
    const existing = await findOpenClawContainer();
    if (existing) {
      return conflict(res, 'OpenClaw is already deployed');
    }

    const image = 'ghcr.io/nousresearch/openclaw:latest';
    const name = 'openclaw-' + Date.now();
    const gatewayToken = generateToken();

    // Pull image
    log.info('Pulling ' + image + '...');
    try {
      await new Promise(function(resolve, reject) {
        docker.client.pull(image, function(err, stream) {
          if (err) return reject(err);
          docker.client.modem.followProgress(stream, function(err2) {
            if (err2) return reject(err2);
            resolve();
          });
        });
      });
    } catch(e) {
      log.error('OpenClaw pull failed: ' + e.message);
      return errorResponse(res, 500, 'Failed to pull image: ' + e.message);
    }

    // Create + start container
    try {
      const container = await docker.client.createContainer({
        name: name,
        Image: image,
        Env: [
          'OPENCLAW_GATEWAY_MODE=local',
          'OPENCLAW_GATEWAY_TOKEN=' + gatewayToken
        ],
        HostConfig: {
          PortBindings: { '18792/tcp': [{ HostPort: '18792' }] },
          RestartPolicy: { Name: 'unless-stopped' },
          Labels: {
            'dashcaddy.managed': 'true',
            'dashcaddy.app': 'openclaw'
          }
        },
        ExposedPorts: { '18792/tcp': {} }
      });

      await container.start();
      log.info('OpenClaw deployed: ' + container.id.slice(0, 12));

      ok(res, {
        deployed: true,
        container: { id: container.id.slice(0, 12), name: name },
        gateway: {
          url: 'http://localhost:18792',
          token: gatewayToken
        }
      });
    } catch(e) {
      log.error('OpenClaw deploy failed: ' + e.message);
      errorResponse(res, 500, 'Deploy failed: ' + e.message);
    }
  }));

  // ── GET /openclaw/proxy/* ───────────────────────────────────────────────

  router.get('/proxy/*', asyncHandler(async function(req, res) {
    const container = await findOpenClawContainer();
    if (!container) return notFound(res, 'OpenClaw not deployed');

    const token = await getGatewayToken(container.Id);
    const port = await getContainerPort(container.Id);
    const baseUrl = 'http://localhost:' + port;
    const path = req.params[0];

    proxyRequest(req, res, baseUrl, path, token);
  }));

  // ── POST /openclaw/proxy/* ──────────────────────────────────────────────

  router.post('/proxy/*', asyncHandler(async function(req, res) {
    const container = await findOpenClawContainer();
    if (!container) return notFound(res, 'OpenClaw not deployed');

    const token = await getGatewayToken(container.Id);
    const port = await getContainerPort(container.Id);
    const baseUrl = 'http://localhost:' + port;
    const path = req.params[0];

    proxyRequest(req, res, baseUrl, path, token);
  }));

  // ── DELETE /openclaw ───────────────────────────────────────────────────

  router.delete('/', asyncHandler(async function(req, res) {
    const container = await findOpenClawContainer();
    if (!container) return notFound(res, 'OpenClaw not deployed');

    try {
      const c = docker.client.container(container.Id);
      await c.stop().catch(function() {});
      await c.remove({ force: true });
      log.info('OpenClaw container ' + container.Id.slice(0, 12) + ' removed');
      ok(res, { message: 'OpenClaw removed' });
    } catch(e) {
      log.error('Failed to remove OpenClaw: ' + e.message);
      errorResponse(res, 500, e.message);
    }
  }));

  return router;
};

// ── token generator ──────────────────────────────────────────────────────────

function generateToken() {
  const chars = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789';
  let result = '';
  for (let i = 0; i < 32; i++) {
    result += chars.charAt(Math.floor(Math.random() * chars.length));
  }
  return result;
}
