import type { SupabaseClient } from "@supabase/supabase-js";
import type { Tables } from "@xtablo/shared-types";

type ClientRow = Tables<"clients">;

export function normalizeClientEmail(email: string) {
  return email.trim().toLowerCase();
}

export async function upsertClientByEmail(supabase: SupabaseClient, email: string) {
  const normalizedEmail = normalizeClientEmail(email);

  const { data: existingClient, error: existingClientError } = await supabase
    .from("clients")
    .select("*")
    .eq("normalized_email", normalizedEmail)
    .maybeSingle();

  if (existingClientError) {
    return { client: null, error: existingClientError.message, wasCreated: false };
  }

  if (existingClient) {
    return { client: existingClient as ClientRow, error: null, wasCreated: false };
  }

  const { data: insertedClient, error: insertError } = await supabase
    .from("clients")
    .insert({
      email: normalizedEmail,
      normalized_email: normalizedEmail,
    })
    .select("*")
    .single();

  if (insertError) {
    return { client: null, error: insertError.message, wasCreated: false };
  }

  return { client: insertedClient as ClientRow, error: null, wasCreated: true };
}

export async function ensureActiveClientAccess(
  supabase: SupabaseClient,
  input: {
    clientId: string;
    grantedBy: string;
    tabloId: string;
  }
) {
  const { data: existingAccess, error: existingAccessError } = await supabase
    .from("client_access")
    .select("id, revoked_at")
    .eq("client_id", input.clientId)
    .eq("tablo_id", input.tabloId)
    .maybeSingle();

  if (existingAccessError) {
    return { error: existingAccessError.message, success: false };
  }

  if (!existingAccess) {
    const { error: insertError } = await supabase.from("client_access").insert({
      client_id: input.clientId,
      granted_by: input.grantedBy,
      tablo_id: input.tabloId,
    });

    return { error: insertError?.message ?? null, success: !insertError };
  }

  if (existingAccess.revoked_at) {
    const { error: updateError } = await supabase
      .from("client_access")
      .update({
        granted_at: new Date().toISOString(),
        granted_by: input.grantedBy,
        revoked_at: null,
      })
      .eq("id", existingAccess.id);

    return { error: updateError?.message ?? null, success: !updateError };
  }

  return { error: null, success: true };
}

export async function clientHasAnyActiveAccess(supabase: SupabaseClient, clientId: string) {
  const { count, error } = await supabase
    .from("client_access")
    .select("id", { count: "exact", head: true })
    .eq("client_id", clientId)
    .is("revoked_at", null);

  if (error) {
    return { error: error.message, hasActiveAccess: false };
  }

  return { error: null, hasActiveAccess: Boolean(count && count > 0) };
}

export async function clientHasTabloAccess(
  supabase: SupabaseClient,
  input: { clientId: string; tabloId: string }
) {
  const { data, error } = await supabase
    .from("client_access")
    .select("id")
    .eq("client_id", input.clientId)
    .eq("tablo_id", input.tabloId)
    .is("revoked_at", null)
    .maybeSingle();

  if (error) {
    return { error: error.message, hasAccess: false };
  }

  return { error: null, hasAccess: Boolean(data) };
}

export async function getActiveClientAccessTabloIds(supabase: SupabaseClient, clientId: string) {
  const { data, error } = await supabase
    .from("client_access")
    .select("tablo_id")
    .eq("client_id", clientId)
    .is("revoked_at", null);

  if (error) {
    return { error: error.message, tabloIds: [] as string[] };
  }

  return {
    error: null,
    tabloIds: (data ?? []).map((row) => row.tablo_id),
  };
}

export async function revokeClientAccess(
  supabase: SupabaseClient,
  input: { clientId: string; tabloId: string }
) {
  const { error } = await supabase
    .from("client_access")
    .update({ revoked_at: new Date().toISOString() })
    .eq("client_id", input.clientId)
    .eq("tablo_id", input.tabloId)
    .is("revoked_at", null);

  return { error: error?.message ?? null, success: !error };
}
