import { keepPreviousData, useInfiniteQuery, useQuery, useQueryClient, UseQueryResult } from "@tanstack/react-query";
import { Team } from "@/components/key_team_helpers/key_list";
import useAuthorized from "@/app/(dashboard)/hooks/useAuthorized";
import { fetchTeams } from "@/app/(dashboard)/networking";
import { createQueryKeys } from "@/app/(dashboard)/hooks/common/queryKeysFactory";
import { teamInfoCall } from "@/components/networking";
import { getProxyBaseUrl, getGlobalLitellmHeaderName, deriveErrorMessage, handleError } from "@/components/networking";
import { teamListScopeUserId } from "@/utils/roles";

export interface TeamsResponse {
  teams: Team[];
  total: number;
  page: number;
  page_size: number;
  total_pages: number;
}

export interface DeletedTeam extends Team {
  deleted_at: string;
  deleted_by: string;
}

export interface TeamListCallOptions {
  organizationID?: string | null;
  teamID?: string | null;
  team_alias?: string | null;
  search?: string | null;
  searchTeamIdMatch?: "exact" | "prefix" | null;
  userID?: string | null;
  sortBy?: string | null;
  sortOrder?: string | null;
  status?: string | null;
}

export const teamListCall = async (
  accessToken: string,
  page: number,
  pageSize: number,
  options: TeamListCallOptions = {},
) => {
  /**
   * Get all available teams on proxy
   */
  try {
    const baseUrl = getProxyBaseUrl();

    const params = new URLSearchParams(
      Object.entries({
        team_id: options.teamID,
        organization_id: options.organizationID,
        team_alias: options.team_alias,
        search: options.search,
        search_team_id_match: options.searchTeamIdMatch,
        user_id: options.userID,
        page,
        page_size: pageSize,
        sort_by: options.sortBy,
        sort_order: options.sortOrder,
        status: options.status,
      })
        .filter(([, value]) => value !== undefined && value !== null)
        .map(([key, value]) => [key, String(value)]),
    );

    const url = `${baseUrl ? `${baseUrl}/v2/team/list` : "/v2/team/list"}?${params}`;

    const response = await fetch(url, {
      method: "GET",
      headers: {
        [getGlobalLitellmHeaderName()]: `Bearer ${accessToken}`,
        "Content-Type": "application/json",
      },
    });

    if (!response.ok) {
      const errorData = await response.json();
      const errorMessage = deriveErrorMessage(errorData);
      handleError(errorMessage);
      throw new Error(errorMessage);
    }

    const data = await response.json();
    return data;
  } catch (error) {
    console.error("Failed to list teams:", error);
    throw error;
  }
};

export const teamsTableKeys = createQueryKeys("teamsTable");

export const useTeamsTable = (
  page: number,
  pageSize: number,
  options: TeamListCallOptions = {},
): UseQueryResult<TeamsResponse> => {
  const { accessToken } = useAuthorized();

  return useQuery<TeamsResponse>({
    queryKey: teamsTableKeys.list({ page, limit: pageSize, ...options }),
    queryFn: async () => await teamListCall(accessToken!, page, pageSize, options),
    enabled: Boolean(accessToken),
    staleTime: 30000,
    placeholderData: keepPreviousData,
  });
};

const teamKeys = createQueryKeys("teams");
export const useTeams = (): UseQueryResult<Team[]> => {
  const { accessToken, userId, userRole } = useAuthorized();
  return useQuery<Team[]>({
    queryKey: teamKeys.list({}),
    queryFn: async () => await fetchTeams(accessToken!, userId, userRole, null),
    enabled: Boolean(accessToken),
  });
};

const ALL_TEAMS_PAGE_SIZE = 100;

const fetchAllTeamsPaged = async (accessToken: string, userID: string | null): Promise<Team[]> => {
  const firstPage: TeamsResponse = await teamListCall(accessToken, 1, ALL_TEAMS_PAGE_SIZE, { userID });
  const totalPages = firstPage.total_pages ?? 1;
  if (totalPages <= 1) return firstPage.teams;

  const remainingPages: TeamsResponse[] = await Promise.all(
    Array.from({ length: totalPages - 1 }, (_, i) => teamListCall(accessToken, i + 2, ALL_TEAMS_PAGE_SIZE, { userID })),
  );
  return [firstPage, ...remainingPages].flatMap((page) => page.teams);
};

