import { type FC, useEffect, useState } from 'react';
import { useMutation, useQueryClient } from 'react-query';

import { IconCheck, IconMinus, IconPlus } from '@material-hu/icons/tabler';
import Box from '@material-hu/mui/Box';
import DialogWrapper from '@material-hu/mui/Dialog';
import IconButton from '@material-hu/mui/IconButton';
import Stack from '@material-hu/mui/Stack';
import { useTheme } from '@material-hu/mui/styles';
import Typography from '@material-hu/mui/Typography';
import useMediaQuery from '@material-hu/mui/useMediaQuery';

import Button from '@material-hu/components/design-system/Buttons/Button';
import Dialog from '@material-hu/components/design-system/Dialog';

import { prodeKeys, savePrediction } from 'src/services/prode';
import {
  type MatchesPage,
  type ProdeApiError,
  ProdeErrorCode,
  type SavePredictionRequest,
} from 'src/types/prode';
import { useLokaliseTranslation } from 'src/utils/i18n';

import {
  predictionModalContainerSx,
  predictionScoreButtonSx,
  predictionScoreInputSx,
  predictionScoreRowSx,
  predictionTeamSx,
} from '../../constants';
import useProdeSnackbar from '../../hooks/useProdeSnackbar';
import MobileDialog from '../MobileDialog';
import TeamFlag from '../TeamFlag';

import { type PredictionModalProps } from './types';

