/**
 * OtterlyAI — AI agent access logger for Netlify
 *
 * Streams AI-agent access logs from this Netlify site to OtterlyAI Analytics.
 *
 * External services: sends metadata of AI-agent requests (requested URL,
 * matched User-Agent, timestamp, referer) to OtterlyAI Analytics for every
 * request whose User-Agent matches a known AI agent. Regular visitor traffic
 * is never sent. No client IP address is collected or transmitted.
 *   Terms:   https://otterly.ai/terms
 *   Privacy: https://otterly.ai/privacy
 *
 * Install:
 *   1. Save as netlify/edge-functions/otterly-agent-log.ts
 *   2. Set OTTERLYAI_API_KEY in Site configuration -> Environment variables
 *   3. Redeploy
 */

import type { Config, Context } from "@netlify/edge-functions";

const INGEST_URL =
  Netlify.env.get("OTTERLYAI_INGEST_URL") || "https://analytics.otterly.ai/logs";
const API_KEY = Netlify.env.get("OTTERLYAI_API_KEY");

/**
 * Canonical AI-bot signatures — kept in sync with OtterlyAI's BOT_TO_SERVICE
 * classifier. Matching is case-insensitive substring.
 */
const BOT_SIGNATURES = [
  "GPTBot",
  "ChatGPT-User",
  "OAI-SearchBot",
  "ClaudeBot",
  "Claude-Web",
  "Claude-SearchBot",
  "Claude-User",
  "Claude-Code",
  "anthropic-ai",
  "PerplexityBot",
  "Perplexity-User",
  "GoogleOther",
  "Google-CloudVertexBot",
  "Google-Agent",
  "GoogleAgent-Mariner",
  "Gemini-Deep-Research",
  "Google-Extended",
  "Applebot-Extended",
  "Amazonbot",
  "Amzn-SearchBot",
  "NovaAct",
  "AzureAI-SearchBot",
  "FacebookBot",
  "Meta-ExternalAgent",
  "Meta-ExternalFetcher",
  "meta-webindexer",
  "GrokBot",
  "Grok-DeepSearch",
  "xAI-Grok",
  "DeepSeekBot",
  "MistralAI-User",
  "cohere-ai",
  "cohere-training-data-crawler",
  "PanguBot",
  "Ai2Bot-Dolma",
  "Ai2Bot",
  "DuckAssistBot",
  "YouBot",
  "quillbot.com",
  "Bytespider",
  "CCBot",
  "Diffbot",
  "ImagesiftBot",
  "Webzio-Extended",
  "Omgilibot",
  "omgili",
  "Timpibot",
  "Manus-User",
  "MyCentralAIScraperBot",
];

// Escape regex metacharacters — "quillbot.com" must match a literal dot.
// Built once at module scope; Deno reuses the isolate across requests.
const AI_AGENT_RE = new RegExp(
  BOT_SIGNATURES.map((s) => s.replace(/[.*+?^${}()|[\]\\]/g, "\\$&")).join("|"),
  "i",
);

export default async (request: Request, context: Context) => {
  if (!API_KEY) {
    console.warn("[OtterlyAI] OTTERLYAI_API_KEY not set — skipping ingest");
    return;
  }

  const userAgent = request.headers.get("user-agent") ?? "";
  if (userAgent === "" || !AI_AGENT_RE.test(userAgent)) {
    // Fast path: bypass without touching the response. ~99% of traffic.
    return;
  }

  console.log(`[OtterlyAI] AI agent matched: "${userAgent}" -> ${request.url}`);

  const response = await context.next();

  // Array-of-one shape — the /logs ingest expects an array.
  // No client IP is collected, keeping the payload GDPR-friendly.
  // request.url is already the intact, absolute, client-facing URL.
  const payload = [
    {
      requestUrl: request.url,
      userAgent,
      timestamp: new Date().toISOString(), // ISO-8601 UTC with milliseconds
      referer: request.headers.get("referer") || null,
    },
  ];

  // Fire-and-forget: waitUntil keeps the isolate alive past the response
  // without blocking it, and ingest failures never surface to the visitor.
  context.waitUntil(
    fetch(INGEST_URL, {
      method: "POST",
      headers: {
        "Content-Type": "application/json",
        Authorization: `Bearer ${API_KEY}`,
      },
      body: JSON.stringify(payload),
    })
      .then((res) =>
        console.log(`[OtterlyAI] ingest POST ${INGEST_URL} -> ${res.status}`),
      )
      .catch((err) =>
        console.error(`[OtterlyAI] ingest failed: ${err}`),
      ),
  );

  return response;
};

export const config: Config = {
  path: "/*",
  // Static assets only. robots.txt, llms.txt, sitemap.xml and feeds are
  // deliberately NOT excluded — they are high-value AI-crawler signals.
  excludedPath: [
    "/assets/*",
    "/static/*",
    "/_next/static/*",
    "/_nuxt/*",
    "/_astro/*",
    "/build/*",
    "/*.css",
    "/*.js",
    "/*.mjs",
    "/*.map",
    "/*.woff",
    "/*.woff2",
    "/*.ttf",
    "/*.png",
    "/*.jpg",
    "/*.jpeg",
    "/*.gif",
    "/*.webp",
    "/*.avif",
    "/*.svg",
    "/*.ico",
    "/*.mp4",
    "/*.webm",
  ],
};
