const BASE_URL =
  process.env.NEXT_PUBLIC_ATHENA_BASE_URL ?? "https://athena-db.com";
const DEFAULT_DB_NAME = process.env.NEXT_PUBLIC_ATHENA_DB_NAME ?? "postgres";
const DEFAULT_CLIENT = process.env.NEXT_PUBLIC_ATHENA_CLIENT ?? "supabase";
const DEFAULT_HEADERS: Record<string, string> = {
  "Content-Type": "application/json",
  "X-Athena-Client": DEFAULT_CLIENT,
};

export type TableMetadata = {
  table_name: string;
  table_schema?: string;
};

export type ColumnMetadata = {
  column_name: string;
  data_type?: string | null;
  column_default?: string | null;
  is_nullable?: string;
};

export type RowValues = Record<string, unknown>;

type ApiOptions = {
  driver?: "athena" | "postgresql" | "supabase";
  dbName?: string;
  clientName?: string;
};

async function executeApiRequest<T>(
  path: string,
  init: RequestInit,
  errorMessage: string,
): Promise<T> {
  const response = await fetch(path, init);
  const body = await response.json().catch(() => null);

  if (!response.ok) {
    const normalizedMessage =
      (body && typeof body === "object" && "error" in body
        ? (body as Record<string, unknown>).error
        : null) ??
      (body && typeof body === "object" && "message" in body
        ? (body as Record<string, unknown>).message
        : null) ??
      errorMessage ??
      "Request failed";
    throw new Error(String(normalizedMessage));
  }

  return body;
}

function buildHeaders(
  headers?: Record<string, string>,
  clientName?: string,
) {
  const result: Record<string, string> = {
    ...DEFAULT_HEADERS,
  };
  if (clientName) {
    result["X-Athena-Client"] = clientName;
  }
  for (const [key, value] of Object.entries(headers ?? {})) {
    if (value && value.length > 0) {
      result[key] = value;
    }
  }
  return result;
}

async function runSql<T = unknown>(
  query: string,
  options?: ApiOptions,
): Promise<T> {
  const payload = {
    query,
    driver: options?.driver ?? "supabase",
    db_name: options?.dbName ?? DEFAULT_DB_NAME,
  };

  const result = await executeApiRequest<unknown>(
    `${BASE_URL}/query/sql`,
    {
      method: "POST",
      headers: buildHeaders(undefined, options?.clientName),
      body: JSON.stringify(payload),
    },
    "Failed to execute SQL",
  );

  if (Array.isArray(result)) {
    return result as T;
  }

  if (result && typeof result === "object") {
    if ("data" in result) {
      const data = (result as Record<string, unknown>).data;
      if (Array.isArray(data)) {
        return data as T;
      }
      if (data && typeof data === "object" && "rows" in (data as object)) {
        return (data as Record<string, unknown>).rows as T;
      }
      return data as T;
    }
  }

  return result as T;
}

async function fetchFromGateway<T>(
  path: string,
  init: RequestInit,
  errorMessage: string,
): Promise<T> {
  return executeApiRequest<T>(`${BASE_URL}${path}`, init, errorMessage);
}

export async function fetchClients(): Promise<string[]> {
  const response = await executeApiRequest<{ clients?: string[] }>(
    `${BASE_URL}/schema/clients`,
    {
      method: "GET",
      headers: buildHeaders(),
    },
    "Failed to fetch clients",
  );

  if (response && typeof response === "object" && "clients" in response) {
    return Array.isArray(response.clients) ? response.clients : [];
  }

  return [];
}

export async function fetchTables(
  clientName?: string,
): Promise<TableMetadata[]> {
  const resolvedClient = clientName ?? DEFAULT_CLIENT;
  const response = await executeApiRequest<{ tables?: TableMetadata[] }>(
    `${BASE_URL}/schema/tables`,
    {
      method: "GET",
      headers: buildHeaders(undefined, resolvedClient),
    },
    `Failed to fetch tables for ${resolvedClient}`,
  );

  if (response && typeof response === "object" && "tables" in response) {
    return Array.isArray(response.tables) ? response.tables : [];
  }

  return [];
}

export async function fetchColumns(
  tableName: string,
  clientName?: string,
): Promise<ColumnMetadata[]> {
  const resolvedClient = clientName ?? DEFAULT_CLIENT;
  const response = await executeApiRequest<{ columns?: ColumnMetadata[] }>(
    `${BASE_URL}/schema/columns?table_name=${encodeURIComponent(tableName)}`,
    {
      method: "GET",
      headers: buildHeaders(undefined, resolvedClient),
    },
    `Failed to fetch columns for ${tableName}`,
  );

  if (response && typeof response === "object" && "columns" in response) {
    return Array.isArray(response.columns) ? response.columns : [];
  }

  return [];
}

export async function fetchRows(
  tableName: string,
  columns: string[] = ["*"],
  clientName?: string,
): Promise<RowValues[]> {
  const body = {
    table_name: tableName,
    columns,
  };
  const resolvedClient = clientName ?? DEFAULT_CLIENT;
  const response = await fetchFromGateway<{
    data?: RowValues[];
    rows?: RowValues[];
  }>(
    "/gateway/fetch",
    {
      method: "POST",
      headers: buildHeaders(undefined, resolvedClient),
      body: JSON.stringify(body),
    },
    `Failed to fetch rows for ${tableName}`,
  );

  const payload = response.data ?? response.rows;
  if (Array.isArray(payload)) {
    return payload;
  }
  return [];
}

function getAuthHeaders() {
  return {
    "X-User-Id": process.env.NEXT_PUBLIC_ATHENA_USER_ID ?? "",
    "X-Company-Id": process.env.NEXT_PUBLIC_ATHENA_COMPANY_ID ?? "",
    "X-Organization-Id":
      process.env.NEXT_PUBLIC_ATHENA_ORGANIZATION_ID ?? "",
  };
}

export async function insertRow(
  tableName: string,
  insertBody: RowValues,
  clientName?: string,
): Promise<RowValues> {
  const resolvedClient = clientName ?? DEFAULT_CLIENT;
  const headers = buildHeaders(getAuthHeaders(), resolvedClient);

  const response = await fetchFromGateway<{ data?: RowValues }>(
    "/gateway/insert",
    {
      method: "PUT",
      headers,
      body: JSON.stringify({ table_name: tableName, insert_body: insertBody }),
    },
    `Failed to insert row into ${tableName}`,
  );

  if (response && typeof response === "object" && "data" in response) {
    return response.data ?? insertBody;
  }

  return insertBody;
}

export async function deleteRow(
  tableName: string,
  resourceId: string,
  clientName?: string,
): Promise<void> {
  const resolvedClient = clientName ?? DEFAULT_CLIENT;
  const headers = buildHeaders(getAuthHeaders(), resolvedClient);

  await fetchFromGateway(
    "/gateway/delete",
    {
      method: "DELETE",
      headers,
      body: JSON.stringify({
        table_name: tableName,
        resource_id: resourceId,
      }),
    },
    `Failed to delete row from ${tableName}`,
  );
}

export async function runDdl(
  statement: string,
  clientName?: string,
): Promise<void> {
  await runSql(statement, { driver: "supabase", clientName });
}
