import { useState, useMemo, useRef, useEffect, useReducer } from 'react';
import { useNavigate, useParams } from 'react-router-dom';

import { useInView } from 'react-intersection-observer';

import {
  useMutation,
  useInfiniteQuery,
  useQuery,
  useQueryClient,
} from 'react-query';

import { useDebounce } from 'src/hooks/useDebounce';

import { IconPlus, IconX } from '@material-hu/icons/tabler';
import Box from '@material-hu/mui/Box';
import Divider from '@material-hu/mui/Divider';
import IconButton from '@material-hu/mui/IconButton';
import Stack from '@material-hu/mui/Stack';
import Typography from '@material-hu/mui/Typography';
import useMediaQuery from '@material-hu/mui/useMediaQuery';
import { useTheme } from '@material-hu/mui/styles';

import Button from '@material-hu/components/design-system/Buttons/Button';
import HuSearch from '@material-hu/components/design-system/Inputs/Search';

import { useAuth } from 'src/contexts/JWTContext';
import { useMobileLayout } from 'src/hooks/useMobileLayout';
import { useMobileToken } from 'src/hooks/useMobileToken';
import {
  getTeams,
  getTournamentPrediction,
  saveTournamentPrediction,
  prodeKeys,
} from 'src/services/prode';
import {
  type ProdeApiError,
  ProdeErrorCode,
  type TeamWithGroup,
  type TournamentPrediction,
  type TournamentPredictionTeam,
} from 'src/types/prode';
import { useLokaliseTranslation } from 'src/utils/i18n';

import { DETAIL_MAX_WIDTH } from './constants';
import SportsPoolLayout from './components/SportsPoolLayout';
import PodiumSlot from './components/PodiumSlot';
import TeamFlag from './components/TeamFlag';
import TeamRowSkeleton from './components/TeamRowSkeleton';
import useProdeSnackbar from './hooks/useProdeSnackbar';
import useSportsPoolRoutes from './hooks/useSportsPoolRoutes';

type PodiumPosition = 'champion' | 'runnerUp' | 'thirdPlace';

type PodiumState = {
  champion: TeamWithGroup | null;
  runnerUp: TeamWithGroup | null;
  thirdPlace: TeamWithGroup | null;
  selectedPosition: PodiumPosition;
};

type PodiumAction =
  | { type: 'SELECT_TEAM'; team: TeamWithGroup }
  | { type: 'REMOVE_TEAM'; position: PodiumPosition }
  | { type: 'SET_POSITION'; position: PodiumPosition }
  | { type: 'INITIALIZE'; prediction: TournamentPrediction }
  | { type: 'RESET' };

const POSITIONS: PodiumPosition[] = ['champion', 'runnerUp', 'thirdPlace'];

const initialPodiumState: PodiumState = {
  champion: null,
  runnerUp: null,
  thirdPlace: null,
  selectedPosition: 'champion',
};

function podiumReducer(state: PodiumState, action: PodiumAction): PodiumState {
  switch (action.type) {
    case 'SELECT_TEAM': {
      const { team } = action;
      if (state.champion && state.runnerUp && state.thirdPlace) return state;

      const next: PodiumState = {
        ...state,
        champion: state.champion?.id === team.id ? null : state.champion,
        runnerUp: state.runnerUp?.id === team.id ? null : state.runnerUp,
        thirdPlace: state.thirdPlace?.id === team.id ? null : state.thirdPlace,
        [state.selectedPosition]: team,
      };

      const currentIndex = POSITIONS.indexOf(state.selectedPosition);
      for (let i = 1; i < POSITIONS.length; i++) {
        const pos = POSITIONS[(currentIndex + i) % POSITIONS.length];
        if (!next[pos]) {
          next.selectedPosition = pos;
          break;
        }
      }

      return next;
    }

    case 'REMOVE_TEAM':
      return {
        ...state,
        [action.position]: null,
        selectedPosition: action.position,
      };

    case 'SET_POSITION':
      return { ...state, selectedPosition: action.position };

    case 'INITIALIZE': {
      const { prediction: pred } = action;
      const champion = toTeamWithGroup(pred.championTeam);
      const runnerUp = toTeamWithGroup(pred.runnerUpTeam);
      const thirdPlace = toTeamWithGroup(pred.thirdPlaceTeam);

      let selectedPosition: PodiumPosition = 'champion';
      if (champion && !runnerUp) selectedPosition = 'runnerUp';
      else if (champion && runnerUp && !thirdPlace)
        selectedPosition = 'thirdPlace';

      return { champion, runnerUp, thirdPlace, selectedPosition };
    }

    case 'RESET':
      return initialPodiumState;
  }
}

