import { Hono } from 'hono';
import { streamSSE } from 'hono/streaming';
import { z } from 'zod';
import { zValidator } from '@hono/zod-validator';
import { AgentRunner } from '../agent/runner.js';
import { createLogger } from '../utils/logger.js';
import type { Config } from '../config.js';

/**
 * Chat request schema
 */
const ChatRequestSchema = z.object({
  message: z.string().min(1, 'Message is required'),
  model: z.string().optional(),
  mode: z.enum(['code', 'plan', 'explore']).optional(),
  session_id: z.string().optional(),
});

type ChatRequest = z.infer<typeof ChatRequestSchema>;

/**
 * Agent API routes
 */
export const agentRoutes = new Hono<{
  Variables: {
    config: Config;
    repoRoot: string;
  };
}>();

/**
 * POST /agent/chat
 *
 * Streaming chat endpoint using Server-Sent Events
 */
agentRoutes.post('/chat', zValidator('json', ChatRequestSchema), async (c) => {
  const body = c.req.valid('json');
  const config = c.get('config');
  const repoRoot = c.get('repoRoot');
  const logger = createLogger({ name: 'agent-api' });

  logger.info(
    { model: body.model, mode: body.mode, sessionId: body.session_id },
    'Starting chat stream'
  );

  return streamSSE(c, async (stream) => {
    const runner = new AgentRunner({
      config,
      logger,
      repoRoot,
      model: body.model,
      mode: body.mode,
      onToolCall: (name, input) => {
        stream.writeSSE({
          event: 'tool_call',
          data: JSON.stringify({ name, input }),
        });
      },
      onToolResult: (name, result) => {
        stream.writeSSE({
          event: 'tool_result',
          data: JSON.stringify({ name, result }),
        });
      },
      onText: (text) => {
        stream.writeSSE({
          event: 'message',
          data: JSON.stringify({ text }),
        });
      },
    });

    try {
      // Send start event
      await stream.writeSSE({
        event: 'start',
        data: JSON.stringify({
          sessionId: body.session_id,
          model: body.model ?? config.defaultModel,
          mode: body.mode ?? 'code',
        }),
      });

      // Run the agent
      const result = await runner.run(body.message);

      // Send completion event
      await stream.writeSSE({
        event: 'done',
        data: JSON.stringify({
          response: result.response,
          usage: result.usage,
          iterations: result.iterations,
          toolCalls: result.toolCalls.length,
        }),
      });
    } catch (err) {
      logger.error({ error: err }, 'Chat stream error');

      await stream.writeSSE({
        event: 'error',
        data: JSON.stringify({
          error: err instanceof Error ? err.message : String(err),
        }),
      });
    }
  });
});

/**
 * POST /agent/run
 *
 * Non-streaming chat endpoint for simple use cases
 */
agentRoutes.post('/run', zValidator('json', ChatRequestSchema), async (c) => {
  const body = c.req.valid('json');
  const config = c.get('config');
  const repoRoot = c.get('repoRoot');
  const logger = createLogger({ name: 'agent-api' });

  logger.info(
    { model: body.model, mode: body.mode },
    'Starting non-streaming chat'
  );

  const runner = new AgentRunner({
    config,
    logger,
    repoRoot,
    model: body.model,
    mode: body.mode,
  });

  try {
    const result = await runner.run(body.message);

    return c.json({
      response: result.response,
      toolCalls: result.toolCalls,
      usage: result.usage,
      iterations: result.iterations,
    });
  } catch (err) {
    logger.error({ error: err }, 'Chat run error');

    return c.json(
      {
        error: err instanceof Error ? err.message : String(err),
      },
      500
    );
  }
});

/**
 * GET /agent/models
 *
 * List available models
 */
agentRoutes.get('/models', (c) => {
  const { listModels } = require('../agent/client.js');

  return c.json({
    models: listModels(),
    default: c.get('config').defaultModel,
  });
});
