import { type FC, useEffect, useMemo, useState } from 'react';
import { useInfiniteQuery, useQuery } from 'react-query';
import { useNavigate } from 'react-router-dom';

import { parseAsStringLiteral, useQueryState } from 'nuqs';
import Box from '@material-hu/mui/Box';
import Stack from '@material-hu/mui/Stack';

import {
  getMatches,
  getNextToPredict,
  getTournamentPrediction,
  getUserRanking,
  prodeKeys,
} from 'src/services/prode';
import {
  type CompetitionDetail,
  type Match,
  MatchFilter,
  type Prediction,
} from 'src/types/prode';
import { useLokaliseTranslation } from 'src/utils/i18n';

import { useTranslation } from '../i18n';
import {
  fullHeightCardSx,
  getStaggerAnimationSx,
  infoCardsContainerSx,
  infoCardsRowSx,
} from '../constants';
import useSportsPoolRoutes from '../hooks/useSportsPoolRoutes';
import { getMatchLocalDate } from '../utils';

import {
  PendingPredictionCardSkeleton,
  PodiumCardSkeleton,
} from './CardSkeletons';
import MatchDetailModal from './MatchDetailModal';
import MatchesList from './MatchesList';
import NextEventCounter from './NextEventCounter';
import PendingPredictionCard from './PendingPredictionCard';
import PodiumCard from './PodiumCard';
import PodiumPreviewDialog from './PodiumPreviewDialog';
import PredictionModal from './PredictionModal';
import RankingCard, { RankingCardSkeleton } from './RankingCard';

// Filter values for URL query param
const MATCH_FILTERS = [
  'all',
  'upcoming',
  'finished',
  'needs_prediction',
] as const;

type PredictionsTabProps = {
  competitionId: number;
  competition: CompetitionDetail;
  onNavigateToRanking: () => void;
  onResetScroll: () => void;
};

