import { Router, Request, Response } from 'express';
import { cacheMiddleware } from '../middleware/cache';
import { BestExecutionPlanner } from '../../services/best-execution-planner';
import { InternalExecutionPlanV2Builder } from '../../services/internal-execution-plan-v2';
import { PlannerRequest } from '../../services/planner-v2-types';
import { logger } from '../../utils/logger';

const router = Router();
const planner = new BestExecutionPlanner();
const executionPlanBuilder = new InternalExecutionPlanV2Builder(planner);

function parsePlannerRequest(body: Record<string, unknown>): PlannerRequest {
  const constraintsBody = body.constraints && typeof body.constraints === 'object'
    ? (body.constraints as Record<string, unknown>)
    : null;

  return {
    sourceChainId: Number(body.sourceChainId),
    destinationChainId: body.destinationChainId !== undefined ? Number(body.destinationChainId) : undefined,
    tokenIn: String(body.tokenIn || ''),
    tokenOut: String(body.tokenOut || ''),
    amountIn: String(body.amountIn || ''),
    recipient: body.recipient ? String(body.recipient) : undefined,
    constraints: constraintsBody
      ? {
          maxSlippageBps: typeof constraintsBody.maxSlippageBps === 'number'
            ? Number(constraintsBody.maxSlippageBps)
            : undefined,
          allowedProviders: Array.isArray(constraintsBody.allowedProviders)
            ? (constraintsBody.allowedProviders as string[])
                .map((value) => String(value)) as NonNullable<PlannerRequest['constraints']>['allowedProviders']
            : undefined,
          maxLegs: typeof constraintsBody.maxLegs === 'number'
            ? Number(constraintsBody.maxLegs)
            : undefined,
          allowedIntermediates: Array.isArray(constraintsBody.allowedIntermediates)
            ? (constraintsBody.allowedIntermediates as string[]).map((value) => String(value))
            : undefined,
          complianceProfile: typeof constraintsBody.complianceProfile === 'string'
            ? String(constraintsBody.complianceProfile) as NonNullable<PlannerRequest['constraints']>['complianceProfile']
            : undefined,
          allowBridge: typeof constraintsBody.allowBridge === 'boolean'
            ? Boolean(constraintsBody.allowBridge)
            : undefined,
          preferredBridges: Array.isArray(constraintsBody.preferredBridges)
            ? (constraintsBody.preferredBridges as string[]).map((value) => String(value))
            : undefined,
          allowCommodityIntermediates: typeof constraintsBody.allowCommodityIntermediates === 'boolean'
            ? Boolean(constraintsBody.allowCommodityIntermediates)
            : undefined,
        }
      : undefined,
  };
}

function validatePlannerRequest(request: PlannerRequest): string | null {
  if (!request.sourceChainId || Number.isNaN(request.sourceChainId)) return 'sourceChainId is required';
  if (!request.tokenIn) return 'tokenIn is required';
  if (!request.tokenOut) return 'tokenOut is required';
  if (!request.amountIn) return 'amountIn is required';
  try {
    BigInt(request.amountIn);
  } catch {
    return 'amountIn must be an integer string';
  }
  return null;
}

function handlePlannerFailure(res: Response, action: string, error: unknown) {
  logger.error(`Planner v2 ${action} failed`, error);
  return res.status(500).json({ error: 'Internal server error' });
}

router.get('/providers/capabilities', cacheMiddleware(15 * 1000), async (req: Request, res: Response) => {
  try {
    const chainId = Number(req.query.chainId || '138');
    return res.json({
      generatedAt: new Date().toISOString(),
      chainId,
      providers: planner.getCapabilities(chainId),
    });
  } catch (error) {
    return handlePlannerFailure(res, 'provider capability lookup', error);
  }
});

router.post('/routes/plan', async (req: Request, res: Response) => {
  try {
    const request = parsePlannerRequest((req.body || {}) as Record<string, unknown>);
    const error = validatePlannerRequest(request);
    if (error) {
      return res.status(400).json({ error });
    }

    const response = await planner.plan({
      ...request,
      destinationChainId: request.destinationChainId || request.sourceChainId,
    });
    return res.json(response);
  } catch (error) {
    return handlePlannerFailure(res, 'route planning', error);
  }
});

router.post('/intents/plan', async (req: Request, res: Response) => {
  try {
    const request = parsePlannerRequest((req.body || {}) as Record<string, unknown>);
    const error = validatePlannerRequest(request);
    if (error) {
      return res.status(400).json({ error });
    }
    if (!request.destinationChainId || request.destinationChainId === request.sourceChainId) {
      return res.status(400).json({ error: 'destinationChainId must be different from sourceChainId for intent planning' });
    }

    const response = await planner.plan(request);
    return res.json(response);
  } catch (error) {
    return handlePlannerFailure(res, 'intent planning', error);
  }
});

router.post('/routes/internal-execution-plan', async (req: Request, res: Response) => {
  try {
    const request = parsePlannerRequest((req.body || {}) as Record<string, unknown>);
    const error = validatePlannerRequest(request);
    if (error) {
      return res.status(400).json({ error });
    }

    const result = await executionPlanBuilder.build(request);
    if (result.error) {
      return res.status(400).json(result);
    }
    return res.json(result);
  } catch (error) {
    return handlePlannerFailure(res, 'internal execution plan build', error);
  }
});

export default router;
