import { NextResponse } from "next/server";
import { verifySignedAgentEvent } from "@pons-agent/protocol";
import { createPublicClient, decodeEventLog, decodeFunctionData, getAddress, http, parseEther, toEventSelector } from "viem";
import { insertAgentEvent, listAgentEvents, listPipelineAgentEvents } from "@/lib/agent-events-db";
import { allowedPublicAgentWallets } from "@/lib/public-agent-wallets";
import {
  PONS_FACTORY,
  PONS_LOCKER,
  PONS_SWAP_ROUTER,
  PONS_WETH,
  ROBINHOOD_RPC_URL,
  feesClaimedEvent,
  robinhoodChain,
  swapRouterAbi,
  tokenLaunchedEvent,
  transferEvent,
} from "@/lib/pons";

export const dynamic = "force-dynamic";
export const runtime = "nodejs";

const client = createPublicClient({
  chain: robinhoodChain,
  transport: http(ROBINHOOD_RPC_URL, { timeout: 12_000, retryCount: 2 }),
});

const confirmedTypes = new Set(["launch.confirmed", "fees.claimed", "buyback.confirmed"]);
const submittedTypes = new Set(["launch.submitted", "buyback.submitted"]);
const tokenLaunchedTopic = toEventSelector(tokenLaunchedEvent);
const feesClaimedTopic = toEventSelector(feesClaimedEvent);
const transferTopic = toEventSelector(transferEvent);

export async function GET(request: Request) {
  const requestedLimit = Number(new URL(request.url).searchParams.get("limit") ?? 30);
  const limit = Number.isSafeInteger(requestedLimit) ? Math.min(100, Math.max(1, requestedLimit)) : 30;
  return NextResponse.json({
    events: listAgentEvents(limit),
    pipelineEvents: listPipelineAgentEvents(),
    updatedAt: new Date().toISOString(),
  });
}

