import React, { useState } from "react"
import Modal from "../elements/Modal"
import WeaponSelector from "../common/WeaponSelector"
import {
  Weapon,
  Build,
  HeadGear,
  ClothingGear,
  ShoesGear,
  Ability,
  HeadOnlyAbility,
  ClothingOnlyAbility,
  ShoesOnlyAbility,
  StackableAbility,
} from "../../types"
import WeaponImage from "../common/WeaponImage"
import Select from "../elements/Select"
import {
  headSelectOptions,
  clothingSelectOptions,
  shoesSelectOptions,
  headOnlyAbilities,
  clothingOnlyAbilities,
  shoesOnlyAbilities,
} from "../../utils/lists"
import Box from "../elements/Box"
import GearImage from "../builds/GearImage"
import Input from "../elements/Input"
import ViewSlots from "../builds/ViewSlots"
import AbilityButtons from "./AbilityButtons"
import TextArea from "../elements/TextArea"
import Button from "../elements/Button"
import { useMutation } from "@apollo/react-hooks"
import { ADD_BUILD } from "../../graphql/mutations/addBuild"
import { useToast } from "@chakra-ui/core"
import { UPDATE_BUILD } from "../../graphql/mutations/updateBuild"
import { DELETE_BUILD } from "../../graphql/mutations/deleteBuild"

interface BuildFormModalProps {
  existingGear: ExistingGearObject
  closeModal: () => void
  buildBeingEdited?: Build | null
}

type ExistingGearObject = Record<
  Partial<HeadGear | ClothingGear | ShoesGear>,
  Ability[]
>

