export const dynamic = 'force-dynamic';
import { NextRequest, NextResponse } from "next/server";
import {
  ListOpenTasksRequestSchema,
  ClaimTaskRequestSchema,
  SubmitSolutionRequestSchema,
  TaskStatus,
  JudgeOverallResult
} from "@agent-bounty/contracts";
import { prisma } from "@/lib/prisma";
import { runSubmissionInSandbox } from "@/lib/sandbox";
import { logAuditEvent } from "@/lib/audit";
import { redis } from "@/lib/redis";
import { authHold, capturePayment } from "@/lib/payment";
import { sendTrafficAlert } from "@/lib/traffic-alert";
import crypto from "crypto";
import { z } from "zod";

export async function POST(request: NextRequest, props: { params: Promise<{ tool: string }> }) {
  const params = await props.params;
  const tool = params.tool;

  const authHeader = request.headers.get("Authorization");
  if (!authHeader || !authHeader.startsWith("Bearer ")) {
    return NextResponse.json({ error: "Unauthorized: Missing Bearer token" }, { status: 401 });
  }

  const token = authHeader.split(" ")[1];
  if (process.env.API_KEY && token !== process.env.API_KEY) {
    return NextResponse.json({ error: "Forbidden: Invalid API Key" }, { status: 403 });
  }

  let body: unknown = null;
  try {
    body = await request.json();

    switch (tool) {
      case "list_open_tasks": {
        ListOpenTasksRequestSchema.parse(body);
        
        const tasks = await prisma.task.findMany({
          where: { status: TaskStatus.OPEN },
        });

        const formattedTasks = tasks.map((t) => ({
          task_id: t.id,
          title: t.title,
          status: t.status,
          difficulty: t.difficulty,
          reward: {
            amount: t.reward_amount,
            currency: t.reward_currency,
            display_amount: `$${(t.reward_amount / 100).toFixed(2)}`
          },
          required_stack: t.required_stack,
          scope_clarity_score: t.scope_clarity_score,
          created_at: t.created_at.toISOString(),
          description_preview: t.description.substring(0, 100) + (t.description.length > 100 ? "..." : ""),
        }));

        void sendTrafficAlert({
          level: "info",
          action: "EXTERNAL_LIST_OPEN_TASKS_MCP",
          surface: "mcp/list_open_tasks",
          actorType: "USER",
          actorId: "mcp-anonymous",
          taskId: "open-tasks",
          message: "外部 MCP 查詢任務列表",
          metadata: {
            count: formattedTasks.length,
            source_tool: tool,
          },
        });

        return NextResponse.json({
          tasks: formattedTasks,
          total_open: formattedTasks.length,
          stockout_warning: formattedTasks.length === 0,
        });
      }

      case "claim_task": {
        const parsed = ClaimTaskRequestSchema.parse(body);

        // Verify Agent Whitelist
        const agent = await prisma.agentProfile.findUnique({
          where: { agent_id: parsed.agent_id }
        });
        if (!agent || agent.status !== "WHITELISTED") {
          return NextResponse.json({ error: "Forbidden: Agent is not whitelisted" }, { status: 403 });
        }
        
        const claim = await prisma.$transaction(async (tx) => {
          const updated = await tx.task.updateMany({
            where: { id: parsed.task_id, status: TaskStatus.OPEN },
            data: { status: TaskStatus.EXECUTING, builder_id: agent.agent_id }
          });
          
          if (updated.count === 0) {
            throw new Error("Task is not OPEN or does not exist");
          }

          const task = await tx.task.findUniqueOrThrow({ where: { id: parsed.task_id } });

          const newClaim = await tx.claim.create({
            data: {
              task_id: task.id,
              agent_id: agent.agent_id,
              developer_wallet: parsed.developer_wallet,
              status: TaskStatus.EXECUTING,
              claim_token: crypto.randomUUID(),
              held_amount: task.reward_amount,
              held_currency: task.reward_currency,
              expires_at: new Date(Date.now() + 3600000)
            }
          });
          
          await authHold(
            tx, 
            task.id, 
            task.reward_amount, 
            task.reward_currency, 
            parsed.developer_wallet, 
            `${newClaim.id}-auth-hold`
          );

          await logAuditEvent(tx, {
            actorType: "AGENT",
            actorId: parsed.developer_wallet,
            action: "CLAIM_TASK",
            entityType: "TASK",
            entityId: task.id,
            beforeState: { status: TaskStatus.OPEN },
            afterState: { status: TaskStatus.EXECUTING }
          });
          
          return newClaim;
        });

        void sendTrafficAlert({
          level: "info",
          action: "EXTERNAL_CLAIM_TASK_SUCCESS",
          surface: "mcp/claim_task",
          actorType: "AGENT",
          actorId: parsed.developer_wallet,
          taskId: claim.task_id,
          message: `Agent 成功接案: ${parsed.task_id}`,
          metadata: {
            agent_id: parsed.agent_id,
            reward: claim.held_amount,
            currency: claim.held_currency,
          },
        });

        // Set Redis TTL key (3600 seconds)
        await redis.set(`vw:task:${claim.task_id}:executing`, claim.claim_token, "EX", 3600);

        return NextResponse.json({
          task_id: claim.task_id,
          status: claim.status,
          held_amount: claim.held_amount,
          held_currency: claim.held_currency,
          expires_at: claim.expires_at.toISOString(),
          claim_token: claim.claim_token,
        });
      }

      case "submit_solution": {
        const parsed = SubmitSolutionRequestSchema.parse(body);
        
        const result = await prisma.$transaction(async (tx) => {
          const claim = await tx.claim.findUnique({ where: { claim_token: parsed.claim_token } });
          if (!claim || claim.task_id !== parsed.task_id || claim.status !== TaskStatus.EXECUTING) {
            throw new Error("Invalid claim token or claim is not EXECUTING");
          }

          const updatedTask = await tx.task.updateMany({
            where: { id: parsed.task_id, status: TaskStatus.EXECUTING },
            data: { status: TaskStatus.VERIFYING }
          });

          if (updatedTask.count === 0) {
            throw new Error("Task is not EXECUTING");
          }

          await tx.claim.update({
            where: { id: claim.id },
            data: { status: TaskStatus.VERIFYING }
          });

          const newSubmission = await tx.submission.create({
            data: {
              task_id: parsed.task_id,
              claim_id: claim.id,
              status: TaskStatus.VERIFYING,
              deliverables: parsed.deliverables as any,
              estimated_judge_complete_at: new Date(Date.now() + 300000)
            }
          });
          
          await logAuditEvent(tx, {
            actorType: "AGENT",
            action: "SUBMIT_SOLUTION",
            entityType: "TASK",
            entityId: parsed.task_id,
            beforeState: { status: TaskStatus.EXECUTING },
            afterState: { status: TaskStatus.VERIFYING }
          });
          
          return {
            submission: newSubmission,
            claim,
          };
        });
        const { submission, claim: submittedClaim } = result;

        void sendTrafficAlert({
          level: "info",
          action: "EXTERNAL_SUBMIT_SOLUTION_SUCCESS",
          surface: "mcp/submit_solution",
          actorType: "AGENT",
          actorId: submittedClaim.developer_wallet,
          taskId: submission.task_id,
          message: `Agent 提交解法: ${parsed.task_id}`,
          metadata: {
            claim_id: submission.claim_id,
            deliverable_count: Object.keys(parsed.deliverables ?? {}).length,
          },
        });

        // Async trigger E2B Sandbox evaluation
        const taskObj = await prisma.task.findUnique({ where: { id: submission.task_id }});
        if (taskObj && typeof taskObj.acceptance_criteria === "object" && taskObj.acceptance_criteria !== null) {
          const criteria = taskObj.acceptance_criteria as any;
          if (criteria.test_file_content) {
            // Fire and forget
            runSubmissionInSandbox(
              submission.id,
              parsed.deliverables as unknown as Record<string, string>,
              criteria.test_file_content
            ).then(async (result) => {
              await prisma.$transaction(async (tx) => {
                // Update submission
                await tx.submission.update({
                  where: { id: submission.id },
                  data: { status: "JUDGED" }
                });

                // Create JudgeResult
                await tx.judgeResult.create({
                  data: {
                    submission_id: submission.id,
                    overall_result: result.overall_result,
                    tests: result.tests,
                    artifacts: result.artifacts,
                    error_classification: result.error_classification,
                    resource_usage: result.resource_usage
                  }
                });

                // Update Task & Claim Status
                const newTaskStatus = result.overall_result === JudgeOverallResult.PASS 
                  ? TaskStatus.COMPLETED 
                  : TaskStatus.FAILED_RETRYABLE;
                  
                await tx.task.update({
                  where: { id: submission.task_id },
                  data: { status: newTaskStatus }
                });

                await tx.claim.update({
                  where: { id: submission.claim_id },
                  data: { status: newTaskStatus }
                });

                if (newTaskStatus === TaskStatus.COMPLETED) {
                  await capturePayment(tx, submission.task_id, `${submission.claim_id}-capture`);
                }
                
                await logAuditEvent(tx, {
                  actorType: "SYSTEM",
                  action: "JUDGE_COMPLETE",
                  entityType: "TASK",
                  entityId: submission.task_id,
                  beforeState: { status: TaskStatus.VERIFYING },
                  afterState: { status: newTaskStatus },
                  metadata: { overall_result: result.overall_result, error_classification: result.error_classification }
                });
              });
            }).catch(console.error);
          }
        }
        
        return NextResponse.json({
          task_id: submission.task_id,
          submission_id: submission.id,
          status: submission.status,
          estimated_judge_complete_at: submission.estimated_judge_complete_at?.toISOString() ?? new Date().toISOString(),
        });
      }

      case "check_payout_status": {
        const parsed = z.object({ task_id: z.string().uuid() }).parse(body);

        const task = await prisma.task.findUnique({ where: { id: parsed.task_id } });
        if (!task) {
          throw new Error("Task not found");
        }

        const ledger = await prisma.ledgerEntry.findFirst({
          where: { task_id: parsed.task_id },
          orderBy: { created_at: 'desc' }
        });

        if (!ledger) {
          return NextResponse.json({
            task_id: task.id,
            phase: task.status === "COMPLETED" ? "PAYOUT_READY" : "NO_LEDGER",
            amount: task.reward_amount,
            currency: task.reward_currency,
            updated_at: task.updated_at.toISOString(),
          });
        }

        return NextResponse.json({
          task_id: task.id,
          phase: ledger.phase,
          amount: task.reward_amount,
          currency: task.reward_currency,
          updated_at: ledger.updated_at.toISOString(),
          ledger_entry: ledger
        });
      }

      default:
        return NextResponse.json({ error: `Unknown tool: ${tool}` }, { status: 404 });
    }
  } catch (error: any) {
    console.error(`[API Gateway] Error handling ${tool}:`, error);

    void sendTrafficAlert({
      level: "error",
      action: `EXTERNAL_${tool.toUpperCase()}_ERROR`,
      surface: `mcp/${tool}`,
      actorType: "AGENT",
      actorId: typeof body === "object" && body !== null && "developer_wallet" in body
        ? String((body as { developer_wallet?: string }).developer_wallet ?? "unknown")
        : "unknown",
      taskId:
        typeof body === "object" && body !== null && "task_id" in body
          ? String((body as { task_id?: string }).task_id ?? "unknown")
          : "unknown",
      message: `${error instanceof Error ? error.message : String(error)}`,
    });
    
    if (error.name === "ZodError") {
      return NextResponse.json({ error_type: "InvalidParams", message: error.errors }, { status: 400 });
    }
    
    const msg = error.message || String(error);
    if (msg.includes("not OPEN") || msg.includes("not EXECUTING") || msg.includes("Invalid claim token") || msg.includes("not found")) {
      return NextResponse.json({ error_type: "StateConflict", message: msg }, { status: 409 });
    }

    return NextResponse.json({ error_type: "InternalError", message: msg }, { status: 500 });
  }
}