export async function POST(request: Request) {
  try {
    const wallets = allowedPublicAgentWallets();
    if (wallets.size === 0) {
      return NextResponse.json({ error: "Public Agent event ingestion is not configured" }, { status: 503 });
    }

    const raw = await request.json();
    if (JSON.stringify(raw).length > 65_536) {
      return NextResponse.json({ error: "Event exceeds the 64 KiB public payload limit" }, { status: 413 });
    }

    const event = await verifySignedAgentEvent(raw);
    if (!wallets.has(event.signer.toLowerCase())) {
      return NextResponse.json({ error: "Signer is not an allowed public Agent wallet" }, { status: 403 });
    }

    const requiresTransaction = confirmedTypes.has(event.type) || submittedTypes.has(event.type);
    if (requiresTransaction && !event.txHash) {
      return NextResponse.json({ error: `${event.type} requires a transaction hash` }, { status: 422 });
    }
    if (confirmedTypes.has(event.type) && event.status !== "confirmed") {
      return NextResponse.json({ error: `${event.type} must use confirmed status` }, { status: 422 });
    }

    let verification: "signature" | "chain" = "signature";
    if (event.txHash && event.status === "confirmed") {
      const receipt = await client.getTransactionReceipt({ hash: event.txHash as `0x${string}` });
      if (receipt.status !== "success") {
        return NextResponse.json({ error: "Transaction did not succeed on Robinhood Chain" }, { status: 422 });
      }

      if (event.type === "launch.confirmed") {
        const launchLog = receipt.logs.find((log) =>
          log.address.toLowerCase() === PONS_FACTORY.toLowerCase()
          && log.topics[0]?.toLowerCase() === tokenLaunchedTopic.toLowerCase(),
        );
        if (!launchLog) {
          return NextResponse.json({ error: "Transaction is not a Pons TokenLaunched transaction" }, { status: 422 });
        }
        const launched = decodeEventLog({ abi: [tokenLaunchedEvent], data: launchLog.data, topics: launchLog.topics });
        const payloadToken = typeof event.payload.token === "string" ? getAddress(event.payload.token) : null;
        const payloadPool = typeof event.payload.pool === "string" ? getAddress(event.payload.pool) : null;
        if (!payloadToken
          || !payloadPool
          || launched.args.deployer.toLowerCase() !== event.signer.toLowerCase()
          || launched.args.token.toLowerCase() !== payloadToken.toLowerCase()
          || launched.args.pool.toLowerCase() !== payloadPool.toLowerCase()) {
          return NextResponse.json({ error: "Launch payload or deployer does not match the Pons transaction" }, { status: 422 });
        }
      } else if (event.type === "fees.claimed") {
        const claimLog = receipt.logs.find((log) =>
          log.address.toLowerCase() === PONS_LOCKER.toLowerCase()
          && log.topics[0]?.toLowerCase() === feesClaimedTopic.toLowerCase(),
        );
        if (!claimLog) {
          return NextResponse.json({ error: "Transaction is not a Pons FeesClaimed transaction" }, { status: 422 });
        }
        const claimed = decodeEventLog({ abi: [feesClaimedEvent], data: claimLog.data, topics: claimLog.topics });
        const payloadToken = typeof event.payload.token === "string" ? getAddress(event.payload.token) : null;
        const payloadToken0 = typeof event.payload.token0 === "string" ? getAddress(event.payload.token0) : null;
        const payloadToken1 = typeof event.payload.token1 === "string" ? getAddress(event.payload.token1) : null;
        if (!payloadToken
          || !payloadToken0
          || !payloadToken1
          || claimed.args.caller.toLowerCase() !== event.signer.toLowerCase()
          || claimed.args.token.toLowerCase() !== payloadToken.toLowerCase()
          || claimed.args.token0.toLowerCase() !== payloadToken0.toLowerCase()
          || claimed.args.token1.toLowerCase() !== payloadToken1.toLowerCase()
          || event.payload.creatorAmount0 !== claimed.args.recipientAmount0.toString()
          || event.payload.creatorAmount1 !== claimed.args.recipientAmount1.toString()) {
          return NextResponse.json({ error: "Fee claim payload or caller does not match the Pons transaction" }, { status: 422 });
        }
      } else if (event.type === "buyback.confirmed") {
        if (receipt.to?.toLowerCase() !== PONS_SWAP_ROUTER.toLowerCase()) {
          return NextResponse.json({ error: "Buyback transaction did not call the Pons swap router" }, { status: 422 });
        }
        const transaction = await client.getTransaction({ hash: event.txHash as `0x${string}` });
        const decoded = decodeFunctionData({ abi: swapRouterAbi, data: transaction.input });
        if (decoded.functionName !== "exactInputSingle") {
          return NextResponse.json({ error: "Buyback transaction is not an exact-input Pons swap" }, { status: 422 });
        }
        const params = decoded.args[0];
        const payloadToken = typeof event.payload.token === "string" ? getAddress(event.payload.token) : null;
        const payloadAmount = typeof event.payload.amountInEth === "string" ? parseEther(event.payload.amountInEth) : null;
        const payloadPoolFee = typeof event.payload.poolFee === "number" ? event.payload.poolFee : null;
        const payloadMinimum = typeof event.payload.minimumAmountOut === "string" ? event.payload.minimumAmountOut : null;
        const payloadAcquired = typeof event.payload.acquiredAmount === "string" ? event.payload.acquiredAmount : null;
        const acquired = receipt.logs
          .filter((log) => log.address.toLowerCase() === payloadToken?.toLowerCase()
            && log.topics[0]?.toLowerCase() === transferTopic.toLowerCase())
          .reduce((total, log) => {
            const transfer = decodeEventLog({ abi: [transferEvent], data: log.data, topics: log.topics });
            return transfer.args.to.toLowerCase() === event.signer.toLowerCase() ? total + transfer.args.value : total;
          }, 0n);
        if (!payloadToken
          || params.tokenIn.toLowerCase() !== PONS_WETH.toLowerCase()
          || params.tokenOut.toLowerCase() !== payloadToken.toLowerCase()
          || params.recipient.toLowerCase() !== event.signer.toLowerCase()
          || payloadAmount !== params.amountIn
          || payloadPoolFee !== params.fee
          || payloadMinimum !== params.amountOutMinimum.toString()
          || payloadAcquired !== acquired.toString()) {
          return NextResponse.json({ error: "Buyback payload does not match the Pons swap transaction" }, { status: 422 });
        }
      }
      verification = "chain";
    }

    const stored = insertAgentEvent(event, verification);
    return NextResponse.json({ event: stored }, { status: 201 });
  } catch (error) {
    const message = error instanceof Error ? error.message : "Invalid Agent event";
    const duplicate = message.includes("UNIQUE constraint failed");
    return NextResponse.json({ error: duplicate ? "Event or sequence already exists" : message }, { status: duplicate ? 409 : 400 });
  }
}