const PredictionsTab: FC<PredictionsTabProps> = ({
  competitionId,
  competition,
  onNavigateToRanking,
  onResetScroll,
}) => {
  const navigate = useNavigate();
  const routes = useSportsPoolRoutes();
  const { t, i18n } = useLokaliseTranslation('sportsPool');
  const { t: tLocal } = useTranslation();
  const [isPodiumPreviewOpen, setIsPodiumPreviewOpen] = useState(false);
  const [isPredictionModalOpen, setIsPredictionModalOpen] = useState(false);
  const [isMatchDetailModalOpen, setIsMatchDetailModalOpen] = useState(false);
  const [selectedMatch, setSelectedMatch] = useState<Match | null>(null);
  const [selectedPrediction, setSelectedPrediction] =
    useState<Prediction | null>(null);

  // URL-synced filter state
  const [matchFilterParam, setMatchFilter] = useQueryState(
    'filter',
    parseAsStringLiteral(MATCH_FILTERS),
  );
  const matchFilter = matchFilterParam ?? MatchFilter.Upcoming;

  // biome-ignore lint/correctness/useExhaustiveDependencies: intentionally reset scroll when filter changes
  useEffect(() => {
    onResetScroll();
  }, [onResetScroll, matchFilter]);

  // Queries
  const { data: userRanking, isFetching: isRankingFetching } = useQuery(
    prodeKeys.userRanking(competitionId),
    () => getUserRanking(competitionId),
    {
      select: res => res.data,
    },
  );

  const { data: tournamentPrediction, isFetching: isTournamentFetching } =
    useQuery(
      prodeKeys.tournamentPrediction(competitionId),
      () => getTournamentPrediction(competitionId),
      {
        select: res => res.data,
      },
    );

  const { data: nextToPredict, isFetching: isNextToPredictFetching } = useQuery(
    prodeKeys.nextToPredict(competitionId),
    () => getNextToPredict(competitionId),
    {
      select: res => res.data,
    },
  );

  // Build params for matches query
  const matchesParams = useMemo(() => {
    const params: { filter?: MatchFilter } = {};
    if (matchFilter && matchFilter !== MatchFilter.All) {
      params.filter = matchFilter as MatchFilter;
    }
    return params;
  }, [matchFilter]);

  const {
    data: matchesInfiniteData,
    isLoading: isMatchesLoading,
    isFetchingNextPage,
    hasNextPage,
    fetchNextPage,
  } = useInfiniteQuery(
    prodeKeys.matches(competitionId, matchesParams),
    ({ pageParam }) =>
      getMatches(competitionId, { ...matchesParams, cursor: pageParam }).then(
        res => res.data,
      ),
    {
      getNextPageParam: lastPage => lastPage.nextCursor ?? undefined,
    },
  );

  const allMatches = useMemo(
    () => matchesInfiniteData?.pages.flatMap(page => page.data) ?? [],
    [matchesInfiniteData],
  );

  // Group matches by date for display — accumulate all pages then re-group
  const matchesByDate = useMemo(() => {
    const grouped = new Map<string, typeof allMatches>();
    allMatches.forEach(mwp => {
      const date = getMatchLocalDate(mwp.match.matchDate, mwp.match.matchTime);
      if (!grouped.has(date)) {
        grouped.set(date, []);
      }
      grouped.get(date)?.push(mwp);
    });

    return Array.from(grouped.entries())
      .sort(([a], [b]) => a.localeCompare(b))
      .map(([date, matches]) => {
        matches.sort((a, b) => {
          const aKey = `${a.match.matchDate}T${a.match.matchTime ?? '99:99'}`;
          const bKey = `${b.match.matchDate}T${b.match.matchTime ?? '99:99'}`;
          return aKey.localeCompare(bKey);
        });
        const dateObj = new Date(`${date}T12:00:00`);
        const dateDisplay = dateObj.toLocaleDateString(i18n.language, {
          weekday: 'long',
          day: '2-digit',
          month: 'long',
        });
        return { date, dateDisplay, matches };
      });
  }, [allMatches, i18n.language]);

  // Tournament has started once the first match begins
  const hasTournamentStarted =
    competition.status === 'ACTIVE' || competition.status === 'FINISHED';
  const canModifyPodium =
    !!tournamentPrediction?.isOpen && !hasTournamentStarted;

  const handlePodiumCardClick = () => {
    if (tournamentPrediction?.prediction) {
      setIsPodiumPreviewOpen(true);
    } else {
      navigate(routes.podium(competitionId));
    }
  };

  const handleModifyPodium = () => {
    setIsPodiumPreviewOpen(false);
    navigate(routes.podium(competitionId));
  };

  const getDeadline = () => {
    if (competition.startDate) {
      const date = new Date(competition.startDate);
      return date.toLocaleDateString(i18n.language, {
        day: '2-digit',
        month: '2-digit',
        hour: '2-digit',
        minute: '2-digit',
      });
    }
    return undefined;
  };

  const handleMatchClick = (matchId: number) => {
    const foundMwp = allMatches.find(mwp => mwp.match.id === matchId);

    if (!foundMwp) return;

    const match: Match = {
      ...foundMwp.match,
      isPredictionOpen: foundMwp.isPredictionOpen,
    };

    setSelectedMatch(match);
    setSelectedPrediction(foundMwp.prediction);

    const hasDefinedTeams = match.homeTeam !== null && match.awayTeam !== null;

    if (match.status === 'FINISHED') {
      setIsMatchDetailModalOpen(true);
    } else if (foundMwp.isPredictionOpen && hasDefinedTeams) {
      setIsPredictionModalOpen(true);
    } else {
      setIsMatchDetailModalOpen(true);
    }
  };

  const handlePendingPredictionClick = () => {
    if (nextToPredict) {
      const match: Match = {
        ...nextToPredict.match,
        isPredictionOpen: nextToPredict.isPredictionOpen,
      };
      setSelectedMatch(match);
      setSelectedPrediction(nextToPredict.prediction);
      setIsPredictionModalOpen(true);
    }
    setMatchFilter(MatchFilter.NeedsPrediction);
  };

  const showRankingSkeleton = isRankingFetching;
  const showNextToPredictSkeleton = isNextToPredictFetching;
  const showTournamentSkeleton = isTournamentFetching;

  return (
    <>
      <Stack sx={{ gap: 2, pb: 2 }}>
        {/* Countdown */}
        {!hasTournamentStarted && competition.startDate && (
          <Box sx={getStaggerAnimationSx(0)}>
            <NextEventCounter
              title={tLocal('startsIn')}
              date={competition.startDate}
            />
          </Box>
        )}
        {/* Info Cards */}
        <Stack sx={infoCardsContainerSx}>
          {/* Ranking Card — only after tournament starts */}
          {hasTournamentStarted && showRankingSkeleton && (
            <Box sx={getStaggerAnimationSx(1)}>
              <RankingCardSkeleton />
            </Box>
          )}
          {hasTournamentStarted && !showRankingSkeleton && userRanking && (
            <Box sx={getStaggerAnimationSx(1)}>
              <RankingCard
                ranking={userRanking}
                onClick={onNavigateToRanking}
              />
            </Box>
          )}

          {/* Pending Prediction and Podium row */}
          <Box sx={getStaggerAnimationSx(2)}>
            <Stack sx={infoCardsRowSx}>
              {/* Pending Prediction */}
              {showNextToPredictSkeleton && (
                <Box sx={fullHeightCardSx}>
                  <PendingPredictionCardSkeleton />
                </Box>
              )}
              {!showNextToPredictSkeleton &&
                nextToPredict?.match &&
                nextToPredict.isPredictionOpen &&
                nextToPredict.match.homeTeam &&
                nextToPredict.match.awayTeam && (
                  <Box sx={fullHeightCardSx}>
                    <PendingPredictionCard
                      match={nextToPredict.match}
                      onClick={handlePendingPredictionClick}
                    />
                  </Box>
                )}

              {/* Podium Card */}
              {showTournamentSkeleton && (
                <Box sx={{ ...fullHeightCardSx, order: { xs: -1, md: 0 } }}>
                  <PodiumCardSkeleton />
                </Box>
              )}
              {!showTournamentSkeleton &&
                canModifyPodium &&
                tournamentPrediction && (
                  <Box sx={{ ...fullHeightCardSx, order: { xs: -1, md: 0 } }}>
                    <PodiumCard
                      tournamentPrediction={tournamentPrediction}
                      deadline={getDeadline()}
                      onClick={handlePodiumCardClick}
                    />
                  </Box>
                )}
            </Stack>
          </Box>
        </Stack>
        {/* Matches List */}
        <MatchesList
          matchesByDate={matchesByDate}
          onMatchClick={handleMatchClick}
          activeFilter={matchFilter as MatchFilter}
          onFilterChange={filter => setMatchFilter(filter)}
          hasTournamentStarted={hasTournamentStarted}
          isLoading={isMatchesLoading}
          hasNextPage={hasNextPage}
          isFetchingNextPage={isFetchingNextPage}
          fetchNextPage={fetchNextPage}
        />
      </Stack>

      {/* Podium Preview Dialog */}
      <PodiumPreviewDialog
        open={isPodiumPreviewOpen && !!tournamentPrediction?.prediction}
        prediction={tournamentPrediction?.prediction}
        deadline={getDeadline()}
        isOpen={canModifyPodium}
        onClose={() => setIsPodiumPreviewOpen(false)}
        onModify={handleModifyPodium}
      />

      {/* Prediction Modal */}
      <PredictionModal
        open={isPredictionModalOpen}
        onClose={() => {
          setIsPredictionModalOpen(false);
          setSelectedMatch(null);
          setSelectedPrediction(null);
        }}
        competitionId={competitionId}
        match={selectedMatch}
        existingPrediction={selectedPrediction}
        isPredictionOpen={selectedMatch?.isPredictionOpen ?? false}
      />

      {/* Match Detail Modal */}
      <MatchDetailModal
        open={isMatchDetailModalOpen}
        onClose={() => {
          setIsMatchDetailModalOpen(false);
          setSelectedMatch(null);
          setSelectedPrediction(null);
        }}
        competitionId={competitionId}
        match={selectedMatch}
        prediction={selectedPrediction}
      />
    </>
  );
};

export default PredictionsTab;
