/**
 * Cowork Service — Fastify server entry point.
 *
 * Bridge between Tauri desktop / external clients and the Rust agent runtime.
 * Connects to platform-service for auth, flags, audit, and billing.
 *
 * Follows the same ecosystem pattern as FlowMonk, ActionTrail, NoteLett, etc.:
 * - @bytelyst/backend-config for Zod env validation
 * - @bytelyst/fastify-auth for JWT context
 * - @bytelyst/backend-telemetry for usage analytics
 * - @bytelyst/backend-flags for feature flags
 * - IPC bridge to Rust cowork-orchestrator via JSON-RPC
 *
 * Port: 4009 (configurable via PORT env var).
 * Product: clawcowork
 */

import { createServiceApp, registerOptionalJwtContext, startService } from '@bytelyst/fastify-core';
import { jwtVerify } from 'jose';
import { healthRoutes } from './modules/health/routes.js';
import { taskRoutes } from './modules/tasks/routes.js';
import { config } from './lib/config.js';
import { productConfig, PRODUCT_ID } from './lib/product-config.js';
import { getIpcBridge } from './lib/ipc-bridge.js';
import { getFlushScheduler } from './lib/flush-scheduler.js';
import { initLlmRouter, getLlmRouter, isLlmRouterReady } from './lib/llm-router.js';
import { llmRoutes } from './modules/llm/routes.js';
import { auditRoutes } from './modules/audit/routes.js';
import { usageRoutes } from './modules/usage/routes.js';
import { notificationRoutes } from './modules/notifications/routes.js';
import { extractionRoutes } from './modules/extraction/routes.js';
import { marketplaceRoutes } from './modules/marketplace/routes.js';
import { sessionRoutes } from './modules/sessions/routes.js';
import { pluginRoutes } from './modules/plugins/routes.js';
import { scheduleRoutes } from './modules/schedule/routes.js';
import { pushRoutes } from './modules/push/routes.js';
import { agentRuntimeRoutes } from './modules/agent-runtime/routes.js';
import type { JwtPayload } from './lib/request-context.js';

const jwtSecret = new TextEncoder().encode(config.JWT_SECRET);

const app = await createServiceApp({
  name: config.SERVICE_NAME,
  version: '0.1.0',
  description: `${productConfig.displayName} — Fastify bridge ↔ Rust agent runtime ↔ platform-service`,
  corsOrigin: config.CORS_ORIGIN,
  swagger: {
    title: `${productConfig.displayName} Service`,
    description: 'REST API for Claw Cowork agent task management',
    port: config.PORT,
  },
  readiness: true,
});

// JWT context — optional in dev, required in production
await registerOptionalJwtContext(app, {
  verifyToken: async (token: string) => {
    const { payload } = await jwtVerify(token, jwtSecret, { issuer: 'bytelyst-platform' });
    return payload as unknown as JwtPayload;
  },
});

// Register route modules
await app.register(healthRoutes);
await app.register(taskRoutes);
await app.register(llmRoutes);
await app.register(auditRoutes);
await app.register(usageRoutes);
await app.register(notificationRoutes);
await app.register(extractionRoutes);
await app.register(marketplaceRoutes);
await app.register(sessionRoutes);
await app.register(pluginRoutes);
await app.register(scheduleRoutes);
await app.register(pushRoutes);
await app.register(agentRuntimeRoutes);

// Bootstrap endpoint (same pattern as FlowMonk, ActionTrail, etc.)
app.get('/api/bootstrap', async () => ({
  productId: PRODUCT_ID,
  displayName: productConfig.displayName,
  platforms: productConfig.platforms,
  ipcBridgeConnected: getIpcBridge().isRunning,
}));

// Start IPC bridge to Rust runtime (non-blocking — service works in fallback mode if it fails)
const bridge = getIpcBridge();
try {
  await bridge.start();
  app.log.info('IPC bridge to Rust runtime connected');
} catch (err) {
  app.log.warn({ err }, 'IPC bridge failed to start — running in fallback mode');
}

// Initialize LLM router (best-effort — works without API keys in dev)
// Set OLLAMA_MODELS=model1,model2 to add local Ollama as a provider.
const ollamaModels = config.OLLAMA_MODELS?.split(',')
  .map(s => s.trim())
  .filter(Boolean);
try {
  const llm = initLlmRouter({
    ollamaModels,
    ollamaBaseUrl: config.OLLAMA_URL,
    onTelemetry: entry => app.log.debug({ llmTelemetry: entry }, 'llm-router event'),
  });
  app.log.info({ providers: llm.getProviders() }, 'LLM router initialized');
} catch (err) {
  app.log.warn({ err }, 'LLM router not available — no provider API keys configured');
}

// Register reverse-IPC handler for LLM interception (Rust → TS)
// When the Rust runtime needs a non-Anthropic LLM call, it sends intercept_llm
// and cowork-service routes through the multi-provider LLM router.
bridge.onIncomingRequest(async (method, params) => {
  if (method === 'intercept_llm') {
    if (!isLlmRouterReady()) {
      throw new Error('LLM router not initialized — no providers configured');
    }
    const messages = (params.messages as Array<{ role: string; content: string }>) ?? [];
    if (messages.length === 0) throw new Error('messages array is required');

    const startMs = Date.now();
    const result = await getLlmRouter().chat({
      messages: messages.map(m => ({
        role: m.role as 'system' | 'user' | 'assistant',
        content: m.content,
      })),
      model: (params.model as string) || undefined,
      temperature: (params.temperature as number) ?? undefined,
      max_tokens: (params.max_tokens as number) ?? undefined,
    });

    // Record spend for budget tracking (best-effort)
    const costUsd = (result as unknown as Record<string, unknown>).costUsd as number | undefined;
    if (costUsd && params.auth && params.taskId) {
      bridge
        .recordSpend(
          result.model,
          0, // token counts not always available from router
          0,
          costUsd,
          params.auth as Record<string, unknown>,
          params.taskId as string
        )
        .catch(err => app.log.warn({ err }, 'Failed to record LLM spend'));
    }

    app.log.info(
      {
        method: 'intercept_llm',
        provider: result.provider,
        model: result.model,
        latencyMs: Date.now() - startMs,
        attempts: result.attempts,
      },
      'LLM interception completed'
    );

    return {
      response: result.response,
      provider: result.provider,
      model: result.model,
      totalLatencyMs: result.totalLatencyMs,
      attempts: result.attempts,
    };
  }

  throw new Error(`Unknown incoming method: ${method}`);
});

// Start flush scheduler (periodic drain of IPC buffers → platform-service)
const scheduler = getFlushScheduler(app.log);
if (bridge.isRunning) {
  scheduler.start();
  app.log.info('flush scheduler started — audit/telemetry/budget/flags');
}

// Graceful shutdown
app.addHook('onClose', async () => {
  await scheduler.finalFlush();
  await bridge.shutdown();
});

await startService(app, { port: config.PORT, host: config.HOST });
