/**
 * IT0 Bridge — runs alongside the OpenClaw gateway inside the container.
 * Exposes a REST API on port 3000 that:
 *  - Forwards task submissions to OpenClaw via WebSocket RPC
 *  - Returns status, session list, and metrics
 *  - Reports heartbeat to IT0 agent-service
 */
import 'dotenv/config';
import express from 'express';
import { OpenClawClient } from './openclaw-client';

const PORT = parseInt(process.env.BRIDGE_PORT ?? '3000', 10);
const OPENCLAW_GATEWAY = process.env.OPENCLAW_GATEWAY_URL ?? 'ws://127.0.0.1:18789';
const OPENCLAW_TOKEN = process.env.OPENCLAW_GATEWAY_TOKEN ?? '';
const INSTANCE_ID = process.env.IT0_INSTANCE_ID ?? 'unknown';
const IT0_AGENT_URL = process.env.IT0_AGENT_SERVICE_URL ?? '';

const app = express();
app.use(express.json());

// ── OpenClaw client ──────────────────────────────────────────────────────────

const ocClient = new OpenClawClient(OPENCLAW_GATEWAY, OPENCLAW_TOKEN);
let startTime = Date.now();
let gatewayReady = false;

async function connectWithRetry(maxRetries = 20, delayMs = 3000): Promise<void> {
  for (let i = 0; i < maxRetries; i++) {
    try {
      await ocClient.connect();
      gatewayReady = true;
      console.log('[bridge] Connected to OpenClaw gateway');
      return;
    } catch (err) {
      console.warn(`[bridge] Gateway not ready (attempt ${i + 1}/${maxRetries}), retrying...`);
      await new Promise((r) => setTimeout(r, delayMs));
    }
  }
  throw new Error('Could not connect to OpenClaw gateway after retries');
}

// ── Routes ───────────────────────────────────────────────────────────────────

// Health check — used by IT0 monitoring
app.get('/health', (_req, res) => {
  res.json({
    status: gatewayReady ? 'ok' : 'starting',
    instanceId: INSTANCE_ID,
    uptime: Math.floor((Date.now() - startTime) / 1000),
    gatewayConnected: ocClient.isConnected(),
  });
});

// Status — detailed instance info
app.get('/status', (_req, res) => {
  res.json({
    instanceId: INSTANCE_ID,
    gatewayConnected: ocClient.isConnected(),
    uptime: Math.floor((Date.now() - startTime) / 1000),
  });
});

// Submit a task to OpenClaw
// Uses chat.send (Protocol v3). sessionKey format: "agent:main:<name>"
// timeoutSeconds: how long to wait for agent reply (0=fire-and-forget, default 25)
app.post('/task', async (req, res) => {
  if (!ocClient.isConnected()) {
    res.status(503).json({ error: 'Gateway not connected' });
    return;
  }
  try {
    const sessionKey = req.body.sessionKey ?? `agent:main:${req.body.sessionId ?? 'main'}`;
    const timeoutSeconds: number = req.body.timeoutSeconds ?? 25;
    const result = await ocClient.rpc(
      'chat.send',
      { sessionKey, message: req.body.prompt, timeoutSeconds },
      (timeoutSeconds + 5) * 1000, // rpc timeout = agent timeout + 5s buffer
    );
    res.json({ ok: true, result });
  } catch (err: any) {
    res.status(500).json({ error: err.message });
  }
});

// List sessions
app.get('/sessions', async (_req, res) => {
  if (!ocClient.isConnected()) {
    res.status(503).json({ error: 'Gateway not connected' });
    return;
  }
  try {
    const result = await ocClient.rpc('sessions.list', { limit: 100 });
    res.json(result);
  } catch (err: any) {
    res.status(500).json({ error: err.message });
  }
});

// Gateway status (metrics.get does not exist in OpenClaw Protocol v3)
app.get('/metrics', async (_req, res) => {
  if (!ocClient.isConnected()) {
    res.status(503).json({ error: 'Gateway not connected' });
    return;
  }
  try {
    const result = await ocClient.rpc('gateway.status');
    res.json(result);
  } catch (err: any) {
    res.status(500).json({ error: err.message });
  }
});

// ── Heartbeat to IT0 agent-service ───────────────────────────────────────────

async function sendHeartbeat(): Promise<void> {
  if (!IT0_AGENT_URL || !INSTANCE_ID || INSTANCE_ID === 'unknown') return;
  try {
    await fetch(`${IT0_AGENT_URL}/api/v1/agent/instances/${INSTANCE_ID}/heartbeat`, {
      method: 'POST',
      headers: { 'Content-Type': 'application/json' },
      body: JSON.stringify({
        gatewayConnected: ocClient.isConnected(),
        uptime: Math.floor((Date.now() - startTime) / 1000),
      }),
    });
  } catch {
    // Best-effort, don't crash on network error
  }
}

// ── Start ─────────────────────────────────────────────────────────────────────

app.listen(PORT, () => {
  console.log(`[bridge] IT0 Bridge listening on port ${PORT}`);
  startTime = Date.now();
});

connectWithRetry().catch((err) => {
  console.error('[bridge] Fatal: could not connect to OpenClaw gateway:', err.message);
  // Keep bridge running for health-check endpoint even if gateway failed
});

// Heartbeat every 60 seconds
setInterval(sendHeartbeat, 60_000);
