import { randomUUID } from "node:crypto";
import { isIP } from "node:net";
import { prisma } from "@/lib/prisma";

type HeaderReader = {
  get(name: string): string | null;
};

const REQUEST_ID_HEADERS = ["x-request-id", "x-correlation-id", "x-trace-id"];
const ACTOR_HEADERS = ["x-agent-id", "x-agent-name", "x-ai-agent-id", "x-ai-id"];
const AI_USER_AGENT_HINTS = [
  "gpt",
  "chatgpt",
  "openai",
  "anthropic",
  "claude",
  "perplexity",
  "llm",
  "mcp",
  "autogpt",
  "agent",
  "assistant",
  "gemini",
  "cursor",
  "copilot",
  "aider",
  "openhands",
  "openclaw",
  "hermes",
];

export function normalizeA2aActorId(value: string | null | undefined, fallback = "agent") {
  const normalized = (value || fallback)
    .trim()
    .toLowerCase()
    .replace(/[^a-z0-9._:-]+/g, "_")
    .replace(/_+/g, "_")
    .slice(0, 80);
  return normalized || fallback;
}

export function asA2aAgentActorId(value: string | null | undefined, fallback = "agent") {
  const normalized = normalizeA2aActorId(value, fallback);
  return normalized.startsWith("agent:") ? normalized : `agent:${normalized}`;
}

function sanitizeIpAddress(value: string | null | undefined) {
  if (!value) return "unknown";
  const first = value.split(",")[0]?.trim();
  if (!first) return "unknown";

  const bracketedMatch = first.match(/^\[(.+)\]:(\d+)$/);
  if (bracketedMatch?.[1]) return bracketedMatch[1].toLowerCase();

  const ipv4WithPortMatch = first.match(/^(\d{1,3}(?:\.\d{1,3}){3}):\d+$/);
  if (ipv4WithPortMatch?.[1]) return ipv4WithPortMatch[1];

  return first.toLowerCase();
}

export function resolveA2aSourceIp(headers: HeaderReader) {
  return sanitizeIpAddress(
    headers.get("x-forwarded-for") ??
      headers.get("x-real-ip") ??
      headers.get("cf-connecting-ip") ??
      headers.get("true-client-ip") ??
      headers.get("x-client-ip") ??
      "unknown"
  );
}

export function isPrivateA2aSourceIp(ip: string | undefined) {
  if (!ip) return true;
  const normalized = ip.trim().toLowerCase();
  if (!normalized || normalized === "unknown" || normalized === "localhost") return true;
  if (normalized === "::1" || normalized.startsWith("fe80")) return true;
  if (isIP(normalized) === 6 && (normalized.startsWith("fc") || normalized.startsWith("fd"))) return true;
  if (isIP(normalized) === 0) return false;
  if (normalized.startsWith("127.") || normalized.startsWith("10.") || normalized.startsWith("192.168.")) return true;
  if (normalized.startsWith("172.")) {
    const secondOctet = Number(normalized.split(".")[1]);
    return secondOctet >= 16 && secondOctet <= 31;
  }
  return false;
}

function resolveRequestId(headers: HeaderReader) {
  for (const headerName of REQUEST_ID_HEADERS) {
    const value = headers.get(headerName);
    if (value) return value;
  }
  return randomUUID();
}

function resolveHeaderActor(headers: HeaderReader) {
  for (const headerName of ACTOR_HEADERS) {
    const value = headers.get(headerName);
    if (value) return asA2aAgentActorId(value);
  }
  return null;
}

function isLikelyAiUserAgent(userAgent: string) {
  const normalized = userAgent.toLowerCase();
  return AI_USER_AGENT_HINTS.some((token) => normalized.includes(token));
}

export function resolveA2aTrafficContext(headers: HeaderReader, fallbackAgentId?: string | null) {
  const sourceIp = resolveA2aSourceIp(headers);
  const userAgent = headers.get("user-agent") || "unknown";
  const headerActorId = resolveHeaderActor(headers);
  const actorId =
    fallbackAgentId
      ? asA2aAgentActorId(fallbackAgentId)
      : headerActorId || (isLikelyAiUserAgent(userAgent) ? asA2aAgentActorId(userAgent) : `external:${sourceIp}`);

  return {
    request_id: resolveRequestId(headers),
    source_ip: sourceIp,
    user_agent: userAgent,
    is_public_ip: !isPrivateA2aSourceIp(sourceIp),
    actor_type: actorId.startsWith("agent:") ? ("AGENT" as const) : ("USER" as const),
    actor_id: actorId,
    request_actor_headers: {
      x_agent_id: headers.get("x-agent-id"),
      x_agent_name: headers.get("x-agent-name"),
      x_ai_agent_id: headers.get("x-ai-agent-id"),
      x_ai_id: headers.get("x-ai-id"),
      x_request_id: headers.get("x-request-id"),
      origin: headers.get("origin"),
      referer: headers.get("referer"),
    },
  };
}

export async function logA2aTrafficEvent(params: {
  headers: HeaderReader;
  fallbackAgentId?: string | null;
  action: string;
  surface: string;
  entityType?: "TASK" | "CLAIM" | "SUBMISSION" | "JUDGE_RESULT" | "SYSTEM";
  entityId: string;
  reason?: string;
  metadata?: Record<string, unknown>;
}) {
  const context = resolveA2aTrafficContext(params.headers, params.fallbackAgentId);

  return prisma.auditEvent.create({
    data: {
      actorType: context.actor_type,
      actorId: context.actor_id,
      action: params.action,
      entityType: params.entityType || "SYSTEM",
      entityId: params.entityId,
      reason: params.reason,
      metadata: {
        ...params.metadata,
        request_id: context.request_id,
        source_ip: context.source_ip,
        user_agent: context.user_agent,
        is_public_ip: context.is_public_ip,
        surface: params.surface,
        request_actor_headers: context.request_actor_headers,
      },
    },
  });
}
