/**
 * Task management routes — proxies to Rust agent runtime via IPC.
 *
 * When the IPC bridge is running, all requests are forwarded to the Rust
 * cowork-orchestrator via JSON-RPC. When the bridge is not available
 * (tests, standalone mode), falls back to an in-memory store.
 *
 * POST /api/tasks       — submit a new task
 * GET  /api/tasks       — list all tasks
 * GET  /api/tasks/:id   — get task status
 * POST /api/tasks/:id/cancel — cancel a running task
 */

import type { FastifyInstance } from 'fastify';
import { BadRequestError, NotFoundError } from '@bytelyst/errors';
import { SubmitTaskSchema, type TaskResponse, type TaskStatus } from './types.js';
import { PRODUCT_ID } from '../../lib/product-config.js';
import { getUserId } from '../../lib/request-context.js';
import { getIpcBridge } from '../../lib/ipc-bridge.js';

// ── Fallback in-memory store (used when IPC bridge is not running) ──

const fallbackTasks = new Map<string, TaskResponse>();
let nextFallbackId = 1;

function generateTaskId(): string {
  return `task_${Date.now()}_${nextFallbackId++}`;
}

/** Build auth context for IPC calls from the current request. */
function buildAuth(req: import('fastify').FastifyRequest): Record<string, unknown> {
  const userId = getUserId(req);
  const role = (req.jwtPayload as Record<string, unknown> | undefined)?.role ?? 'user';
  return { userId, role, productId: PRODUCT_ID, isPlatformAuth: !!req.jwtPayload };
}

export async function taskRoutes(app: FastifyInstance) {
  const bridge = getIpcBridge();

  // Submit a new task
  app.post('/api/tasks', async (req, reply) => {
    const parsed = SubmitTaskSchema.safeParse(req.body);
    if (!parsed.success) {
      throw new BadRequestError(parsed.error.issues.map(i => i.message).join('; '));
    }

    const input = parsed.data;

    // ── IPC path ──
    if (bridge.isRunning) {
      const resp = await bridge.submitTask(input.goal, input.folder, buildAuth(req), {
        model: input.model,
        plugins: input.plugins,
      });
      if (resp.error) {
        throw new BadRequestError(resp.error.message);
      }
      reply.code(201);
      return resp.result;
    }

    // ── Fallback in-memory ──
    const now = new Date().toISOString();
    const task: TaskResponse = {
      id: generateTaskId(),
      productId: PRODUCT_ID,
      goal: input.goal,
      folder: input.folder,
      model: input.model,
      status: 'pending',
      createdAt: now,
      updatedAt: now,
    };
    fallbackTasks.set(task.id, task);
    req.log.info({ taskId: task.id, goal: input.goal }, 'task submitted (fallback)');
    reply.code(201);
    return task;
  });

  // List all tasks
  app.get('/api/tasks', async (req) => {
    if (bridge.isRunning) {
      const resp = await bridge.listTasks(buildAuth(req));
      if (resp.error) throw new BadRequestError(resp.error.message);
      return resp.result;
    }
    return { tasks: Array.from(fallbackTasks.values()) };
  });

  // Get single task
  app.get('/api/tasks/:id', async (req) => {
    const { id } = req.params as { id: string };

    if (bridge.isRunning) {
      const resp = await bridge.getTaskStatus(id, buildAuth(req));
      if (resp.error) {
        if (resp.error.code === -32001) throw new NotFoundError('Task not found');
        throw new BadRequestError(resp.error.message);
      }
      return resp.result;
    }

    const task = fallbackTasks.get(id);
    if (!task) throw new NotFoundError('Task not found');
    return task;
  });

  // Cancel a task
  app.post('/api/tasks/:id/cancel', async (req) => {
    const { id } = req.params as { id: string };

    if (bridge.isRunning) {
      const resp = await bridge.cancelTask(id, buildAuth(req));
      if (resp.error) {
        if (resp.error.code === -32001) throw new NotFoundError('Task not found');
        if (resp.error.code === -32003) throw new BadRequestError(resp.error.message);
        throw new BadRequestError(resp.error.message);
      }
      return resp.result;
    }

    const task = fallbackTasks.get(id);
    if (!task) throw new NotFoundError('Task not found');

    if (task.status === 'completed' || task.status === 'failed' || task.status === 'cancelled') {
      throw new BadRequestError(`Cannot cancel task in '${task.status}' state`);
    }

    task.status = 'cancelled' as TaskStatus;
    task.updatedAt = new Date().toISOString();
    req.log.info({ taskId: id }, 'task cancelled (fallback)');
    return task;
  });
}
