import {
  ActionIcon,
  Box,
  Button,
  Group,
  Paper,
  Stack,
  Text,
} from "@mantine/core";
import { CaretLeftIcon, CaretRightIcon, InfoIcon } from "@phosphor-icons/react";
import WizardOrbIcon from "@/components/wizard-orb-icon";
import { Trans, useLingui } from "@lingui/react/macro";
import { useNavigate } from "@tanstack/react-router";
import React, { useEffect, useMemo, useRef, useState } from "react";
import { Tournament } from "@/features/tournaments/types";
import Sheet from "@/components/sheet/sheet";
import { useSheet } from "@/hooks/use-sheet";
import { PicksMap } from "../types";
import { useSubmitPrediction } from "../queries";
import {
  getMatchLabel,
  getPickableMatches,
  resolvePredictedBracket,
  setPick,
} from "../utils";
import { PredictionBracket } from "./prediction-bracket";
import { MatchupSheet } from "./matchup-sheet";
import { WinnerSelector } from "./winner-selector";

interface PredictionEditorProps {
  tournament: Tournament;
  initialPicks: PicksMap;
}

export const PredictionEditor: React.FC<PredictionEditorProps> = ({
  tournament,
  initialPicks,
}) => {
  const { t, i18n } = useLingui();
  const navigate = useNavigate();
  const containerRef = useRef<HTMLDivElement>(null);
  const matchupSheet = useSheet();
  const matches = tournament.matches || [];
  const [picks, setPicks] = useState<PicksMap>(initialPicks);

  const pickable = useMemo(
    () => getPickableMatches(matches, picks),
    [matches, picks]
  );

  const resolved = useMemo(
    () => resolvePredictedBracket(matches, picks),
    [matches, picks]
  );

  const firstUnpickedLid = useMemo(
    () =>
      pickable.find((match) => !resolved.get(match.lid)?.pickedWinnerId)?.lid,
    [pickable, resolved]
  );

  const [activeLid, setActiveLid] = useState<number | undefined>(undefined);
  useEffect(() => {
    if (activeLid === undefined && pickable.length > 0) {
      setActiveLid(firstUnpickedLid ?? pickable[0].lid);
    }
  }, [activeLid, firstUnpickedLid, pickable]);

  useEffect(() => {
    if (activeLid === undefined) return;
    const card = containerRef.current?.querySelector(
      `[data-match-lid="${activeLid}"]`
    ) as HTMLElement | null;
    const viewport = card?.closest(
      ".mantine-ScrollArea-viewport"
    ) as HTMLElement | null;
    if (!card || !viewport) return;

    const cardRect = card.getBoundingClientRect();
    const viewportRect = viewport.getBoundingClientRect();
    viewport.scrollTo({
      left: Math.max(
        0,
        viewport.scrollLeft +
          (cardRect.left - viewportRect.left) -
          (viewportRect.width - cardRect.width) / 2
      ),
      top: Math.max(
        0,
        viewport.scrollTop +
          (cardRect.top - viewportRect.top) -
          (viewportRect.height - cardRect.height) / 2
      ),
      behavior: "smooth",
    });
  }, [activeLid]);

  const pickedCount = pickable.filter(
    (match) => resolved.get(match.lid)?.pickedWinnerId
  ).length;
  const complete = pickedCount === pickable.length;

  const submit = useSubmitPrediction(tournament.id);

  const handlePick = (lid: number, teamId: string) => {
    const next = setPick(matches, picks, lid, teamId);
    setPicks(next);

    const nextResolved = resolvePredictedBracket(matches, next);
    const nextPickable = getPickableMatches(matches, next);
    const nextUnpicked = nextPickable.find(
      (match) => !nextResolved.get(match.lid)?.pickedWinnerId
    );
    setActiveLid(nextUnpicked?.lid ?? lid);
  };

  const activeIndex = pickable.findIndex((match) => match.lid === activeLid);
  const stepTo = (offset: number) => {
    const next = pickable[activeIndex + offset];
    if (next) setActiveLid(next.lid);
  };

  const activeResolved =
    activeLid !== undefined ? resolved.get(activeLid) : undefined;
  const activeMatch = pickable[activeIndex];

  const handleSubmit = async () => {
    try {
      await submit.mutateAsync({
        data: { tournamentId: tournament.id, picks },
      });
      navigate({ to: "/" });
    } catch {
    }
  };

  return (
    <Box pos="relative" ref={containerRef}>
      <PredictionBracket
        matches={matches}
        picks={picks}
        mode="edit"
        activeLid={activeLid}
        onActivate={setActiveLid}
      />

      <Box
        pos="absolute"
        left={0}
        right={0}
        bottom={0}
        p="md"
        style={{ zIndex: 2, pointerEvents: "none" }}
      >
        <Paper
          withBorder
          shadow="md"
          radius="lg"
          p="sm"
          style={{
            pointerEvents: "auto",
            borderColor: "var(--mantine-primary-color-filled)",
          }}
        >
          <Stack gap="xs">
            {activeMatch && (
              <>
                <Group gap={6} align="center" wrap="nowrap">
                  <WizardOrbIcon
                    size={16}
                    weight="duotone"
                    color="var(--mantine-primary-color-filled)"
                  />
                  <Text
                    size="xs"
                    fw={700}
                    tt="uppercase"
                    c="var(--mantine-primary-color-filled)"
                    style={{ letterSpacing: 0.5 }}
                  >
                    {getMatchLabel(i18n, matches, activeMatch)}
                  </Text>
                </Group>
                <WinnerSelector
                  home={activeResolved?.home}
                  away={activeResolved?.away}
                  pickedId={activeResolved?.pickedWinnerId}
                  onSelect={(teamId) => handlePick(activeMatch.lid, teamId)}
                />
              </>
            )}
            <Group justify="space-between" align="center" wrap="nowrap">
              <Group gap={4} align="center" wrap="nowrap">
                <ActionIcon
                  variant="default"
                  size="lg"
                  radius="md"
                  onClick={() => stepTo(-1)}
                  disabled={activeIndex <= 0}
                  aria-label={t`Previous match`}
                >
                  <CaretLeftIcon size={16} />
                </ActionIcon>
                <ActionIcon
                  variant="default"
                  size="lg"
                  radius="md"
                  onClick={() => stepTo(1)}
                  disabled={activeIndex < 0 || activeIndex >= pickable.length - 1}
                  aria-label={t`Next match`}
                >
                  <CaretRightIcon size={16} />
                </ActionIcon>
                <ActionIcon
                  variant="default"
                  size="lg"
                  radius="md"
                  onClick={matchupSheet.open}
                  disabled={!activeResolved?.home && !activeResolved?.away}
                  aria-label={t`Matchup details`}
                >
                  <InfoIcon size={16} />
                </ActionIcon>
              </Group>
              <Group gap="sm" align="center" wrap="nowrap">
                <Text size="sm" fw={600} c={complete ? undefined : "dimmed"}>
                  {pickedCount}/{pickable.length}
                </Text>
                <Button
                  disabled={!complete}
                  loading={submit.isPending}
                  onClick={handleSubmit}
                >
                  <Trans>Submit</Trans>
                </Button>
              </Group>
            </Group>
          </Stack>
        </Paper>
      </Box>

      <Sheet
        title={activeMatch ? getMatchLabel(i18n, matches, activeMatch) : t`Matchup`}
        {...matchupSheet.props}
      >
        <MatchupSheet
          home={activeResolved?.home}
          away={activeResolved?.away}
          isOpen={matchupSheet.isOpen}
        />
      </Sheet>
    </Box>
  );
};
