import {
  Alert,
  AlertIcon,
  Box,
  Button,
  Checkbox,
  FormLabel,
  Grid,
  Input,
  InputGroup,
  InputRightElement,
  NumberDecrementStepper,
  NumberIncrementStepper,
  NumberInput,
  NumberInputField,
  NumberInputStepper,
  Radio,
  RadioGroup,
  Stack,
  Textarea,
} from "@chakra-ui/react";
import { t, Trans } from "@lingui/macro";
import { useLingui } from "@lingui/react";
import { RankedMode } from "@prisma/client";
import Breadcrumbs from "components/common/Breadcrumbs";
import ModeImage from "components/common/ModeImage";
import SubText from "components/common/SubText";
import { stages } from "lib/lists/stages";
import { setManySearchParams } from "lib/setSearchParams";
import { shuffleArray } from "lib/shuffleArray";
import { useRouter } from "next/router";
import { ChangeEvent, Fragment, useEffect, useState } from "react";
import { FiCheck, FiFilter, FiRotateCw } from "react-icons/fi";

const MapsGeneratorPage = () => {
  const router = useRouter();
  const { i18n } = useLingui();

  const [stagesSelected, setStagesSelected] = useState<
    Record<string, RankedMode[]>
  >(getInitialStages());
  const [generationMode, setGenerationMode] = useState<
    "EQUAL" | "SZ_EVERY_OTHER"
  >("EQUAL");
  const [maplist, setMaplist] = useState("");
  const [count, setCount] = useState(9);
  const [editing, setEditing] = useState(false);
  const [copied, setCopied] = useState<null | "URL" | "LIST">(null);

  function getInitialStages() {
    const filtersFromUrl = Object.entries(router.query).reduce(
      (acc: Record<string, RankedMode[]>, cur) => {
        if (stages.includes(cur[0]) && typeof cur[1] === "string") {
          // @ts-ignore
          acc[cur[0]] = cur[1]
            .split(",")
            .filter((mode) => ["SZ", "TC", "RM", "CB"].includes(mode));
        }
        return acc;
      },
      {}
    );

    return Object.keys(filtersFromUrl).length
      ? filtersFromUrl
      : stages.reduce((acc: Record<string, RankedMode[]>, cur) => {
          acc[cur] = ["SZ", "TC", "RM", "CB"];
          return acc;
        }, {});
  }

  const poolForUrl = (stagesSelectedForUrl: Record<string, string[]>) => {
    return Object.entries(stagesSelectedForUrl).map(([key, stages]) => ({
      key,
      value: stages.join(","),
    }));
  };

  useEffect(() => {
    const maplist = localStorage.getItem("maplist");
    if (!maplist) return;
    setMaplist(maplist);
  }, []);

  useEffect(() => {
    if (!copied) return;

    const timer = setTimeout(() => {
      setCopied(null);
    }, 750);

    return () => clearTimeout(timer);
  }, [copied]);

  const handleChange = (
    e: ChangeEvent<HTMLInputElement>,
    mode: RankedMode,
    stage: string
  ) => {
    const oldArray = stagesSelected[stage] ?? [];
    const newArray = e.target.checked
      ? oldArray.concat(mode)
      : oldArray.filter((modeInArray) => modeInArray !== mode);

    const newStagesSelected = { ...stagesSelected, [stage]: newArray };

    setStagesSelected(newStagesSelected);
    setManySearchParams(poolForUrl(newStagesSelected), true);
  };

  const generateMaps = () => {
    let modeStages = Object.entries(stagesSelected).reduce(
      (acc: Record<RankedMode, string[]>, [stage, modes]) => {
        modes.forEach((mode) => acc[mode].push(stage));
        return acc;
      },
      { SZ: [], TC: [], RM: [], CB: [] }
    );

    modeStages = {
      SZ: shuffleArray(modeStages.SZ),
      TC: shuffleArray(modeStages.TC),
      RM: shuffleArray(modeStages.RM),
      CB: shuffleArray(modeStages.CB),
    };

    const modesFromGenerationMode =
      generationMode === "SZ_EVERY_OTHER"
        ? ["TC", "RM", "CB"]
        : ["SZ", "TC", "RM", "CB"];

    const modes = (shuffleArray(
      modesFromGenerationMode
    ) as RankedMode[]).filter((mode) => modeStages[mode].length > 0);
    if (modes.length === 0) {
      return "I can't generate a maplist without any maps in it you know.";
    }

    const stagesAlreadyPicked = new Set<string>();

    const isSZFirst = false;

    return new Array(count)
      .fill(null)
      .map((_, i) => {
        let modeOfRound: RankedMode = "SZ";

        if (
          generationMode !== "SZ_EVERY_OTHER" ||
          i % 2 === Number(isSZFirst)
        ) {
          modes.push(modes.shift() as RankedMode);
          modeOfRound = modes[0];
        }

        const stageArray = modeStages[modeOfRound];

        stageArray.push(stageArray.shift() as string);

        let shifted = 0;
        while (
          stagesAlreadyPicked.has(stageArray[0]) &&
          shifted < stageArray.length
        ) {
          stageArray.push(stageArray.shift() as string);
          shifted++;
        }

        stagesAlreadyPicked.add(stageArray[0]);

        return `${i + 1}) ${modeOfRound} on ${stageArray[0]}`;
      })
      .join("\n");
  };

  return (
    <>
      <Breadcrumbs pages={[{ name: t`Maplist Generator` }]} />
      {editing ? (
        <>
          <Alert status="info" mb={8}>
            <AlertIcon />
            <Trans>
              Pro tip: bookmark this page after making your map list to save it
            </Trans>
          </Alert>
          <Grid
            templateColumns="2fr 2fr 1fr 1fr 1fr 1fr"
            rowGap={4}
            placeItems="center"
            mx={["-20px", "0"]}
          >
            <Box />
            <Box />
            <ModeImage mode="SZ" />
            <ModeImage mode="TC" />
            <ModeImage mode="RM" />
            <ModeImage mode="CB" />
            {stages.map((stage) => {
              const buttonIsAdd = (stagesSelected[stage]?.length ?? 0) < 4;
              return (
                <Fragment key={stage}>
                  <SubText textAlign="center">{i18n._(stage)}</SubText>

                  <Button
                    size="sm"
                    variant="outline"
                    colorScheme={buttonIsAdd ? "theme" : "red"}
                    onClick={() => {
                      const newStagesSelected = {
                        ...stagesSelected,
                        [stage]: ["SZ", "TC", "RM", "CB"] as RankedMode[],
                      };

                      if (!buttonIsAdd) delete newStagesSelected[stage];

                      setStagesSelected(newStagesSelected);
                      setManySearchParams(poolForUrl(newStagesSelected), true);
                    }}
                  >
                    {buttonIsAdd ? <Trans>All</Trans> : <Trans>Clear</Trans>}
                  </Button>
                  <Checkbox
                    value="SZ"
                    isChecked={(stagesSelected[stage] ?? []).includes("SZ")}
                    onChange={(e) => handleChange(e, "SZ", stage)}
                  />
                  <Checkbox
                    value="TC"
                    isChecked={(stagesSelected[stage] ?? []).includes("TC")}
                    onChange={(e) => handleChange(e, "TC", stage)}
                  />
                  <Checkbox
                    value="RM"
                    isChecked={(stagesSelected[stage] ?? []).includes("RM")}
                    onChange={(e) => handleChange(e, "RM", stage)}
                  />
                  <Checkbox
                    value="CB"
                    isChecked={(stagesSelected[stage] ?? []).includes("CB")}
                    onChange={(e) => handleChange(e, "CB", stage)}
                  />
                </Fragment>
              );
            })}
          </Grid>
          <FormLabel htmlFor="share" mt={4}>
            <Trans>Share your map pool</Trans>
          </FormLabel>
          {window && (
            <InputGroup size="md" mb={8}>
              <Input name="share" value={window.location.href} readOnly />
              <InputRightElement width="4.5rem">
                <Button
                  onClick={() => {
                    navigator.clipboard.writeText(window.location.href);
                    setCopied("URL");
                  }}
                  h="1.75rem"
                  size="sm"
                  disabled={copied === "URL"}
                >
                  {copied === "URL" ? <FiCheck /> : <Trans>Copy</Trans>}
                </Button>
              </InputRightElement>
            </InputGroup>
          )}
        </>
      ) : (
        <Grid
          my={8}
          templateColumns="repeat(4, 1fr)"
          rowGap={4}
          columnGap={4}
          display={["none", null, "grid"]}
        >
          <Box textAlign="center">
            <ModeImage mode="SZ" />
          </Box>
          <Box textAlign="center">
            <ModeImage mode="TC" />
          </Box>
          <Box textAlign="center">
            <ModeImage mode="RM" />
          </Box>
          <Box textAlign="center">
            <ModeImage mode="CB" />
          </Box>
          <Box textAlign="center">
            {Object.entries(stagesSelected).map(([stage, modes]) =>
              modes.includes("SZ") ? (
                <SubText key={stage}>{stage}</SubText>
              ) : null
            )}
          </Box>
          <Box textAlign="center">
            {Object.entries(stagesSelected).map(([stage, modes]) =>
              modes.includes("TC") ? (
                <SubText key={stage}>{stage}</SubText>
              ) : null
            )}
          </Box>
          <Box textAlign="center">
            {Object.entries(stagesSelected).map(([stage, modes]) =>
              modes.includes("RM") ? (
                <SubText key={stage}>{stage}</SubText>
              ) : null
            )}
          </Box>
          <Box textAlign="center">
            {Object.entries(stagesSelected).map(([stage, modes]) =>
              modes.includes("CB") ? (
                <SubText key={stage}>{stage}</SubText>
              ) : null
            )}
          </Box>
        </Grid>
      )}
      <Stack direction={["column", "row"]} spacing={4} mb={4}>
        <Button
          leftIcon={<FiRotateCw />}
          onClick={() => {
            const maplist = generateMaps();
            setMaplist(maplist);
            localStorage.setItem("maplist", maplist);
          }}
        >
          <Trans>Generate maps</Trans>
        </Button>
        <Button
          leftIcon={<FiFilter />}
          variant="outline"
          onClick={() => setEditing(!editing)}
        >
          {editing ? <Trans>Hide maps</Trans> : <Trans>Change map pool</Trans>}
        </Button>
      </Stack>
      <RadioGroup
        onChange={(value) =>
          setGenerationMode(value as "EQUAL" | "SZ_EVERY_OTHER")
        }
        value={generationMode}
      >
        <Stack direction="row" mb={4}>
          <Radio value="EQUAL">
            <Trans>All modes equally</Trans>
          </Radio>
          <Radio value="SZ_EVERY_OTHER">
            <Trans>SZ every other</Trans>
          </Radio>
        </Stack>
      </RadioGroup>
      <FormLabel htmlFor="count" fontSize="sm">
        <Trans>Amount of maps to generate</Trans>
      </FormLabel>
      <NumberInput
        name="count"
        size="sm"
        value={count}
        min={1}
        max={100}
        onChange={(_, value) => {
          if (!Number.isNaN(value)) setCount(value);
        }}
        mb={4}
        width={24}
      >
        <NumberInputField />
        <NumberInputStepper>
          <NumberIncrementStepper />
          <NumberDecrementStepper />
        </NumberInputStepper>
      </NumberInput>
      {maplist && (
        <>
          <Textarea value={maplist} readOnly rows={9} />
          <Button
            mt={2}
            variant="outline"
            size="sm"
            width={16}
            disabled={copied === "LIST"}
            onClick={() => {
              navigator.clipboard.writeText(maplist);
              setCopied("LIST");
            }}
          >
            {copied === "LIST" ? <FiCheck /> : <Trans>Copy</Trans>}
          </Button>
        </>
      )}
    </>
  );
};

export default MapsGeneratorPage;