const PredictionModal: FC<PredictionModalProps> = ({
  open,
  onClose,
  competitionId,
  match,
  existingPrediction,
  isPredictionOpen,
}) => {
  const { t } = useLokaliseTranslation('sportsPool');
  const { enqueueSnackbar } = useProdeSnackbar();
  const queryClient = useQueryClient();
  const theme = useTheme();
  const isMobile = useMediaQuery(theme.breakpoints.down('sm'));

  const [homeScore, setHomeScore] = useState<number | null>(null);
  const [awayScore, setAwayScore] = useState<number | null>(null);
  const [penaltyWinnerId, setPenaltyWinnerId] = useState<number | null>(null);

  // Check if this is a knockout match with a draw prediction (requires penalty winner selection)
  const isKnockout = match?.stageType === 'KNOCKOUT';
  const isDraw =
    homeScore !== null && awayScore !== null && homeScore === awayScore;
  const requiresPenaltyWinner = isKnockout && isDraw;

  // Check if scores have been set (not null)
  const hasScoresSet = homeScore !== null && awayScore !== null;

  // Initialize with existing prediction if available
  useEffect(() => {
    if (existingPrediction) {
      setHomeScore(existingPrediction.predictedHomeScore);
      setAwayScore(existingPrediction.predictedAwayScore);
      setPenaltyWinnerId(existingPrediction.predictedQualifierId);
    } else {
      setHomeScore(null);
      setAwayScore(null);
      setPenaltyWinnerId(null);
    }
  }, [existingPrediction, open]);

  // Reset penalty winner when scores change and it's no longer a draw
  useEffect(() => {
    if (!isDraw) {
      setPenaltyWinnerId(null);
    }
  }, [isDraw]);

  const saveMutation = useMutation(
    () => {
      const request: SavePredictionRequest = {
        matchId: match!.id,
        predictedHomeScore: homeScore ?? 0,
        predictedAwayScore: awayScore ?? 0,
      };

      // Include qualifier for knockout draws
      if (requiresPenaltyWinner && penaltyWinnerId) {
        request.predictedQualifierId = penaltyWinnerId;
      }

      return savePrediction(competitionId, request);
    },
    {
      onSuccess: response => {
        const savedPrediction = response.data;

        // Optimistically update the specific match in all cached matches queries.
        // These queries use useInfiniteQuery, so the cached data is shaped as
        // { pages: MatchesPage[], pageParams: unknown[] } — we need to walk each page.
        const matchesQueries = queryClient
          .getQueryCache()
          .findAll(prodeKeys.matchesAll(competitionId));
        matchesQueries.forEach(query => {
          const oldData = query.state.data as
            | { pages: MatchesPage[]; pageParams: unknown[] }
            | undefined;
          if (!oldData?.pages) return;

          // Check if this cache is for the needs_prediction filter
          // Query key: ['prode', 'competitions', id, 'matches', { filter, stageId }]
          const params = query.queryKey[query.queryKey.length - 1] as
            | { filter?: string }
            | undefined;
          const isNeedsPrediction = params?.filter === 'needs_prediction';

          // Preserve references of pages that don't contain the match.
          let hasChanges = false;
          const updatedPages: MatchesPage[] = oldData.pages.map(page => {
            const hasMatch = page.data.some(mwp => mwp.match.id === match!.id);
            if (!hasMatch) return page;

            hasChanges = true;
            return {
              ...page,
              data: isNeedsPrediction
                ? // Remove the match from the list since it now has a prediction
                  page.data.filter(mwp => mwp.match.id !== match!.id)
                : // Otherwise just update the prediction on the match card
                  page.data.map(mwp =>
                    mwp.match.id === match!.id
                      ? { ...mwp, prediction: savedPrediction }
                      : mwp,
                  ),
            };
          });

          if (!hasChanges) return;

          queryClient.setQueryData(query.queryKey, {
            ...oldData,
            pages: updatedPages,
          });
        });

        // Still need to fetch the new next-to-predict match
        queryClient.invalidateQueries(prodeKeys.nextToPredict(competitionId));
        // Ranking may change with new prediction
        queryClient.invalidateQueries(prodeKeys.userRanking(competitionId));

        enqueueSnackbar({
          title: t('prediction.saved'),
          variant: 'success',
          autoHideDuration: 3000,
        });
        onClose();
      },
      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,
          });
          onClose();
        } else {
          enqueueSnackbar({
            title: t('prediction.error'),
            variant: 'error',
            autoHideDuration: 3000,
          });
        }
      },
    },
  );

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

  const handleIncrement = (team: 'home' | 'away') => {
    if (team === 'home') {
      setHomeScore(prev => (prev === null ? 0 : prev + 1));
    } else {
      setAwayScore(prev => (prev === null ? 0 : prev + 1));
    }
  };

  const handleDecrement = (team: 'home' | 'away') => {
    if (team === 'home') {
      setHomeScore(prev => (prev === null ? 0 : Math.max(0, prev - 1)));
    } else {
      setAwayScore(prev => (prev === null ? 0 : Math.max(0, prev - 1)));
    }
  };

  const handleSelectPenaltyWinner = (teamId: number) => {
    setPenaltyWinnerId(teamId);
  };

  // Enable save when scores have been set
  // For knockout draws, also require a penalty winner to be selected
  const hasChanges = existingPrediction
    ? homeScore !== existingPrediction.predictedHomeScore ||
      awayScore !== existingPrediction.predictedAwayScore ||
      penaltyWinnerId !== existingPrediction.predictedQualifierId
    : hasScoresSet;

  // For knockout draws, the save button should be disabled until a penalty winner is selected
  const canSave =
    isPredictionOpen &&
    hasScoresSet &&
    hasChanges &&
    (!requiresPenaltyWinner || penaltyWinnerId !== null);

  if (!match) return null;

  const matchTitle = `${match.homeTeam?.localizedName ?? match.homeTeam?.name ?? match.homeTeamPlaceholder ?? t('matches.tbd')} - ${
    match.awayTeam?.localizedName ??
    match.awayTeam?.name ??
    match.awayTeamPlaceholder ??
    t('matches.tbd')
  }`;

  const brandColor = theme.palette.newBase?.brand[500];
  const brandBg = theme.palette.new.background.elements.brand;
  const defaultBg = theme.palette.new.background.elements.default;
  const borderColor = theme.palette.new.border.neutral.divider;

  const content = (
    <Stack sx={predictionModalContainerSx}>
      {/* Score Controls Row */}
      <Stack sx={{ width: '100%' }}>
        {/* Main score row: Flag - [+/Score/-] - vs - [+/Score/-] - Flag */}
        <Stack sx={predictionScoreRowSx}>
          {/* Home team flag */}
          <Stack sx={predictionTeamSx}>
            <TeamFlag
              countryCode={match.homeTeam?.countryCode}
              flagUrl={match.homeTeam?.flagUrl}
              name={match.homeTeam?.name ?? ''}
              emoji={match.homeTeam?.emoji}
              size={62}
            />
          </Stack>

          {/* Home score column: + / score / - */}
          <Stack sx={{ alignItems: 'center', gap: 2 }}>
            <IconButton
              onClick={() => handleIncrement('home')}
              sx={predictionScoreButtonSx}
            >
              <IconPlus size={20} />
            </IconButton>
            <Stack sx={predictionScoreInputSx}>
              <Typography
                variant="globalL"
                fontWeight={600}
                sx={{
                  color:
                    homeScore === null
                      ? theme.palette.new.text.neutral.lighter
                      : undefined,
                }}
              >
                {homeScore === null ? '-' : homeScore}
              </Typography>
            </Stack>
            <IconButton
              onClick={() => handleDecrement('home')}
              sx={predictionScoreButtonSx}
              disabled={homeScore === null || homeScore === 0}
            >
              <IconMinus size={20} />
            </IconButton>
          </Stack>

          {/* VS */}
          <Typography
            variant="globalXS"
            fontWeight={600}
            sx={{ color: theme.palette.new.text.neutral.lighter }}
          >
            {t('matches.vs')}
          </Typography>

          {/* Away score column: + / score / - */}
          <Stack sx={{ alignItems: 'center', gap: 2 }}>
            <IconButton
              onClick={() => handleIncrement('away')}
              sx={predictionScoreButtonSx}
            >
              <IconPlus size={20} />
            </IconButton>
            <Stack sx={predictionScoreInputSx}>
              <Typography
                variant="globalL"
                fontWeight={600}
                sx={{
                  color:
                    awayScore === null
                      ? theme.palette.new.text.neutral.lighter
                      : undefined,
                }}
              >
                {awayScore === null ? '-' : awayScore}
              </Typography>
            </Stack>
            <IconButton
              onClick={() => handleDecrement('away')}
              sx={predictionScoreButtonSx}
              disabled={awayScore === null || awayScore === 0}
            >
              <IconMinus size={20} />
            </IconButton>
          </Stack>

          {/* Away team flag */}
          <Stack sx={predictionTeamSx}>
            <TeamFlag
              countryCode={match.awayTeam?.countryCode}
              flagUrl={match.awayTeam?.flagUrl}
              name={match.awayTeam?.name ?? ''}
              emoji={match.awayTeam?.emoji}
              size={62}
            />
          </Stack>
        </Stack>

        {/* Penalty Winner Selection (for knockout draws) */}
        {requiresPenaltyWinner && match.homeTeam && match.awayTeam && (
          <Stack
            sx={{
              gap: 1.5,
              mt: 2,
              pt: 2,
              borderTop: '1px solid',
              borderColor,
            }}
          >
            <Typography
              variant="globalXS"
              fontWeight={600}
              sx={{ textAlign: 'center' }}
            >
              {t('prediction.penalty_winner_title')}
            </Typography>
            <Typography
              variant="globalXXS"
              sx={{
                textAlign: 'center',
                color: theme.palette.new.text.neutral.lighter,
              }}
            >
              {t('prediction.penalty_winner_description')}
            </Typography>

            {/* Team selection buttons */}
            <Stack
              sx={{
                flexDirection: 'row',
                gap: 1,
                justifyContent: 'center',
                pt: 1,
              }}
            >
              {/* Home Team Button */}
              <Stack
                onClick={() => handleSelectPenaltyWinner(match.homeTeam!.id)}
                sx={{
                  flexDirection: 'row',
                  p: 2,
                  gap: 1,
                  alignItems: 'center',
                  borderRadius: 2,
                  border: '1px solid',
                  height: 72,
                  borderColor:
                    penaltyWinnerId === match.homeTeam.id
                      ? brandColor
                      : borderColor,
                  backgroundColor:
                    penaltyWinnerId === match.homeTeam.id ? brandBg : defaultBg,
                  cursor: 'pointer',
                  flex: 1,
                  position: 'relative',
                  '&:hover': {
                    borderColor: brandColor,
                  },
                }}
              >
                {penaltyWinnerId === match.homeTeam.id && (
                  <Box
                    sx={{
                      position: 'absolute',
                      top: -8,
                      right: -8,
                      width: 20,
                      height: 20,
                      borderRadius: '100%',
                      backgroundColor: brandColor,
                      display: 'flex',
                      alignItems: 'center',
                      justifyContent: 'center',
                    }}
                  >
                    <IconCheck
                      size={14}
                      color={theme.palette.new.text.neutral.inverted}
                    />
                  </Box>
                )}
                <Stack
                  sx={{
                    width: 40,
                    height: 40,
                    alignItems: 'center',
                    justifyContent: 'center',
                    flexShrink: 0,
                  }}
                >
                  <TeamFlag
                    countryCode={match.homeTeam.countryCode}
                    flagUrl={match.homeTeam.flagUrl}
                    name={match.homeTeam.name}
                    emoji={match.homeTeam.emoji}
                    size={32}
                  />
                </Stack>
                <Typography
                  variant="globalS"
                  fontWeight={600}
                  noWrap
                >
                  {match.homeTeam.localizedName ?? match.homeTeam.name}
                </Typography>
              </Stack>

              {/* Away Team Button */}
              <Stack
                onClick={() => handleSelectPenaltyWinner(match.awayTeam!.id)}
                sx={{
                  flexDirection: 'row',
                  p: 2,
                  gap: 1,
                  alignItems: 'center',
                  borderRadius: 2,
                  border: '1px solid',
                  height: 72,
                  borderColor:
                    penaltyWinnerId === match.awayTeam.id
                      ? brandColor
                      : borderColor,
                  backgroundColor:
                    penaltyWinnerId === match.awayTeam.id ? brandBg : defaultBg,
                  cursor: 'pointer',
                  flex: 1,
                  position: 'relative',
                  '&:hover': {
                    borderColor: brandColor,
                  },
                }}
              >
                {penaltyWinnerId === match.awayTeam.id && (
                  <Box
                    sx={{
                      position: 'absolute',
                      top: -8,
                      right: -8,
                      width: 20,
                      height: 20,
                      borderRadius: '100%',
                      backgroundColor: brandColor,
                      display: 'flex',
                      alignItems: 'center',
                      justifyContent: 'center',
                    }}
                  >
                    <IconCheck
                      size={14}
                      color={theme.palette.new.text.neutral.inverted}
                    />
                  </Box>
                )}
                <Stack
                  sx={{
                    width: 40,
                    height: 40,
                    alignItems: 'center',
                    justifyContent: 'center',
                    flexShrink: 0,
                  }}
                >
                  <TeamFlag
                    countryCode={match.awayTeam.countryCode}
                    flagUrl={match.awayTeam.flagUrl}
                    name={match.awayTeam.name}
                    emoji={match.awayTeam.emoji}
                    size={32}
                  />
                </Stack>
                <Typography
                  variant="globalS"
                  fontWeight={600}
                  noWrap
                >
                  {match.awayTeam.localizedName ?? match.awayTeam.name}
                </Typography>
              </Stack>
            </Stack>
          </Stack>
        )}
      </Stack>
    </Stack>
  );

  const saveButton = (
    <Stack
      sx={{
        p: 3,
        borderTop: '1px solid',
        borderColor,
      }}
    >
      <Button
        fullWidth
        variant="primary"
        size="large"
        disabled={!canSave || saveMutation.isLoading}
        loading={saveMutation.isLoading}
        onClick={handleSave}
      >
        {t('prediction.save_prediction')}
      </Button>
    </Stack>
  );

  if (isMobile) {
    return (
      <MobileDialog
        open={open}
        onClose={onClose}
        title={matchTitle}
      >
        {content}
        {saveButton}
      </MobileDialog>
    );
  }

  return (
    <DialogWrapper
      open={open}
      onClose={onClose}
      maxWidth="xs"
      fullWidth
      PaperProps={{
        sx: {
          maxHeight: '90vh',
          maxWidth: 540,
          width: '100%',
        },
      }}
    >
      <Dialog
        onClose={onClose}
        title={matchTitle}
        body={content}
        primaryButtonProps={{
          onClick: handleSave,
          children: t('prediction.save_prediction'),
          disabled: !canSave || saveMutation.isLoading,
          loading: saveMutation.isLoading,
        }}
      />
    </DialogWrapper>
  );
};

export default PredictionModal;
