import { ActionIcon, Card, Flex, Text, Indicator, Stack } from "@mantine/core";
import { PlayIcon, PencilIcon, SpeakerHighIcon } from "@phosphor-icons/react";
import React, { useCallback, useMemo } from "react";
import { MatchSlot } from "./match-slot";
import { Match } from "@/features/matches/types";
import { Team } from "@/features/teams/types";
import { useSheet } from "@/hooks/use-sheet";
import { MatchForm } from "./match-form";
import { MatchReport } from "./match-report";
import toast from "@/lib/sonner";
import Sheet from "@/components/sheet/sheet";
import { useServerMutation } from "@/lib/tanstack-query/hooks";
import { endMatch, startMatch } from "@/features/matches/server";
import { tournamentKeys } from "@/features/tournaments/queries";
import { useQueryClient } from "@tanstack/react-query";
import { useSpotifyPlayback } from "@/lib/spotify/hooks";
import { getGroupLabel } from "../utils/group-label";
import styles from "./styles.module.css";
import { Trans, useLingui } from "@lingui/react/macro";

interface MatchCardProps {
  match: Match;
  orders: Record<number, number>;
  showControls?: boolean;
  groupConfig?: {
    num_groups: number;
    advance_per_group: number;
  };
  onTap?: (match: Match) => void;
  selected?: boolean;
  nextUpMatchId?: string;
}

