import "jsr:@supabase/functions-js/edge-runtime.d.ts";
import { createClient } from "jsr:@supabase/supabase-js@2";
import { isVersionRequest, versionResponse } from "../../../_shared/ef-meta/index.ts";

/**
 * cerefox-metadata-search -- Supabase Edge Function
 *
 * Query documents by metadata key-value criteria without a text search term.
 * Calls the cerefox_metadata_search() RPC via the service-role key.
 *
 * Called by:
 *   - GPT Custom Actions (direct HTTP POST via OpenAPI schema)
 *   - Any authenticated HTTP client
 *
 * Note: cerefox-mcp calls the RPC directly (not this Edge Function).
 *
 * Request body (JSON):
 *   metadata_filter  object       optional  Key-value pairs (AND semantics)
 *   project_id       string       optional  Project UUID filter
 *
 * At least one of metadata_filter / project_id / updated_since / created_since
 * must be supplied (an empty filter + project_id lists that project's docs).
 *   updated_since    string       optional  ISO-8601 lower bound for updated_at
 *   created_since    string       optional  ISO-8601 lower bound for created_at
 *   limit            number       optional  Max results (default: 10)
 *   include_content  boolean      optional  Include full text (default: false)
 *   max_bytes        number       optional  Byte budget when include_content=true
 *
 * Response (200): Array of matching documents
 * Response (400): { error: "..." }
 */

const MAX_BYTES = 200_000;

const CORS_HEADERS = {
  "Access-Control-Allow-Origin": "*",
  "Access-Control-Allow-Methods": "POST, OPTIONS",
  "Access-Control-Allow-Headers": "Content-Type, Authorization, apikey",
};

Deno.serve(async (req: Request): Promise<Response> => {
  if (req.method === "OPTIONS") {
    return new Response(null, { status: 200, headers: CORS_HEADERS });
  }

  if (isVersionRequest(req)) {
    return versionResponse("cerefox-metadata-search", { ...CORS_HEADERS, "Content-Type": "application/json" });
  }

  if (req.method !== "POST") {
    return new Response("Method Not Allowed", { status: 405, headers: CORS_HEADERS });
  }

  try {
    const body = await req.json();
    const metadata_filter = body.metadata_filter;

    if (
      metadata_filter !== undefined &&
      metadata_filter !== null &&
      (typeof metadata_filter !== "object" || Array.isArray(metadata_filter))
    ) {
      return new Response(
        JSON.stringify({ error: "metadata_filter must be a JSON object when provided" }),
        { status: 400, headers: { ...CORS_HEADERS, "Content-Type": "application/json" } },
      );
    }

    const project_id = body.project_id ?? null;
    const updated_since = body.updated_since ?? null;
    const created_since = body.created_since ?? null;

    // metadata_filter is optional, but at least one narrowing criterion is
    // required so this never becomes an unbounded whole-KB dump. An empty
    // filter + project_id lists a project's documents (the RPC's
    // `metadata @> '{}'` matches every row; the project predicate narrows it).
    const has_metadata =
      metadata_filter && typeof metadata_filter === "object" &&
      Object.keys(metadata_filter).length > 0;
    if (!has_metadata && !project_id && !updated_since && !created_since) {
      return new Response(
        JSON.stringify({
          error: "Provide at least one of: metadata_filter, project_id, updated_since, or created_since.",
        }),
        { status: 400, headers: { ...CORS_HEADERS, "Content-Type": "application/json" } },
      );
    }
    const limit = body.limit ?? 10;
    const include_content = body.include_content ?? false;
    const requested_max_bytes = body.max_bytes;

    const max_bytes = include_content
      ? Math.min(requested_max_bytes ?? MAX_BYTES, MAX_BYTES)
      : null;

    const supabaseUrl = Deno.env.get("SUPABASE_URL")!;
    const supabaseKey = Deno.env.get("SUPABASE_SERVICE_ROLE_KEY")!;
    const supabase = createClient(supabaseUrl, supabaseKey);

    // Configurable requestor enforcement
    const identityField = "requestor";
    const identityValue = body[identityField];
    const { data: reqConfig } = await supabase.rpc("cerefox_get_config", { p_key: "require_requestor_identity" });
    if (reqConfig === "true") {
      if (!identityValue || (typeof identityValue === "string" && identityValue.trim() === "")) {
        return new Response(
          JSON.stringify({ error: `Missing required parameter "${identityField}". Server requires caller identity.` }),
          { status: 400, headers: { ...CORS_HEADERS, "Content-Type": "application/json" } },
        );
      }
      const { data: fmtConfig } = await supabase.rpc("cerefox_get_config", { p_key: "requestor_identity_format" });
      if (fmtConfig && typeof fmtConfig === "string" && fmtConfig.trim() !== "") {
        if (!new RegExp(fmtConfig).test(identityValue)) {
          return new Response(
            JSON.stringify({ error: `Invalid "${identityField}" format. Does not match pattern: ${fmtConfig}` }),
            { status: 400, headers: { ...CORS_HEADERS, "Content-Type": "application/json" } },
          );
        }
      }
    }

    const params: Record<string, unknown> = {
      p_metadata_filter: has_metadata ? metadata_filter : {},
      p_project_id: project_id,
      p_updated_since: updated_since,
      p_created_since: created_since,
      p_limit: limit,
      p_include_content: include_content,
    };
    if (max_bytes !== null) {
      params.p_max_bytes = max_bytes;
    }

    const { data, error } = await supabase.rpc("cerefox_metadata_search", params);

    if (error) {
      return new Response(JSON.stringify({ error: error.message }), {
        status: 500,
        headers: { ...CORS_HEADERS, "Content-Type": "application/json" },
      });
    }

    // Fire-and-forget usage logging
    Promise.resolve(supabase.rpc("cerefox_log_usage", {
      p_operation: "metadata_search",
      p_access_path: "edge-function",
      p_requestor: body.requestor ?? null,
      p_query_text: JSON.stringify(metadata_filter),
      p_result_count: (data ?? []).length,
      p_project_id: project_id,
    })).catch(() => {});

    return new Response(JSON.stringify(data ?? []), {
      status: 200,
      headers: { ...CORS_HEADERS, "Content-Type": "application/json" },
    });
  } catch (err) {
    const message = err instanceof Error ? err.message : String(err);
    return new Response(JSON.stringify({ error: message }), {
      status: 500,
      headers: { ...CORS_HEADERS, "Content-Type": "application/json" },
    });
  }
});
