import { useQueryClient } from "@tanstack/react-query";
import { useServerMutation, useServerSuspenseQuery } from "@/lib/tanstack-query/hooks";
import { Match } from "@/features/matches/types";
import {
  getMatchesBetweenTeams,
  getMatchesBetweenPlayers,
  reportMatchScore,
  confirmMatchScore,
  clearMatchReport,
} from "./server";

export const matchKeys = {
  headToHeadTeams: (team1Id: string, team2Id: string) => ['matches', 'headToHead', 'teams', team1Id, team2Id] as const,
  headToHeadPlayers: (player1Id: string, player2Id: string) => ['matches', 'headToHead', 'players', player1Id, player2Id] as const,
};

export const matchQueries = {
  headToHeadTeams: (team1Id: string, team2Id: string) => ({
    queryKey: matchKeys.headToHeadTeams(team1Id, team2Id),
    queryFn: () => getMatchesBetweenTeams({ data: { team1Id, team2Id } }),
  }),
  headToHeadPlayers: (player1Id: string, player2Id: string) => ({
    queryKey: matchKeys.headToHeadPlayers(player1Id, player2Id),
    queryFn: () => getMatchesBetweenPlayers({ data: { player1Id, player2Id } }),
  }),
};

export const useTeamHeadToHead = (team1Id: string, team2Id: string, enabled = true) =>
  useServerSuspenseQuery({
    ...matchQueries.headToHeadTeams(team1Id, team2Id),
    enabled,
  });

export const usePlayerHeadToHead = (player1Id: string, player2Id: string, enabled = true) =>
  useServerSuspenseQuery({
    ...matchQueries.headToHeadPlayers(player1Id, player2Id),
    enabled,
  });

const tournamentsRoot = { queryKey: ["tournaments"] as const };

const reportTeamId = (t: Match["home"]): string | undefined =>
  !t ? undefined : typeof t === "string" ? t : t.id;

const reportTeamPlayerIds = (t: Match["home"]): string[] =>
  t && typeof t !== "string" ? (t.players ?? []).map((p) => p.id) : [];

function patchTournamentsData(
  data: unknown,
  matchId: string,
  patch: (match: Match) => Partial<Match>
): unknown {
  if (!data) return data;
  if (Array.isArray(data)) {
    return data.map((entry) => patchTournamentsData(entry, matchId, patch));
  }
  const tournament = data as { matches?: Match[] };
  if (!Array.isArray(tournament.matches)) return data;

  let changed = false;
  const matches = tournament.matches.map((m) => {
    if (m.id !== matchId) return m;
    changed = true;
    return { ...m, ...patch(m) };
  });
  return changed ? { ...tournament, matches } : data;
}

type MatchVariables = { data: { matchId: string } };
type TournamentsSnapshot = [readonly unknown[], unknown][];

function useOptimisticMatchMutation<TData, TVariables extends MatchVariables>(
  options: Parameters<typeof useServerMutation<TData, TVariables>>[0] & {
    buildPatch: (match: Match, variables: TVariables) => Partial<Match>;
  }
) {
  const queryClient = useQueryClient();
  const { buildPatch, ...mutationOptions } = options;

  return useServerMutation<TData, TVariables>({
    ...mutationOptions,
    onMutate: async (variables) => {
      const { matchId } = variables.data;
      await queryClient.cancelQueries(tournamentsRoot);

      const previous = queryClient.getQueriesData(tournamentsRoot);
      queryClient.setQueriesData(tournamentsRoot, (data: unknown) =>
        patchTournamentsData(data, matchId, (match) => buildPatch(match, variables))
      );

      return { previous };
    },
    onError: (error, variables, onMutateResult, context) => {
      if (context && typeof context === "object" && "previous" in context) {
        const snapshot = (context as { previous: TournamentsSnapshot }).previous;
        for (const [key, data] of snapshot) {
          queryClient.setQueryData(key, data);
        }
      }
      mutationOptions.onError?.(error, variables, onMutateResult, context);
    },
    onSettled: (data, error, variables, onMutateResult, context) => {
      queryClient.invalidateQueries(tournamentsRoot);
      mutationOptions.onSettled?.(data, error, variables, onMutateResult, context);
    },
  });
}

type MatchMutationOptions = {
  onSuccess?: Parameters<typeof useServerMutation>[0]["onSuccess"];
};

export const useReportMatchScore = (userId?: string, options?: MatchMutationOptions) =>
  useOptimisticMatchMutation({
    mutationFn: reportMatchScore,
    showSuccessToast: false,
    ...options,
    buildPatch: (match, variables) => {
      const onHome = !!userId && reportTeamPlayerIds(match.home).includes(userId);
      const onAway = !!userId && reportTeamPlayerIds(match.away).includes(userId);
      const callerTeamId = onHome
        ? reportTeamId(match.home)
        : onAway
          ? reportTeamId(match.away)
          : undefined;
      return {
        reported_home_cups: variables.data.home_cups,
        reported_away_cups: variables.data.away_cups,
        reported_ot_count: variables.data.ot_count,
        reported_by_team: callerTeamId,
        reported_by_player: userId,
      };
    },
  });

export const useConfirmMatchScore = (options?: MatchMutationOptions) =>
  useOptimisticMatchMutation({
    mutationFn: confirmMatchScore,
    showSuccessToast: false,
    ...options,
    buildPatch: (match) => ({
      status: "ended",
      home_cups: match.reported_home_cups ?? match.home_cups,
      away_cups: match.reported_away_cups ?? match.away_cups,
      ot_count: match.reported_ot_count ?? match.ot_count,
      reported_home_cups: undefined,
      reported_away_cups: undefined,
      reported_ot_count: undefined,
      reported_by_team: undefined,
      reported_by_player: undefined,
    }),
  });

export const useClearMatchReport = (options?: MatchMutationOptions) =>
  useOptimisticMatchMutation({
    mutationFn: clearMatchReport,
    ...options,
    buildPatch: () => ({
      reported_home_cups: undefined,
      reported_away_cups: undefined,
      reported_ot_count: undefined,
      reported_by_team: undefined,
      reported_by_player: undefined,
    }),
  });
