import type { FastifyInstance } from 'fastify';
import {
  AgentDispatchRequestSchema,
  AgentRunSchema,
  AgentSessionSchema,
  type AgentRun,
  type AgentSession,
} from '@bytelyst/events';
import { requireJwtOrApiKey } from '../../lib/api-key-auth.js';
import { BadRequestError, ForbiddenError } from '../../lib/errors.js';
import * as runRepo from '../runs/repository.js';
import * as sessionRepo from '../sessions/repository.js';

function mapOriginSurface(
  platform: string
): 'browser' | 'mobile' | 'desktop' | 'web' | 'product-api' {
  switch (platform) {
    case 'ios':
    case 'android':
      return 'mobile';
    case 'desktop':
      return 'desktop';
    case 'web':
      return 'web';
    default:
      return 'product-api';
  }
}

function mapSessionStatus(session: {
  revokedAt?: string;
  expiresAt: string;
}): AgentSession['status'] {
  if (session.revokedAt) return 'cancelled';
  if (new Date(session.expiresAt).getTime() < Date.now()) return 'completed';
  return 'active';
}

function mapRunStatus(status: string): AgentRun['status'] {
  switch (status) {
    case 'queued':
      return 'queued';
    case 'running':
      return 'running';
    case 'succeeded':
      return 'completed';
    case 'failed':
      return 'failed';
    case 'cancelled':
      return 'cancelled';
    default:
      return 'paused';
  }
}

function toAgentSession(doc: {
  id: string;
  productId: string;
  userId: string;
  platform: string;
  createdAt: string;
  lastActiveAt: string;
  expiresAt: string;
  revokedAt?: string;
}): AgentSession {
  return AgentSessionSchema.parse({
    sessionId: doc.id,
    productId: doc.productId,
    userId: doc.userId,
    status: mapSessionStatus(doc),
    startedAt: doc.createdAt,
    updatedAt: doc.lastActiveAt,
    resumable: !doc.revokedAt,
    currentTaskId: null,
    memoryRefs: [],
    artifactRefs: [],
    approvalRefs: [],
    dispatchContext: {
      originSurface: mapOriginSurface(doc.platform),
      originProductId: doc.productId,
      dispatchMode: 'interactive',
      initiatedAt: doc.createdAt,
    },
  });
}

function toAgentRun(doc: {
  id: string;
  productId: string;
  status: string;
  createdAt: string;
  completedAt?: string;
  metadata?: Record<string, unknown>;
}): AgentRun {
  return AgentRunSchema.parse({
    runId: doc.id,
    sessionId:
      typeof doc.metadata?.sessionId === 'string' && doc.metadata.sessionId.length > 0
        ? doc.metadata.sessionId
        : `sessionless_${doc.id}`,
    productId: doc.productId,
    status: mapRunStatus(doc.status),
    startedAt: doc.createdAt,
    completedAt: doc.completedAt ?? null,
    checkpointArtifactId:
      typeof doc.metadata?.checkpointArtifactId === 'string'
        ? doc.metadata.checkpointArtifactId
        : null,
    correlationId:
      typeof doc.metadata?.correlationId === 'string' ? doc.metadata.correlationId : null,
  });
}

export async function agentRuntimeRoutes(app: FastifyInstance) {
  app.get('/agent-runtime/sessions', async req => {
    const access = await requireJwtOrApiKey(req, {
      allowJwt: true,
      apiKeyScopes: ['jobs:read'],
      apiKeyTokenTypes: ['service_api'],
    });

    const requestedUserId =
      typeof (req.query as { userId?: string }).userId === 'string'
        ? (req.query as { userId?: string }).userId
        : undefined;

    if (access.source === 'jwt') {
      const isAdmin = req.jwtPayload?.role === 'admin' || req.jwtPayload?.role === 'super_admin';
      if (!isAdmin && requestedUserId && requestedUserId !== access.actorId) {
        throw new ForbiddenError('Non-admin callers can only access their own runtime sessions');
      }
    }

    const userId = access.source === 'jwt' ? (requestedUserId ?? access.actorId) : requestedUserId;
    if (!userId) {
      throw new BadRequestError('userId is required for API key callers');
    }

    const docs = await sessionRepo.listAllUserSessions(userId);
    return {
      sessions: docs.map(toAgentSession),
      count: docs.length,
    };
  });

  app.get('/agent-runtime/runs', async req => {
    const access = await requireJwtOrApiKey(req, {
      jwtRoles: ['super_admin', 'admin'],
      apiKeyScopes: ['jobs:read'],
      apiKeyTokenTypes: ['service_api'],
      rateLimitKey: 'jobs:read',
    });

    const limitRaw = (req.query as { limit?: string }).limit;
    const limit = limitRaw ? Number(limitRaw) : 20;
    const docs = await runRepo.listRuns(access.productId, {
      limit,
      kind: undefined,
      status: undefined,
    });

    return {
      runs: docs.map(toAgentRun),
      count: docs.length,
    };
  });

  app.post('/agent-runtime/dispatch/validate', async req => {
    const access = await requireJwtOrApiKey(req, {
      allowJwt: true,
      apiKeyScopes: ['jobs:write'],
      apiKeyTokenTypes: ['service_api'],
      rateLimitKey: 'jobs:write',
    });

    const parsed = AgentDispatchRequestSchema.safeParse(req.body);
    if (!parsed.success) {
      throw new BadRequestError(parsed.error.issues.map(issue => issue.message).join('; '));
    }

    if (parsed.data.targetProductId !== access.productId) {
      throw new BadRequestError(
        `Dispatch target product '${parsed.data.targetProductId}' must match authenticated product '${access.productId}'`
      );
    }

    return {
      valid: true,
      dispatchRequest: parsed.data,
    };
  });
}