const BuildFormModal: React.FC<BuildFormModalProps> = ({
  existingGear,
  closeModal,
  buildBeingEdited,
}) => {
  const toast = useToast()
  const [build, setBuild] = useState<Partial<Build>>(
    buildBeingEdited
      ? buildBeingEdited
      : {
          headgear: ["UNKNOWN", "UNKNOWN", "UNKNOWN", "UNKNOWN"],
          clothing: ["UNKNOWN", "UNKNOWN", "UNKNOWN", "UNKNOWN"],
          shoes: ["UNKNOWN", "UNKNOWN", "UNKNOWN", "UNKNOWN"],
        }
  )

  const [addBuild] = useMutation<{ addBuild: Build }, Build>(ADD_BUILD, {
    variables: { ...(build as Build) },
    onCompleted: data => {
      closeModal()
      toast({
        description: `New ${data.addBuild.weapon} build created`,
        position: "top-right",
        status: "success",
        duration: 10000,
      })
    },
    onError: error => {
      toast({
        title: "An error occurred",
        description: error.message,
        position: "top-right",
        status: "success",
        duration: 10000,
      })
    },
    refetchQueries: ["searchForBuilds"],
  })

  const [updateBuild] = useMutation<{ updateBuild: Build }, Build>(
    UPDATE_BUILD,
    {
      variables: { ...(build as Build) },
      onCompleted: () => {
        closeModal()
        toast({
          description: "Build updated",
          position: "top-right",
          status: "success",
          duration: 10000,
        })
      },
      onError: error => {
        toast({
          title: "An error occurred",
          description: error.message,
          position: "top-right",
          status: "success",
          duration: 10000,
        })
      },
      refetchQueries: ["searchForBuilds"],
    }
  )

  const [deleteBuild] = useMutation<boolean, { id: string }>(DELETE_BUILD, {
    variables: { id: build.id as string },
    onCompleted: () => {
      closeModal()
      toast({
        description: "Build deleted",
        position: "top-right",
        status: "success",
        duration: 10000,
      })
    },
    onError: error => {
      toast({
        title: "An error occurred",
        description: error.message,
        position: "top-right",
        status: "success",
        duration: 10000,
      })
    },
    refetchQueries: ["searchForBuilds"],
  })

  const handleChange = (value: Object) => setBuild({ ...build, ...value })

  const handleAbilityButtonClick = (ability: Ability) => {
    if (headOnlyAbilities.indexOf(ability as any) !== -1) {
      if (build.headgear![0] === "UNKNOWN") {
        handleChange({
          headgear: [
            ability,
            build.headgear![1],
            build.headgear![2],
            build.headgear![3],
          ],
        })
      }
    } else if (clothingOnlyAbilities.indexOf(ability as any) !== -1) {
      if (build.clothing![0] === "UNKNOWN") {
        handleChange({
          clothing: [
            ability,
            build.clothing![1],
            build.clothing![2],
            build.clothing![3],
          ],
        })
      }
    } else if (shoesOnlyAbilities.indexOf(ability as any) !== -1) {
      if (build.shoes![0] === "UNKNOWN") {
        handleChange({
          shoes: [ability, build.shoes![1], build.shoes![2], build.shoes![3]],
        })
      }
    } else {
      const headI = build.headgear!.indexOf("UNKNOWN")
      if (headI !== -1) {
        const copy = build.headgear!.slice()
        copy[headI] = ability as HeadOnlyAbility | StackableAbility
        handleChange({
          headgear: copy,
        })
        return
      }

      const clothingI = build.clothing!.indexOf("UNKNOWN")
      if (clothingI !== -1) {
        const copy = build.clothing!.slice()
        copy[clothingI] = ability as ClothingOnlyAbility | StackableAbility
        handleChange({
          clothing: copy,
        })
        return
      }

      const shoesI = build.shoes!.indexOf("UNKNOWN")
      if (shoesI !== -1) {
        const copy = build.shoes!.slice()
        copy[shoesI] = ability as ShoesOnlyAbility | StackableAbility
        handleChange({
          shoes: copy,
        })
      }
    }
  }

  const handleClickBuildAbility = (
    slot: "HEAD" | "CLOTHING" | "SHOES",
    index: number
  ) => {
    if (slot === "HEAD") {
      const copy = build.headgear!.slice()
      copy[index] = "UNKNOWN"
      handleChange({
        headgear: copy,
      })
    } else if (slot === "CLOTHING") {
      const copy = build.clothing!.slice()
      copy[index] = "UNKNOWN"
      handleChange({
        clothing: copy,
      })
    } else {
      const copy = build.shoes!.slice()
      copy[index] = "UNKNOWN"
      handleChange({
        shoes: copy,
      })
    }
  }

  const buildCanBeSubmitted = () => {
    if (!build.weapon) {
      return false
    }

    const abilitiesFilled =
      build.headgear?.every(ability => ability !== "UNKNOWN") &&
      build.clothing?.every(ability => ability !== "UNKNOWN") &&
      build.shoes?.every(ability => ability !== "UNKNOWN")

    if (!abilitiesFilled) {
      return false
    }

    return true
  }

  return (
    <Modal
      title={buildBeingEdited ? "Editing existing build" : "Adding a new build"}
      closeModal={() => closeModal()}
    >
      {buildBeingEdited && (
        <Box
          color="red.500"
          mb="1em"
          textDecoration="underline"
          cursor="pointer"
          onClick={() => deleteBuild()}
        >
          Delete build
        </Box>
      )}
      <WeaponSelector
        required
        label="Weapon"
        setValue={(weapon: Weapon) => handleChange({ weapon })}
        initialValue={build.weapon}
      />
      {build.weapon && (
        <WeaponImage englishName={build.weapon as Weapon} size="MEDIUM" />
      )}
      <Box asFlex mt="1em" justifyContent="space-between" flexWrap="wrap">
        <Box asFlex flexDirection="column" alignItems="center">
          <Select
            label="Headgear"
            options={headSelectOptions}
            clearable
            value={build.headgearItem}
            setValue={(headgearItem: HeadGear) => {
              if (
                (!build.headgear ||
                  build.headgear.every(ability => ability === "UNKNOWN")) &&
                existingGear.hasOwnProperty(headgearItem)
              ) {
                handleChange({
                  headgearItem,
                  headgear: existingGear[headgearItem],
                })
              } else {
                handleChange({ headgearItem })
              }
            }}
          />
          {build.headgearItem && (
            <Box mt="0.5em">
              <GearImage englishName={build.headgearItem} />
            </Box>
          )}
        </Box>
        <Box asFlex flexDirection="column" alignItems="center">
          <Select
            label="Clothing"
            options={clothingSelectOptions}
            clearable
            value={build.clothingItem}
            setValue={(clothingItem: ClothingGear) => {
              if (
                (!build.clothing ||
                  build.clothing.every(ability => ability === "UNKNOWN")) &&
                existingGear.hasOwnProperty(clothingItem)
              ) {
                handleChange({
                  clothingItem,
                  clothing: existingGear[clothingItem],
                })
              } else {
                handleChange({ clothingItem })
              }
            }}
          />
          {build.clothingItem && (
            <Box mt="0.5em">
              <GearImage englishName={build.clothingItem} />
            </Box>
          )}
        </Box>
        <Box asFlex flexDirection="column" alignItems="center">
          <Select
            label="Shoes"
            options={shoesSelectOptions}
            clearable
            value={build.shoesItem}
            setValue={(shoesItem: ShoesGear) => {
              if (
                (!build.shoes ||
                  build.shoes.every(ability => ability === "UNKNOWN")) &&
                existingGear.hasOwnProperty(shoesItem)
              ) {
                handleChange({
                  shoesItem,
                  shoes: existingGear[shoesItem],
                })
              } else {
                handleChange({ shoesItem })
              }
            }}
          />
          {build.shoesItem && (
            <Box mt="0.5em">
              <GearImage englishName={build.shoesItem} />
            </Box>
          )}
        </Box>
      </Box>
      <Box mt="1em">
        <Input
          value={build.title}
          setValue={(value: string) => handleChange({ title: value })}
          label="Title"
          limit={100}
        />
      </Box>
      <Box mt="1em">
        <ViewSlots build={build} onAbilityClick={handleClickBuildAbility} />
      </Box>
      <Box mt="1em">
        <AbilityButtons
          onClick={ability => handleAbilityButtonClick(ability)}
        />
      </Box>
      <Box mt="1em">
        <TextArea
          value={build.description}
          setValue={(value: string) => handleChange({ description: value })}
          label="Description"
          limit={1000}
        />
      </Box>
      <Box mt="1em">
        <Button
          disabled={!buildCanBeSubmitted()}
          onClick={buildBeingEdited ? () => updateBuild() : () => addBuild()}
        >
          Submit
        </Button>
        <Box as="span" ml="0.5em">
          <Button outlined onClick={() => closeModal()}>
            Cancel
          </Button>
        </Box>
      </Box>
    </Modal>
  )
}

export default BuildFormModal
