import { IS_TEST_BUILD } from "@/ipc/utils/test_utils";
import { retryWithRateLimit } from "@/ipc/utils/retryWithRateLimit";
import { getSupabaseClient } from "./supabase_management_client";
import {
  SUPABASE_SCHEMA_QUERY,
  SUPABASE_FUNCTIONS_QUERY,
  buildSupabaseSchemaQuery,
} from "./supabase_schema_query";

async function getPublishableKey({
  projectId,
  organizationSlug,
}: {
  projectId: string;
  organizationSlug: string | null;
}) {
  if (IS_TEST_BUILD) {
    return "test-publishable-key";
  }

  const supabase = await getSupabaseClient({ organizationSlug });
  let keys;
  try {
    keys = await retryWithRateLimit(
      () => supabase.getProjectApiKeys(projectId),
      `Get API keys for ${projectId}`,
    );
  } catch (error) {
    throw new Error(
      `Failed to fetch API keys for Supabase project "${projectId}". This could be due to: 1) Invalid project ID, 2) Network connectivity issues, or 3) Supabase API unavailability. Original error: ${error instanceof Error ? error.message : String(error)}`,
    );
  }
  if (!keys) {
    throw new Error("No keys found for Supabase project " + projectId);
  }
  const publishableKey = keys.find(
    (key) =>
      (key as any)["name"] === "anon" || (key as any)["type"] === "publishable",
  );

  if (!publishableKey) {
    throw new Error(
      "No publishable key found for project. Make sure you are connected to the correct Supabase account and project. See https://dyad.sh/docs/integrations/supabase#no-publishable-keys",
    );
  }
  return publishableKey.api_key;
}
export const getSupabaseClientCode = async function ({
  projectId,
  organizationSlug,
}: {
  projectId: string;
  organizationSlug: string | null;
}) {
  const publishableKey = await getPublishableKey({
    projectId,
    organizationSlug,
  });
  return `
// This file is automatically generated. Do not edit it directly.
import { createClient } from '@supabase/supabase-js';

const SUPABASE_URL = "https://${projectId}.supabase.co";
const SUPABASE_PUBLISHABLE_KEY = "${publishableKey}";

// Import the supabase client like this:
// import { supabase } from "@/integrations/supabase/client";

export const supabase = createClient(SUPABASE_URL, SUPABASE_PUBLISHABLE_KEY);`;
};

export async function getSupabaseContext({
  supabaseProjectId,
  organizationSlug,
}: {
  supabaseProjectId: string;
  organizationSlug: string | null;
}) {
  if (IS_TEST_BUILD) {
    if (supabaseProjectId === "test-branch-project-id") {
      return "1234".repeat(200_000);
    }
    return "[[TEST_BUILD_SUPABASE_CONTEXT]]";
  }

  const supabase = await getSupabaseClient({ organizationSlug });
  const publishableKey = await getPublishableKey({
    projectId: supabaseProjectId,
    organizationSlug,
  });
  const schema = await retryWithRateLimit(
    () => supabase.runQuery(supabaseProjectId, SUPABASE_SCHEMA_QUERY),
    `Get schema for ${supabaseProjectId}`,
  );

  const secrets = await retryWithRateLimit(
    () => supabase.getSecrets(supabaseProjectId),
    `Get secrets for ${supabaseProjectId}`,
  );
  const secretNames = secrets?.map((secret) => secret.name);

  // TODO: include EDGE FUNCTIONS and SECRETS!

  const context = `
  # Supabase Context

  ## Supabase Project ID
  ${supabaseProjectId}

  ## Publishable key (aka anon key)
  ${publishableKey}

  ## Secret names (environmental variables)
  ${JSON.stringify(secretNames)}

  ## Schema
  ${JSON.stringify(schema)}
  `;

  return context;
}

// Query to get just table names (lightweight)
const TABLE_NAMES_QUERY = `
  SELECT table_name 
  FROM information_schema.tables 
  WHERE table_schema = 'public'
  ORDER BY table_name;
`;

/**
 * Get high-level Supabase project info: project ID, publishable key, secret names, and table names.
 * This is a lightweight call that doesn't fetch full schema details.
 * Optionally includes database functions when include_db_functions is true.
 */
export async function getSupabaseProjectInfo({
  supabaseProjectId,
  organizationSlug,
  includeDbFunctions,
}: {
  supabaseProjectId: string;
  organizationSlug: string | null;
  includeDbFunctions?: boolean;
}): Promise<string> {
  if (IS_TEST_BUILD) {
    let result = `# Supabase Project Info

## Project ID
${supabaseProjectId}

## Publishable Key
test-publishable-key

## Secret Names
["TEST_SECRET_1", "TEST_SECRET_2"]

## Table Names
["users", "posts", "comments"]
`;
    if (includeDbFunctions) {
      result += `
## Database Functions
[{"name": "test_function", "arguments": "", "return_type": "void", "language": "plpgsql"}]
`;
    }
    return result;
  }

  const supabase = await getSupabaseClient({ organizationSlug });
  const publishableKey = await getPublishableKey({
    projectId: supabaseProjectId,
    organizationSlug,
  });

  const secrets = await retryWithRateLimit(
    () => supabase.getSecrets(supabaseProjectId),
    `Get secrets for ${supabaseProjectId}`,
  );
  const secretNames = secrets?.map((secret) => secret.name) ?? [];

  const tableResult = await retryWithRateLimit(
    () => supabase.runQuery(supabaseProjectId, TABLE_NAMES_QUERY),
    `Get table names for ${supabaseProjectId}`,
  );
  const tableNames =
    (tableResult as unknown as { table_name: string }[] | undefined)?.map(
      (row) => row.table_name,
    ) ?? [];

  let result = `# Supabase Project Info

## Project ID
${supabaseProjectId}

## Publishable Key
${publishableKey}

## Secret Names
${JSON.stringify(secretNames)}

## Table Names
${JSON.stringify(tableNames)}
`;

  if (includeDbFunctions) {
    const functionsResult = await retryWithRateLimit(
      () => supabase.runQuery(supabaseProjectId, SUPABASE_FUNCTIONS_QUERY),
      `Get DB functions for ${supabaseProjectId}`,
    );
    result += `
## Database Functions
${JSON.stringify(functionsResult)}
`;
  }

  return result;
}

/**
 * Get database table schema. If tableName is provided, returns schema for that specific table.
 * If tableName is omitted, returns schema for all tables.
 */
export async function getSupabaseTableSchema({
  supabaseProjectId,
  organizationSlug,
  tableName,
}: {
  supabaseProjectId: string;
  organizationSlug: string | null;
  tableName?: string;
}): Promise<string> {
  if (IS_TEST_BUILD) {
    return `[[TEST_TABLE_SCHEMA${tableName ? `:${tableName}` : ""}]]`;
  }

  const supabase = await getSupabaseClient({ organizationSlug });
  const query = buildSupabaseSchemaQuery(tableName);
  const schemaResult = await retryWithRateLimit(
    () => supabase.runQuery(supabaseProjectId, query),
    `Get table schema for ${supabaseProjectId}${tableName ? `:${tableName}` : ""}`,
  );

  return JSON.stringify(schemaResult);
}
