/**
 * HTTP transport adapter — exposes IInteractiveSession over REST API.
 *
 * Built on Hono for Cloudflare Workers + Node.js + AWS Lambda compatibility.
 * Each endpoint maps 1:1 to an IInteractiveSession API method.
 */

import { Hono } from 'hono';
import { streamSSE } from 'hono/streaming';

import type { IInteractiveSession } from '@robota-sdk/agent-interface-transport';
import type { Context } from 'hono';

/** Callback that resolves an IInteractiveSession from the request context. */
export type TSessionFactory = (c: Context) => IInteractiveSession | Promise<IInteractiveSession>;

export interface IAgentRoutesOptions {
  /** Resolve an IInteractiveSession per request (e.g., by auth token, session ID). */
  sessionFactory: TSessionFactory;
}

/**
 * Create a Hono router with all agent HTTP endpoints.
 *
 * Usage:
 * ```typescript
 * const routes = createAgentRoutes({ sessionFactory });
 * app.route('/agent', routes);          // mount on existing app
 * export default routes;                // or use standalone (CF Workers)
 * ```
 */
export function createAgentRoutes(options: IAgentRoutesOptions): Hono {
  const { sessionFactory } = options;
  const app = new Hono();

  // POST /submit — execute prompt, stream events via SSE
  app.post('/submit', async (c) => {
    const session = await sessionFactory(c);
    const body = await c.req.json<{ prompt: string }>();

    if (!body.prompt || typeof body.prompt !== 'string') {
      return c.json({ error: 'prompt is required' }, 400);
    }

    return streamSSE(c, async (stream) => {
      const cleanup: Array<() => void> = [];

      const subscribe = <T>(event: string, handler: (data: T) => void): void => {
        session.on(event as 'text_delta', handler as () => void);
        cleanup.push(() => session.off(event as 'text_delta', handler as () => void));
      };

      let completed = false;
      const done = new Promise<void>((resolve) => {
        subscribe('text_delta', (delta: string) => {
          stream.writeSSE({ event: 'text_delta', data: JSON.stringify({ delta }) });
        });

        subscribe('tool_start', (state) => {
          stream.writeSSE({ event: 'tool_start', data: JSON.stringify(state) });
        });

        subscribe('tool_end', (state) => {
          stream.writeSSE({ event: 'tool_end', data: JSON.stringify(state) });
        });

        subscribe('thinking', (isThinking: boolean) => {
          stream.writeSSE({ event: 'thinking', data: JSON.stringify({ isThinking }) });
          if (!isThinking && completed) {
            resolve();
          }
        });

        subscribe('complete', (result) => {
          completed = true;
          stream.writeSSE({ event: 'complete', data: JSON.stringify(result) });
        });

        subscribe('interrupted', (result) => {
          completed = true;
          stream.writeSSE({ event: 'interrupted', data: JSON.stringify(result) });
        });

        subscribe('error', (error: Error) => {
          completed = true;
          stream.writeSSE({ event: 'error', data: JSON.stringify({ message: error.message }) });
        });
      });

      await session.submit(body.prompt);
      await done;

      for (const fn of cleanup) fn();
    });
  });

  // POST /command — execute system command
  app.post('/command', async (c) => {
    const session = await sessionFactory(c);
    const body = await c.req.json<{ name: string; args?: string }>();

    if (!body.name || typeof body.name !== 'string') {
      return c.json({ error: 'name is required' }, 400);
    }

    const result = await session.executeCommand(body.name, body.args ?? '');
    if (!result) {
      return c.json({ error: `Unknown command: ${body.name}` }, 404);
    }
    return c.json(result);
  });

  // POST /abort — abort current execution
  app.post('/abort', async (c) => {
    const session = await sessionFactory(c);
    session.abort();
    return c.json({ ok: true });
  });

  // POST /cancel-queue — cancel queued prompt
  app.post('/cancel-queue', async (c) => {
    const session = await sessionFactory(c);
    session.cancelQueue();
    return c.json({ ok: true });
  });

  // GET /messages — get message history
  app.get('/messages', async (c) => {
    const session = await sessionFactory(c);
    return c.json(session.getMessages());
  });

  // GET /context — get context window state
  app.get('/context', async (c) => {
    const session = await sessionFactory(c);
    return c.json(session.getContextState());
  });

  // GET /executing — check if currently executing
  app.get('/executing', async (c) => {
    const session = await sessionFactory(c);
    return c.json({ executing: session.isExecuting() });
  });

  // GET /pending — get pending queued prompt
  app.get('/pending', async (c) => {
    const session = await sessionFactory(c);
    return c.json({ pending: session.getPendingPrompt() });
  });

  return app;
}