const ITEMS_PER_PAGE = 20;

const toTeamWithGroup = (
  team: TournamentPredictionTeam | null,
): TeamWithGroup | null => {
  if (!team) return null;
  return {
    ...team,
    shortName: null,
    countryCode: team.countryCode,
    groupId: null,
  };
};

const PodiumPage = () => {
  const { competitionId: competitionIdParam } = useParams<{
    competitionId: string;
  }>();
  const competitionId = Number(competitionIdParam);
  const isValidId = !!competitionId && !Number.isNaN(competitionId);
  const navigate = useNavigate();
  const { t } = useLokaliseTranslation('sportsPool');
  const { enqueueSnackbar } = useProdeSnackbar();
  const queryClient = useQueryClient();
  const theme = useTheme();
  const isMobile = useMediaQuery(theme.breakpoints.down('md'));
  const scrollContainerRef = useRef<HTMLDivElement>(null);
  const [sentinelRef, inView] = useInView({ threshold: 0 });
  const routes = useSportsPoolRoutes();
  const { isAuthenticated } = useAuth();

  useMobileToken();
  useMobileLayout();

  const defaultBg = theme.palette.new.background.elements.default;
  const layoutBg = theme.palette.new.background.layout.default;
  const borderBrand = theme.palette.new.border.neutral.brand;
  const borderDefault = theme.palette.new.border.neutral.default;
  const borderDivider = theme.palette.new.border.neutral.divider;

  const [searchQuery, setSearchQuery] = useState('');
  const { debouncedValue: debouncedSearch, isDebouncing } = useDebounce(
    searchQuery,
    300,
  );
  const [podium, dispatch] = useReducer(podiumReducer, initialPodiumState);
  const { champion, runnerUp, thirdPlace, selectedPosition } = podium;

  const handleGoBack = () => navigate(routes.competition(competitionId));

  const { data: tournamentPrediction } = useQuery(
    prodeKeys.tournamentPrediction(competitionId),
    () => getTournamentPrediction(competitionId),
    {
      enabled: isAuthenticated && isValidId,
      select: res => res.data,
    },
  );

  const canModifyPodium = !!tournamentPrediction?.isOpen;

  useEffect(() => {
    if (!isValidId) {
      navigate(routes.competitions(), { replace: true });
    } else if (tournamentPrediction && !canModifyPodium) {
      handleGoBack();
    }
  }, [isValidId, tournamentPrediction, canModifyPodium]);

  useEffect(() => {
    if (tournamentPrediction?.prediction) {
      dispatch({
        type: 'INITIALIZE',
        prediction: tournamentPrediction.prediction,
      });
    } else {
      dispatch({ type: 'RESET' });
    }
  }, [tournamentPrediction]);

  const searchParam = debouncedSearch.trim() || undefined;

  const {
    data: teamsData,
    isLoading: isTeamsLoading,
    isFetching: isTeamsFetching,
    fetchNextPage,
    hasNextPage,
    isFetchingNextPage,
  } = useInfiniteQuery(
    prodeKeys.teams(competitionId, searchParam),
    ({ pageParam }) =>
      getTeams(competitionId, {
        search: searchParam,
        cursor: pageParam,
        limit: ITEMS_PER_PAGE,
      }),
    {
      enabled: isAuthenticated && isValidId,
      getNextPageParam: lastPage => lastPage.data.nextCursor ?? undefined,
      keepPreviousData: true,
    },
  );

  const saveMutation = useMutation(
    () =>
      saveTournamentPrediction(competitionId, {
        championTeamId: champion!.id,
        runnerUpTeamId: runnerUp!.id,
        thirdPlaceTeamId: thirdPlace!.id,
      }),
    {
      onSuccess: () => {
        queryClient.invalidateQueries(
          prodeKeys.tournamentPrediction(competitionId),
        );
        queryClient.invalidateQueries(prodeKeys.userRanking(competitionId));
        enqueueSnackbar({
          title: t('podium.saved_title'),
          description: t('podium.saved_description'),
          variant: 'success',
          autoHideDuration: 3000,
        });
        handleGoBack();
      },
      onError: (error: unknown) => {
        const code = (error as { response?: { data?: ProdeApiError } })
          ?.response?.data?.code;
        if (code === ProdeErrorCode.PredictionLocked) {
          enqueueSnackbar({
            title: t('prediction.prediction_locked'),
            variant: 'error',
            autoHideDuration: 3000,
          });
          handleGoBack();
        } else {
          enqueueSnackbar({
            title: t('podium.error_title'),
            description: t('podium.error_description'),
            variant: 'error',
            autoHideDuration: 3000,
          });
        }
      },
    },
  );

  const allTeams = useMemo(() => {
    if (!teamsData?.pages) return [];
    return teamsData.pages.flatMap(page => page.data.data);
  }, [teamsData]);

  const isSearching = isDebouncing || (isTeamsFetching && !isFetchingNextPage);

  useEffect(() => {
    if (inView && hasNextPage && !isFetchingNextPage) {
      fetchNextPage();
    }
  }, [inView, hasNextPage, isFetchingNextPage, fetchNextPage]);

  const isTeamSelected = (teamId: number) =>
    champion?.id === teamId ||
    runnerUp?.id === teamId ||
    thirdPlace?.id === teamId;

  const getTeamPosition = (teamId: number): PodiumPosition | null => {
    if (champion?.id === teamId) return 'champion';
    if (runnerUp?.id === teamId) return 'runnerUp';
    if (thirdPlace?.id === teamId) return 'thirdPlace';
    return null;
  };

  const allPositionsFilled = !!champion && !!runnerUp && !!thirdPlace;
  const canSave = !!champion && !!runnerUp && !!thirdPlace;

  const handleSave = () => {
    if (canSave) {
      saveMutation.mutate();
    }
  };

  return (
    <SportsPoolLayout
      slotProps={{
        root: {
          sx: {
            backgroundColor: theme =>
              theme.palette.new.background.layout.default,
          },
        },
        header: {
          title: t('podium.modal_title'),
          onBack: handleGoBack,
          onClose: handleGoBack,
        },
      }}
    >
      <Stack
        ref={scrollContainerRef}
        sx={{
          flex: 1,
          backgroundColor: layoutBg,
          alignItems: 'center',
          py: { xs: 2, md: 4 },
          px: { xs: 2, md: 3 },
          gap: 2,
          overflow: 'auto',
        }}
      >
        <Stack
          sx={{
            width: '100%',
            maxWidth: DETAIL_MAX_WIDTH,
            gap: 2,
          }}
        >
          <Stack
            sx={{
              display: 'grid',
              gridTemplateColumns: 'repeat(3, minmax(0, 160px))',
              gap: 2,
              justifyContent: 'start',
            }}
          >
            <PodiumSlot
              position="champion"
              team={champion}
              label={t('podium.champion')}
              icon={
                <Typography
                  variant="globalXXS"
                  sx={{ lineHeight: 1 }}
                >
                  🏆
                </Typography>
              }
              number={1}
              isSelected={
                !allPositionsFilled && selectedPosition === 'champion'
              }
              onSelect={() =>
                dispatch({ type: 'SET_POSITION', position: 'champion' })
              }
              onRemove={() =>
                dispatch({ type: 'REMOVE_TEAM', position: 'champion' })
              }
            />
            <PodiumSlot
              position="runnerUp"
              team={runnerUp}
              label={t('podium.runner_up')}
              icon={
                <Typography
                  variant="globalXXS"
                  sx={{ lineHeight: 1 }}
                >
                  🥈
                </Typography>
              }
              number={2}
              isSelected={
                !allPositionsFilled && selectedPosition === 'runnerUp'
              }
              onSelect={() =>
                dispatch({ type: 'SET_POSITION', position: 'runnerUp' })
              }
              onRemove={() =>
                dispatch({ type: 'REMOVE_TEAM', position: 'runnerUp' })
              }
            />
            <PodiumSlot
              position="thirdPlace"
              team={thirdPlace}
              label={t('podium.third_place')}
              icon={
                <Typography
                  variant="globalXXS"
                  sx={{ lineHeight: 1 }}
                >
                  🥉
                </Typography>
              }
              number={3}
              isSelected={
                !allPositionsFilled && selectedPosition === 'thirdPlace'
              }
              onSelect={() =>
                dispatch({ type: 'SET_POSITION', position: 'thirdPlace' })
              }
              onRemove={() =>
                dispatch({ type: 'REMOVE_TEAM', position: 'thirdPlace' })
              }
            />
          </Stack>

          <Stack sx={{ gap: 0.5 }}>
            <Typography
              variant="globalS"
              fontWeight="fontWeightSemiBold"
            >
              {t('podium.countries')}
            </Typography>

            <HuSearch
              value={searchQuery}
              onChange={setSearchQuery}
              placeholder={t('podium.search_placeholder')}
            />
          </Stack>

          <Stack
            sx={{
              width: '100%',
              backgroundColor: defaultBg,
              borderRadius: theme => theme.shape.borderRadiusL,
              border: '1px solid',
              borderColor: borderDefault,
              overflow: 'hidden',
              p: 2,
            }}
          >
            {(isTeamsLoading || (isSearching && !allTeams.length)) && (
              <TeamRowSkeleton />
            )}

            {!isTeamsLoading &&
              !(isSearching && !allTeams.length) &&
              allTeams.map((team, index) => (
                <Stack key={team.id}>
                  <Stack
                    sx={{
                      flexDirection: 'row',
                      alignItems: 'center',
                      justifyContent: 'space-between',
                      py: 1,
                    }}
                  >
                    <Stack
                      sx={{
                        flexDirection: 'row',
                        alignItems: 'center',
                        gap: 1,
                      }}
                    >
                      <TeamFlag
                        countryCode={team.countryCode}
                        flagUrl={team.flagUrl}
                        name={team.name}
                        emoji={team.emoji}
                        size={28}
                        sx={{
                          width: 40,
                          height: 40,
                        }}
                      />
                      <Typography
                        variant="globalXS"
                        fontWeight="fontWeightSemiBold"
                      >
                        {team.localizedName ?? team.name}
                      </Typography>
                    </Stack>
                    <IconButton
                      onClick={() => {
                        if (isTeamSelected(team.id)) {
                          const pos = getTeamPosition(team.id);
                          if (pos)
                            dispatch({ type: 'REMOVE_TEAM', position: pos });
                        } else {
                          dispatch({ type: 'SELECT_TEAM', team });
                        }
                      }}
                      disabled={!isTeamSelected(team.id) && allPositionsFilled}
                      variant="secondary"
                      sx={{
                        width: 40,
                        height: 40,
                        border: '1px solid',
                        borderColor: borderBrand,
                        borderRadius: 1,
                        backgroundColor:
                          theme.palette.new.action.button.background.secondary
                            .default,
                        color: theme.palette.new.text.neutral.brand,
                        '&.Mui-disabled': {
                          border: '1px solid',
                          borderColor: borderDefault,
                          color:
                            theme.palette.new.action.button.text.disabled
                              .darker,
                        },
                      }}
                    >
                      {isTeamSelected(team.id) ? (
                        <IconX size={24} />
                      ) : (
                        <IconPlus size={24} />
                      )}
                    </IconButton>
                  </Stack>
                  {index < allTeams.length - 1 && (
                    <Divider sx={{ borderColor: borderDefault, my: 1 }} />
                  )}
                </Stack>
              ))}

            {hasNextPage && (
              <Box
                ref={sentinelRef}
                sx={{ height: '2px' }}
              />
            )}

            {isFetchingNextPage && <TeamRowSkeleton />}
          </Stack>
        </Stack>
      </Stack>

      <Stack
        sx={{
          justifyContent: 'center',
          alignItems: 'center',
          px: { xs: 2, md: 3 },
          py: 2,
          gap: 1,
          backgroundColor: defaultBg,
          ...(isMobile
            ? {
                boxShadow: `0px -2px 24px ${theme.palette.new.shadows['4dp']}`,
              }
            : {
                borderTop: '1px solid',
                borderColor: borderDivider,
              }),
        }}
      >
        <Button
          fullWidth
          variant="primary"
          size="large"
          disabled={!canSave || saveMutation.isLoading}
          loading={saveMutation.isLoading}
          onClick={handleSave}
          sx={{
            maxWidth: { xs: '100%', md: 248 },
          }}
        >
          {t('podium.save')}
        </Button>
      </Stack>
    </SportsPoolLayout>
  );
};

export default PodiumPage;