export const useAllTeams = (): UseQueryResult<Team[]> => {
  const { accessToken, userId, userRole } = useAuthorized();
  const scopedUserID = teamListScopeUserId(userRole, userId);
  return useQuery<Team[]>({
    queryKey: teamKeys.list({
      filters: {
        scope: "all",
        pageSize: ALL_TEAMS_PAGE_SIZE,
        accessToken: accessToken ?? "",
        userID: scopedUserID ?? "",
      },
    }),
    queryFn: async () => await fetchAllTeamsPaged(accessToken!, scopedUserID),
    enabled: Boolean(accessToken),
    staleTime: 30000,
  });
};

export const useTeam = (teamId?: string) => {
  const { accessToken } = useAuthorized();
  const queryClient = useQueryClient();
  return useQuery<Team>({
    queryKey: teamKeys.detail(teamId!),
    enabled: Boolean(accessToken && teamId),

    queryFn: async () => {
      if (!accessToken || !teamId) {
        throw new Error("Missing auth or teamId");
      }

      return teamInfoCall(accessToken, teamId);
    },

    initialData: () => {
      if (!teamId) return undefined;

      const teams = queryClient.getQueryData<Team[]>(teamKeys.list({}));

      return teams?.find((team) => team.team_id === teamId);
    },
  });
};

const infiniteTeamKeys = createQueryKeys("infiniteTeams");

export const useInfiniteTeams = (pageSize: number = 50, search?: string, organizationId?: string | null) => {
  const { accessToken, userId, userRole } = useAuthorized();
  const isAdmin = userRole === "Admin" || userRole === "Admin Viewer";

  return useInfiniteQuery<TeamsResponse>({
    queryKey: infiniteTeamKeys.list({
      filters: {
        pageSize,
        ...(search && { search }),
        ...(organizationId && { organizationId }),
        ...(userId && { userId }),
      },
    }),
    queryFn: async ({ pageParam }) => {
      return await teamListCall(accessToken!, pageParam as number, pageSize, {
        team_alias: search || undefined,
        organizationID: organizationId,
        userID: !isAdmin ? userId : undefined,
      });
    },
    initialPageParam: 1,
    getNextPageParam: (lastPage) => {
      if (lastPage.page < lastPage.total_pages) {
        return lastPage.page + 1;
      }
      return undefined;
    },
    enabled: Boolean(accessToken),
  });
};

const deletedTeamListCall = async (
  accessToken: string,
  page: number,
  pageSize: number,
  options: TeamListCallOptions = {},
) => {
  /**
   * Get deleted teams from proxy
   */
  try {
    const baseUrl = getProxyBaseUrl();

    const params = new URLSearchParams(
      Object.entries({
        team_id: options.teamID,
        organization_id: options.organizationID,
        team_alias: options.team_alias,
        search: options.search,
        search_team_id_match: options.searchTeamIdMatch,
        user_id: options.userID,
        page,
        page_size: pageSize,
        sort_by: options.sortBy,
        sort_order: options.sortOrder,
        status: "deleted",
      })
        .filter(([, value]) => value !== undefined && value !== null)
        .map(([key, value]) => [key, String(value)]),
    );

    const url = `${baseUrl ? `${baseUrl}/v2/team/list` : "/v2/team/list"}?${params}`;

    const response = await fetch(url, {
      method: "GET",
      headers: {
        [getGlobalLitellmHeaderName()]: `Bearer ${accessToken}`,
        "Content-Type": "application/json",
      },
    });

    if (!response.ok) {
      const errorData = await response.json();
      const errorMessage = deriveErrorMessage(errorData);
      handleError(errorMessage);
      throw new Error(errorMessage);
    }

    const data = await response.json();

    // Extract teams array from response if it's wrapped in a response object
    // Otherwise return the data directly if it's already an array
    if (data && typeof data === "object" && "teams" in data) {
      return data.teams as DeletedTeam[];
    }
    return data as DeletedTeam[];
  } catch (error) {
    console.error("Failed to list deleted teams:", error);
    throw error;
  }
};

export const deletedTeamKeys = createQueryKeys("deletedTeams");
export const useDeletedTeams = (
  page: number,
  pageSize: number,
  options: TeamListCallOptions = {},
): UseQueryResult<DeletedTeam[]> => {
  const { accessToken } = useAuthorized();

  return useQuery<DeletedTeam[]>({
    queryKey: deletedTeamKeys.list({ page, limit: pageSize, ...options }),
    queryFn: async () => await deletedTeamListCall(accessToken!, page, pageSize, options),
    enabled: Boolean(accessToken),
    staleTime: 30000, // 30 seconds
    placeholderData: keepPreviousData,
  });
};