export const MatchCard: React.FC<MatchCardProps> = ({
  match,
  orders,
  showControls,
  groupConfig,
  onTap,
  selected,
  nextUpMatchId,
}) => {
  const queryClient = useQueryClient();
  const editSheet = useSheet();
  const { playTrack, pause } = useSpotifyPlayback();
  const { t, i18n } = useLingui();

  const canTap = !!(onTap && match.home && match.away);

  const isNextUp = nextUpMatchId !== undefined && match.id === nextUpMatchId;

  const homeSlot = useMemo(
    () => ({
      from: orders[match.home_from_lid],
      from_loser: match.home_from_loser,
      team: match.home,
      seed: match.home_seed,
      cups: match.status === "ended" ? match.home_cups : undefined,
      isWinner:
        match.status === "ended" &&
        match.home_cups !== undefined &&
        match.away_cups !== undefined &&
        match.home_cups > match.away_cups,
      groupLabel: !match.home && match.home_seed ? getGroupLabel(i18n, match.home_seed, groupConfig) : undefined,
    }),
    [match, orders, groupConfig, i18n]
  );
  const awaySlot = useMemo(
    () => ({
      from: orders[match.away_from_lid],
      from_loser: match.away_from_loser,
      team: match.away,
      seed: match.away_seed,
      cups: match.status === "ended" ? match.away_cups : undefined,
      isWinner:
        match.status === "ended" &&
        match.away_cups !== undefined &&
        match.home_cups !== undefined &&
        match.away_cups > match.home_cups,
      groupLabel: !match.away && match.away_seed ? getGroupLabel(i18n, match.away_seed, groupConfig) : undefined,
    }),
    [match, orders, groupConfig, i18n]
  );

  const showToolbar = useMemo(
    () => match.status === "ready" && showControls,
    [match.status, showControls]
  );

  const showEditButton = useMemo(
    () =>
      showControls && (match.status === "started" || match.status === "ended"),
    [showControls, match.status]
  );

  const hasWalkoutData = showControls && match.home && match.away && 'song_id' in match.home && 'song_id' in match.away;

  const start = useServerMutation({
    mutationFn: startMatch,
    successMessage: t`Match started!`,
    onSuccess: () => {
      queryClient.invalidateQueries({
        queryKey: tournamentKeys.details(match.tournament.id),
      });
    },
  });

  const end = useServerMutation({
    mutationFn: endMatch,
    onSuccess: (data) => {
      queryClient.invalidateQueries({
        queryKey: tournamentKeys.details(match.tournament.id),
      });
      editSheet.close();
      if (data?.downstreamReset) {
        toast.error(
          t`Downstream matches were reset because the correction changed who advances.`
        );
      }
      if (data?.groupEditAfterKnockout) {
        toast.error(
          t`Group result changed after the bracket was seeded — reseed the knockout stage if needed.`
        );
      }
    },
  });

  const handleFormSubmit = useCallback(
    async (data: {
      home_cups: number;
      away_cups: number;
      ot_count: number;
    }) => {
      end.mutate({
        data: {
          ...data,
          matchId: match.id,
        },
      });
    },
    [end, match.id]
  );

  const speak = useCallback((text: string): Promise<void> => {
    return new Promise((resolve) => {
      if (!("speechSynthesis" in window)) {
        resolve();
        return;
      }

      const utterance = new SpeechSynthesisUtterance(text);
      const voices = window.speechSynthesis.getVoices();

      const preferredVoice =
        voices.find(
          (voice) =>
            voice.lang.startsWith("en") && voice.name.includes("Daniel")
        ) ||
        voices.find((voice) => voice.lang.startsWith("en") && voice.default);

      if (preferredVoice) {
        utterance.voice = preferredVoice;
      }

      utterance.rate = 0.9;
      utterance.volume = 0.8;
      utterance.pitch = 1.0;

      utterance.onend = () => resolve();
      utterance.onerror = () => resolve();

      window.speechSynthesis.speak(utterance);
    });
  }, []);

  const playTeamWalkout = useCallback((team: Team): Promise<void> => {
    return new Promise((resolve) => {
      const songDuration = (team.song_end - team.song_start) * 1000;

      playTrack(team.song_id, undefined, team.song_start * 1000);

      setTimeout(async () => {
        await pause();
        resolve();
      }, songDuration);
    });
  }, [playTrack, pause]);

  const handleSpeakerClick = useCallback(async () => {
    if (!hasWalkoutData || !match.home?.name || !match.away?.name) {
      await speak(t`${match.home?.name || t({ message: "Home", context: "match team" })} vs. ${match.away?.name || t({ message: "Away", context: "match team" })}`);
      return;
    }

    try {
      const homeTeam = match.home as Team;
      const awayTeam = match.away as Team;

      await playTeamWalkout(homeTeam);
      await speak(homeTeam.name);
      await speak(t`versus`);
      await playTeamWalkout(awayTeam);
      await speak(awayTeam.name);
      await speak(t`have fun, good luck!`);

    } catch (error) {
      console.warn('Walkout sequence error:', error);
      await speak(t`${match.home.name} vs. ${match.away.name}`);
    }
  }, [hasWalkoutData, match.home, match.away, speak, playTeamWalkout]);

  const handleStart = useCallback(async () => {
    start.mutate({
      data: match.id,
    });

    const isRegional = match.tournament?.regional === true;

    if (!isRegional && hasWalkoutData && match.home?.name && match.away?.name) {
      try {
        const homeTeam = match.home as Team;
        const awayTeam = match.away as Team;

        await playTeamWalkout(homeTeam);
        await speak(homeTeam.name);
        await speak(t`versus`);
        await playTeamWalkout(awayTeam);
        await speak(awayTeam.name);
        await speak(t`have fun, good luck!`);
      } catch (error) {
        console.warn('Auto-walkout sequence error:', error);
      }
    }
  }, [match, start, hasWalkoutData, playTeamWalkout, speak]);

  return (
    <Stack gap={6} align="flex-end">
      <Flex direction="row" align="center" justify="end" gap={8}>
        <Text
          c="dimmed"
          fw="bolder"
          px={6}
          py={2}
          style={{
            backgroundColor: 'var(--mantine-color-body)'
          }}
        >
          {match.order}
        </Text>
        <Flex
          align="stretch"
          style={{
            borderRadius: "var(--mantine-radius-default)",
            boxShadow: isNextUp
              ? "0 0 0 1px var(--mantine-primary-color-filled), 0 0 12px var(--mantine-primary-color-light-hover)"
              : undefined,
            transition:
              "box-shadow 200ms cubic-bezier(0.32, 0.72, 0, 1)",
          }}
        >
          <Indicator
            inline
            processing={match.status === "started"}
            color="red"
            size={12}
            disabled={match.status !== "started" || showEditButton}
          >
            <Card
              w={showToolbar || showEditButton ? 200 : 220}
              withBorder
              pos="relative"
              className={canTap ? styles["tappable-card"] : undefined}
              onClick={canTap ? () => onTap!(match) : undefined}
              role={canTap ? "button" : undefined}
              tabIndex={canTap ? 0 : undefined}
              onKeyDown={
                canTap
                  ? (e: React.KeyboardEvent) => {
                      if (e.key === "Enter" || e.key === " ") {
                        e.preventDefault();
                        onTap!(match);
                      }
                    }
                  : undefined
              }
              aria-label={
                canTap
                  ? t`Match ${match.order}: ${match.home!.name} vs ${match.away!.name}`
                  : undefined
              }
              style={{
                overflow: "visible",
                backgroundColor: 'var(--mantine-color-body)',
                borderColor: selected
                  ? 'var(--mantine-primary-color-filled)'
                  : 'var(--mantine-color-default-border)',
                boxShadow: 'var(--mantine-shadow-sm)',
                transition:
                  'border-color 200ms cubic-bezier(0.32, 0.72, 0, 1)',
              }}
              data-match-lid={match.lid}
            >
              <Card.Section withBorder p={0}>
                <MatchSlot {...homeSlot} />
              </Card.Section>

              <Card.Section p={0} mb={-16}>
                <MatchSlot {...awaySlot} />
              </Card.Section>

              {match.reset && (
                <Text
                  pos="absolute"
                  top={-20}
                  left={8}
                  size="xs"
                  c="dimmed"
                  fw="bold"
                >
                  <Trans>* If necessary</Trans>
                </Text>
              )}

              {showControls && match.status !== "tbd" && (
                <ActionIcon
                  pos="absolute"
                  bottom={-2}
                  left={-26}
                  size="sm"
                  variant="subtle"
                  color="gray"
                  onClick={(e) => {
                    e.stopPropagation();
                    handleSpeakerClick();
                  }}
                  aria-label={t`Announce matchup`}
                >
                  <SpeakerHighIcon size={12} />
                </ActionIcon>
              )}
            </Card>
          </Indicator>

          {showToolbar && (
            <Flex direction="column" justify="center" align="center">
              <ActionIcon
                color="green"
                onClick={handleStart}
                loading={start.isPending}
                size="sm"
                h="100%"
                radius="sm"
                ml={-4}
                aria-label={t`Start match`}
                style={{
                  borderTopLeftRadius: 0,
                  borderBottomLeftRadius: 0,
                }}
              >
                <PlayIcon size={14} />
              </ActionIcon>
            </Flex>
          )}


          {showEditButton && (
            <Flex direction="column" justify="center" align="center">
              <ActionIcon
                color={match.status === "ended" ? "gray" : "blue"}
                onClick={editSheet.open}
                loading={end.isPending}
                size="sm"
                h="100%"
                radius="sm"
                ml={-4}
                aria-label={t`Edit match score`}
                style={{
                  borderTopLeftRadius: 0,
                  borderBottomLeftRadius: 0,
                }}
              >
                <PencilIcon size={14} />
              </ActionIcon>
            </Flex>
          )}
        </Flex>

        <Sheet title={t`Edit Match`} {...editSheet.props}>
          <MatchForm
            match={match}
            onSubmit={handleFormSubmit}
            onCancel={editSheet.close}
            loading={end.isPending}
          />
        </Sheet>
      </Flex>

      {!showControls && <MatchReport match={match} compact />}
    </Stack>
  );
};
