import PocketBase from "pocketbase";
import { Badge, BadgeProgress, EarnedBadge } from "@/features/badges/types";
import { transformBadge, transformBadgeProgress, transformEarnedBadge } from "@/lib/pocketbase/util/transform-types";

export interface PlayerStats {
  player_id: string;
  matches: number;
  wins: number;
  losses: number;
  total_cups_made: number;
  total_cups_against: number;
  margin_of_victory: number;
}

export function createBadgesService(pb: PocketBase) {
  return {
    async getBadge(id: string): Promise<Badge> {
      const result = await pb.collection("badges").getOne(id);
      return transformBadge(result);
    },

    async listBadges(): Promise<Badge[]> {
      const results = await pb.collection("badges").getFullList({
        sort: 'name',
      });
      return results.map(transformBadge);
    },

    async getBadgeProgress(id: string): Promise<BadgeProgress> {
      const result = await pb.collection("badge_progress").getOne(id, {
        expand: 'badge,player',
      });
      return transformBadgeProgress(result);
    },

    async getPlayerBadgeProgress(playerId: string): Promise<BadgeProgress[]> {
      const results = await pb.collection("badge_progress").getFullList({
        filter: `player = "${playerId}"`,
        expand: 'badge',
      });
      return results.map(transformBadgeProgress);
    },

     async listEarnedBadges(): Promise<EarnedBadge[]> {
      const results = await pb.collection("badge_progress").getFullList({
        filter: `earned = true`,
        expand: 'player',
      });
      return results.map(transformEarnedBadge);
    },

    async createBadgeProgress(data: {
      badge: string;
      player: string;
      progress: number;
      earned: boolean;
    }): Promise<BadgeProgress> {
      return await pb.collection("badge_progress").create<BadgeProgress>(data);
    },

    async updateBadgeProgress(id: string, data: {
      progress?: number;
      earned?: boolean;
    }): Promise<BadgeProgress> {
      return await pb.collection("badge_progress").update<BadgeProgress>(id, data);
    },

    async deleteBadgeProgress(id: string): Promise<boolean> {
      await pb.collection("badge_progress").delete(id);
      return true;
    },

    async clearAllBadgeProgress(): Promise<number> {
      const existingProgress = await pb.collection("badge_progress").getFullList({
        filter: 'badge.type != "manual"',
      });
      for (const progress of existingProgress) {
        await pb.collection("badge_progress").delete(progress.id);
      }
      return existingProgress.length;
    },

    async calculateBadgeProgress(playerId: string, badge: Badge): Promise<number> {
      if (badge.type === "manual") {
        return 0;
      }

      if (badge.type === "match") {
        return await this.calculateMatchBadgeProgress(playerId, badge);
      }

      if (badge.type === "tournament") {
        return await this.calculateTournamentBadgeProgress(playerId, badge);
      }

      return 0;
    },

    async calculateMatchBadgeProgress(playerId: string, badge: Badge): Promise<number> {
      const criteria = badge.criteria;

      const stats = await pb.collection("player_mainline_stats").getFirstListItem<PlayerStats>(
        `player_id = "${playerId}"`
      ).catch(() => null);

      if (!stats) return 0;

      if (criteria.matches_played !== undefined) {
        return stats.matches;
      }

      if (criteria.overtime_matches !== undefined || criteria.overtime_wins !== undefined) {
        const matches = await pb.collection("matches").getFullList({
          filter: `(home.players.id ?~ "${playerId}" || away.players.id ?~ "${playerId}") && status = "ended" && ot_count > 0 && (tournament.regional = false || tournament.regional = null)`,
          expand: 'tournament,home,away,home.players,away.players',
        });

        if (criteria.overtime_matches !== undefined) {
          return matches.length;
        }

        if (criteria.overtime_wins !== undefined) {
          const overtimeWins = matches.filter(m => {
            const isHome = m.expand?.home?.expand?.players?.some((p: any) => p.id === playerId) ||
                           m.expand?.home?.players?.includes(playerId);
            const isAway = m.expand?.away?.expand?.players?.some((p: any) => p.id === playerId) ||
                           m.expand?.away?.players?.includes(playerId);

            if (isHome) {
              return m.home_cups > m.away_cups;
            } else if (isAway) {
              return m.away_cups > m.home_cups;
            }
            return false;
          });
          return overtimeWins.length;
        }
      }

      if (criteria.margin_of_victory !== undefined) {
        const matches = await pb.collection("matches").getFullList({
          filter: `(home.players.id ?~ "${playerId}" || away.players.id ?~ "${playerId}") && status = "ended" && (tournament.regional = false || tournament.regional = null)`,
          expand: 'tournament,home,away,home.players,away.players',
        });

        const bigWins = matches.filter(m => {
          const isHome = m.expand?.home?.expand?.players?.some((p: any) => p.id === playerId) ||
                         m.expand?.home?.players?.includes(playerId);
          const isAway = m.expand?.away?.expand?.players?.some((p: any) => p.id === playerId) ||
                         m.expand?.away?.players?.includes(playerId);

          if (isHome && m.home_cups > m.away_cups) {
            return (m.home_cups - m.away_cups) >= criteria.margin_of_victory;
          } else if (isAway && m.away_cups > m.home_cups) {
            return (m.away_cups - m.home_cups) >= criteria.margin_of_victory;
          }
          return false;
        });

        return bigWins.length > 0 ? 1 : 0;
      }

      if (criteria.unique_partners !== undefined) {
        const matches = await pb.collection("matches").getFullList({
          filter: `(home.players.id ?~ "${playerId}" || away.players.id ?~ "${playerId}") && status = "ended"`,
          expand: 'home,away,home.players,away.players',
        });

        const partners = new Set<string>();

        for (const match of matches) {
          const isHome = match.expand?.home?.expand?.players?.some((p: any) => p.id === playerId) ||
                         match.expand?.home?.players?.includes(playerId);
          const isAway = match.expand?.away?.expand?.players?.some((p: any) => p.id === playerId) ||
                         match.expand?.away?.players?.includes(playerId);

          let teamPlayers: any[] = [];
          if (isHome) {
            teamPlayers = match.expand?.home?.expand?.players || match.expand?.home?.players || [];
          } else if (isAway) {
            teamPlayers = match.expand?.away?.expand?.players || match.expand?.away?.players || [];
          }

          for (const player of teamPlayers) {
            const partnerId = typeof player === 'string' ? player : player.id;
            if (partnerId !== playerId) {
              partners.add(partnerId);
            }
          }
        }

        return partners.size;
      }

      if (criteria.beat_mainline_partner !== undefined) {
        const mainlineMatches = await pb.collection("matches").getFullList({
          filter: `(home.players.id ?~ "${playerId}" || away.players.id ?~ "${playerId}") && status = "ended" && (tournament.regional = false || tournament.regional = null)`,
          expand: 'home,away,home.players,away.players',
        });

        const mainlinePartners = new Set<string>();
        for (const match of mainlineMatches) {
          const isHome = match.expand?.home?.expand?.players?.some((p: any) => p.id === playerId) ||
                         match.expand?.home?.players?.includes(playerId);
          const isAway = match.expand?.away?.expand?.players?.some((p: any) => p.id === playerId) ||
                         match.expand?.away?.players?.includes(playerId);

          let teamPlayers: any[] = [];
          if (isHome) {
            teamPlayers = match.expand?.home?.expand?.players || match.expand?.home?.players || [];
          } else if (isAway) {
            teamPlayers = match.expand?.away?.expand?.players || match.expand?.away?.players || [];
          }

          for (const player of teamPlayers) {
            const partnerId = typeof player === 'string' ? player : player.id;
            if (partnerId !== playerId) {
              mainlinePartners.add(partnerId);
            }
          }
        }

        const regionalMatches = await pb.collection("matches").getFullList({
          filter: `(home.players.id ?~ "${playerId}" || away.players.id ?~ "${playerId}") && status = "ended" && tournament.regional = true`,
          expand: 'home,away,home.players,away.players',
        });

        for (const match of regionalMatches) {
          const isHome = match.expand?.home?.expand?.players?.some((p: any) => p.id === playerId) ||
                         match.expand?.home?.players?.includes(playerId);
          const isAway = match.expand?.away?.expand?.players?.some((p: any) => p.id === playerId) ||
                         match.expand?.away?.players?.includes(playerId);

          const didWin = (isHome && match.home_cups > match.away_cups) ||
                         (isAway && match.away_cups > match.home_cups);

          if (!didWin) continue;

          const opponentPlayers: any[] = isHome
            ? (match.expand?.away?.expand?.players || match.expand?.away?.players || [])
            : (match.expand?.home?.expand?.players || match.expand?.home?.players || []);

          for (const opponent of opponentPlayers) {
            const opponentId = typeof opponent === 'string' ? opponent : opponent.id;
            if (mainlinePartners.has(opponentId)) {
              return 1;
            }
          }
        }

        return 0;
      }

      return 0;
    },

    async calculateTournamentBadgeProgress(playerId: string, badge: Badge): Promise<number> {
      const criteria = badge.criteria;

      const matches = await pb.collection("matches").getFullList({
        filter: `(home.players.id ?~ "${playerId}" || away.players.id ?~ "${playerId}") && status = "ended" && (tournament.regional = false || tournament.regional = null)`,
        expand: 'tournament,home,away,home.players,away.players',
      });

      const tournamentIds = new Set(matches.map(m => m.tournament));
      const tournamentsAttended = tournamentIds.size;

      if (criteria.tournaments_attended !== undefined) {
        if (tournamentsAttended === 0 && criteria.tournaments_attended === 0) {
          const teams = await pb.collection("teams").getFullList({
            filter: `players.id ?~ "${playerId}"`,
            expand: 'tournaments',
          });

          const hasEnrollment = teams.some((team: any) => {
            const tournaments = team.tournaments || [];
            return tournaments.length > 0;
          });

          return 0;
        }

        return tournamentsAttended;
      }

      if (criteria.won_tournament !== undefined) {
        const tournamentId = criteria.won_tournament;

        try {
          const tournamentMatches = await pb.collection("matches").getFullList({
            filter: `tournament = "${tournamentId}" && status = "ended"`,
            expand: 'home,away,home.players,away.players',
          });

          const winnersMatches = tournamentMatches.filter(m => !m.is_losers_bracket);
          const finalsMatch = winnersMatches.reduce((highest: any, current: any) =>
            (!highest || current.lid > highest.lid) ? current : highest, null);

          if (finalsMatch && finalsMatch.status === 'ended') {
            const finalsWinnerId = (finalsMatch.home_cups > finalsMatch.away_cups) ? finalsMatch.home : finalsMatch.away;

            const winningTeam = finalsMatch.expand?.[finalsWinnerId === finalsMatch.home ? 'home' : 'away'];
            const winningPlayers = winningTeam?.expand?.players || winningTeam?.players || [];

            const playerWon = winningPlayers.some((p: any) =>
              (typeof p === 'string' ? p : p.id) === playerId
            );

            return playerWon ? 1 : 0;
          }

          return 0;
        } catch (error) {
          return 0;
        }
      }

      if (criteria.tournament_wins !== undefined) {
        if (tournamentIds.size === 0) return 0;

        let tournamentWins = 0;

        for (const tournamentId of tournamentIds) {
          const tournamentMatches = await pb.collection("matches").getFullList({
            filter: `tournament = "${tournamentId}" && status = "ended" && (tournament.regional = false || tournament.regional = null)`,
            expand: 'tournament,home,away,home.players,away.players',
          });

          const winnersMatches = tournamentMatches.filter(m => !m.is_losers_bracket);
          const finalsMatch = winnersMatches.reduce((highest: any, current: any) =>
            (!highest || current.lid > highest.lid) ? current : highest, null);

          if (finalsMatch && finalsMatch.status === 'ended') {
            const finalsWinnerId = (finalsMatch.home_cups > finalsMatch.away_cups) ? finalsMatch.home : finalsMatch.away;

            const winningTeam = finalsMatch.expand?.[finalsWinnerId === finalsMatch.home ? 'home' : 'away'];
            const winningPlayers = winningTeam?.expand?.players || winningTeam?.players || [];

            const playerWon = winningPlayers.some((p: any) =>
              (typeof p === 'string' ? p : p.id) === playerId
            );

            if (playerWon) {
              tournamentWins++;
            }
          }
        }

        return tournamentWins;
      }

      if (criteria.placement !== undefined && typeof criteria.placement === 'number') {
        let placementCount = 0;

        for (const tournamentId of tournamentIds) {
          const tournamentMatches = await pb.collection("matches").getFullList({
            filter: `tournament = "${tournamentId}" && status = "ended" && (tournament.regional = false || tournament.regional = null)`,
            expand: 'tournament,home,away,home.players,away.players',
          });

          if (criteria.placement === 2) {
            const winnersMatches = tournamentMatches.filter(m => !m.is_losers_bracket);
            const finalsMatch = winnersMatches.reduce((highest: any, current: any) =>
              (!highest || current.lid > highest.lid) ? current : highest, null);

            if (finalsMatch && finalsMatch.status === 'ended') {
              const finalsLoserId = (finalsMatch.home_cups > finalsMatch.away_cups) ? finalsMatch.away : finalsMatch.home;

              const losingTeam = finalsMatch.expand?.[finalsLoserId === finalsMatch.home ? 'home' : 'away'];
              const losingPlayers = losingTeam?.expand?.players || losingTeam?.players || [];

              const playerLost = losingPlayers.some((p: any) =>
                (typeof p === 'string' ? p : p.id) === playerId
              );

              if (playerLost) {
                placementCount++;
              }
            }
          }

          if (criteria.placement === 3) {
            const losersMatches = tournamentMatches.filter(m => m.is_losers_bracket);
            const losersFinale = losersMatches.reduce((highest: any, current: any) =>
              (!highest || current.lid > highest.lid) ? current : highest, null);

            if (losersFinale && losersFinale.status === 'ended') {
              const losersFinaleLoserId = (losersFinale.home_cups > losersFinale.away_cups) ? losersFinale.away : losersFinale.home;

              const losingTeam = losersFinale.expand?.[losersFinaleLoserId === losersFinale.home ? 'home' : 'away'];
              const losingPlayers = losingTeam?.expand?.players || losingTeam?.players || [];

              const playerLost = losingPlayers.some((p: any) =>
                (typeof p === 'string' ? p : p.id) === playerId
              );

              if (playerLost) {
                placementCount++;
              }
            }
          }
        }

        return placementCount;
      }

      if (criteria.tournament_record !== undefined) {
        const includeRegional = criteria.tournament_record === "0-3";
        const tournamentFilter = includeRegional
          ? ''
          : 'regional = false || regional = null';

        const tournaments = await pb.collection("tournaments").getFullList({
          filter: tournamentFilter,
          sort: 'start_time',
        });

        const allMatches = includeRegional
          ? await pb.collection("matches").getFullList({
              filter: `(home.players.id ?~ "${playerId}" || away.players.id ?~ "${playerId}") && status = "ended"`,
              expand: 'tournament,home,away,home.players,away.players',
            })
          : matches;

        const relevantTournamentIds = includeRegional
          ? new Set(allMatches.map(m => m.tournament))
          : tournamentIds;

        let timesMetRecord = 0;

        for (const tournamentId of relevantTournamentIds) {
          const tournament = tournaments.find(t => t.id === tournamentId);
          if (!tournament) continue;

          const tournamentMatches = allMatches.filter(m => m.tournament === tournamentId);

          let wins = 0;
          let losses = 0;

          for (const match of tournamentMatches) {
            const isHome = match.expand?.home?.expand?.players?.some((p: any) => p.id === playerId) ||
                           match.expand?.home?.players?.includes(playerId);
            const isAway = match.expand?.away?.expand?.players?.some((p: any) => p.id === playerId) ||
                           match.expand?.away?.players?.includes(playerId);

            if (isHome && match.home_cups > match.away_cups) {
              wins++;
            } else if (isAway && match.away_cups > match.home_cups) {
              wins++;
            } else {
              losses++;
            }
          }

          const record = `${wins}-${losses}`;

          if (record === criteria.tournament_record) {
            if (criteria.won_previous !== undefined && criteria.won_previous === true) {
              const currentIndex = tournaments.findIndex(t => t.id === tournamentId);
              if (currentIndex > 0) {
                const previousTournament = tournaments[currentIndex - 1];
                if (previousTournament.winner_id === playerId) {
                  timesMetRecord++;
                }
              }
            } else {
              timesMetRecord++;
            }
          }
        }

        return timesMetRecord > 0 ? 1 : 0;
      }

      if (criteria.consecutive_wins !== undefined) {
        const allTournaments = await pb.collection("tournaments").getFullList({
          filter: 'regional = false || regional = null',
          sort: 'start_time',
        });

        const tournamentResults: { tournament: any; playerWon: boolean }[] = [];

        for (const tournament of allTournaments) {
          const matches = await pb.collection("matches").getFullList({
            filter: `tournament = "${tournament.id}" && status = "ended"`,
            expand: 'home,away,home.players,away.players',
          });

          if (matches.length === 0) {
            continue;
          }

          const winnersMatches = matches.filter(m => !m.is_losers_bracket);

          if (winnersMatches.length === 0) {
            continue;
          }

          const finalsMatch = winnersMatches.reduce((highest: any, current: any) => {
            if (!highest) return current;
            return (current.lid > highest.lid) ? current : highest;
          }, null);

          if (!finalsMatch || finalsMatch.status !== 'ended') {
            continue;
          }

          const winningTeamId = (finalsMatch.home_cups > finalsMatch.away_cups)
            ? finalsMatch.home
            : finalsMatch.away;

          const winningTeam = finalsMatch.expand?.[winningTeamId === finalsMatch.home ? 'home' : 'away'];
          if (!winningTeam) {
            continue;
          }

          const winningPlayers = winningTeam.expand?.players || winningTeam.players || [];

          const playerWon = winningPlayers.some((p: any) => {
            const pid = (typeof p === 'string') ? p : p.id;
            return pid === playerId;
          });

          tournamentResults.push({
            tournament,
            playerWon,
          });
        }

        let currentStreak = 0;
        let maxStreak = 0;

        for (const result of tournamentResults) {
          if (result.playerWon) {
            currentStreak++;
            maxStreak = Math.max(maxStreak, currentStreak);
          } else {
            currentStreak = 0;
          }
        }

        return maxStreak;
      }

      if (criteria.undefeated_group_stage !== undefined) {
        const regionalTournaments = await pb.collection("tournaments").getFullList({
          filter: 'regional = true',
          sort: 'start_time',
        });

        for (const tournament of regionalTournaments) {
          const groups = await pb.collection("groups").getFullList({
            filter: `tournament = "${tournament.id}"`,
            expand: 'teams,teams.players',
          });

          for (const group of groups) {
            const teams = group.expand?.teams || [];
            let playerTeamId: string | null = null;

            for (const team of teams) {
              const teamPlayers = team.expand?.players || team.players || [];
              const isPlayerInTeam = teamPlayers.some((p: any) =>
                (typeof p === 'string' ? p : p.id) === playerId
              );
              if (isPlayerInTeam) {
                playerTeamId = team.id;
                break;
              }
            }

            if (!playerTeamId) continue;

            const groupMatches = await pb.collection("matches").getFullList({
              filter: `tournament = "${tournament.id}" && group = "${group.id}" && status = "ended" && (home = "${playerTeamId}" || away = "${playerTeamId}")`,
              expand: 'home,away',
            });

            if (groupMatches.length === 0) continue;

            let isUndefeated = true;
            for (const match of groupMatches) {
              const isHome = match.home === playerTeamId;
              const didWin = isHome
                ? match.home_cups > match.away_cups
                : match.away_cups > match.home_cups;

              if (!didWin) {
                isUndefeated = false;
                break;
              }
            }

            if (isUndefeated) {
              return 1;
            }
          }
        }

        return 0;
      }

      return 0;
    },

    getTargetProgress(badge: Badge): number {
      if (badge.type === "manual") {
        return 1;
      }

      const criteria = badge.criteria;

      // Use explicit checks to handle 0 values correctly
      if (criteria.matches_played !== undefined) return criteria.matches_played;
      if (criteria.tournament_wins !== undefined) return criteria.tournament_wins;
      if (criteria.tournaments_attended !== undefined) return criteria.tournaments_attended;
      if (criteria.overtime_matches !== undefined) return criteria.overtime_matches;
      if (criteria.overtime_wins !== undefined) return criteria.overtime_wins;
      if (criteria.consecutive_wins !== undefined) return criteria.consecutive_wins;
      if (criteria.unique_partners !== undefined) return criteria.unique_partners;
      if (criteria.won_tournament !== undefined) return 1;
      if (criteria.placement !== undefined) return 1;
      if (criteria.margin_of_victory !== undefined) return 1;
      if (criteria.tournament_record !== undefined) return 1;
      if (criteria.beat_mainline_partner !== undefined) return 1;
      if (criteria.undefeated_group_stage !== undefined) return 1;

      return 1;
    },

    async awardManualBadge(playerId: string, badgeId: string): Promise<BadgeProgress> {
      const existingProgress = await pb.collection("badge_progress").getFirstListItem(
        `player = "${playerId}" && badge = "${badgeId}"`,
        { expand: 'badge' }
      ).catch(() => null);

      if (existingProgress) {
        const updated = await pb.collection("badge_progress").update(existingProgress.id, {
          progress: 1,
          earned: true,
        }, { expand: 'badge' });
        return transformBadgeProgress(updated);
      }

      const created = await pb.collection("badge_progress").create({
        badge: badgeId,
        player: playerId,
        progress: 1,
        earned: true,
      }, { expand: 'badge' });
      return transformBadgeProgress(created);
    },

    async migrateBadgeProgress(): Promise<{
      success: boolean;
      playersProcessed: number;
      progressRecordsCreated: number;
      totalBadgesEarned: number;
      averageBadgesPerPlayer: string;
    }> {
      await this.clearAllBadgeProgress();

      const badges = await this.listBadges();

      const allPlayers = await pb.collection("players").getFullList();
      const uniquePlayers = new Set(allPlayers.map((p: any) => p.id));

      let totalProgressRecords = 0;
      let totalBadgesEarned = 0;

      for (const playerId of uniquePlayers) {
        for (const badge of badges) {
          try {
            const progress = await this.calculateBadgeProgress(playerId, badge);
            const target = this.getTargetProgress(badge);

            const isPlacementBadge = badge.criteria.placement !== undefined;
            const is8YearVeteran = badge.key === 'veteran_8_badge';
            const earned = badge.progressive || isPlacementBadge || is8YearVeteran
              ? progress >= target
              : progress === target;

            if (progress > 0 || earned) {
              await this.createBadgeProgress({
                badge: badge.id,
                player: playerId,
                progress: progress,
                earned: earned,
              });

              totalProgressRecords++;

              if (earned) {
                totalBadgesEarned++;
              }
            }
          } catch (error: any) {
            console.error(`Error processing badge "${badge.name}" for player ${playerId}:`, error.message);
          }
        }
      }

      return {
        success: true,
        playersProcessed: uniquePlayers.size,
        progressRecordsCreated: totalProgressRecords,
        totalBadgesEarned: totalBadgesEarned,
        averageBadgesPerPlayer: (totalBadgesEarned / uniquePlayers.size).toFixed(2),
      };
    },
  };
}
