import {
  ButtonHTMLAttributes,
  Fragment,
  ReactNode,
  useEffect,
  useRef,
  useState,
} from "react";
import { ReactSortable, ReactSortableProps } from "react-sortablejs";
import { apiAuth, User } from "./api";
import { useSession } from "./Session";
import { Chemistry, MVPRanking } from "./types";
import TabController from "./TabController";

type PlayerListProps = Partial<ReactSortableProps<any>> & {
  orderedList?: boolean;
};

function PlayerList(props: PlayerListProps) {
  return (
    <ReactSortable {...props} animation={200} swapThreshold={0.4}>
      {props.list?.map((item, index) => (
        <div key={item.id} className="item">
          {props.orderedList
            ? index + 1 + ". " + item.display_name
            : item.display_name}
        </div>
      ))}
    </ReactSortable>
  );
}

const LoadButton = (props: ButtonHTMLAttributes<HTMLButtonElement>) => {
  return (
    <button {...props} style={{ padding: "4px 16px" }}>
      🗃️ restore previous
    </button>
  );
};

const ClearButton = (props: ButtonHTMLAttributes<HTMLButtonElement>) => {
  return (
    <button {...props} style={{ padding: "4px 16px" }}>
      🗑️ start over
    </button>
  );
};

function filterSort(list: User[], ids: number[]): User[] {
  const objectMap = new Map(list.map((obj) => [obj.id, obj]));
  const filteredAndSortedObjects = ids
    .map((id) => objectMap.get(id))
    .filter((obj) => obj !== undefined);
  return filteredAndSortedObjects;
}

interface PlayerInfoProps {
  user: User;
  players: User[];
}

function ChemistryDnD({ user, players }: PlayerInfoProps) {
  var otherPlayers = players.filter((player) => player.id !== user.id);
  const [playersLeft, setPlayersLeft] = useState<User[]>([]);
  const [playersMiddle, setPlayersMiddle] = useState<User[]>(otherPlayers);
  const [playersRight, setPlayersRight] = useState<User[]>([]);

  useEffect(() => {
    setPlayersMiddle(otherPlayers);
  }, [players]);

  const [dialog, setDialog] = useState("dialog");
  const dialogRef = useRef<HTMLDialogElement>(null);

  async function handleSubmit() {
    if (dialogRef.current) dialogRef.current.showModal();
    setDialog("sending...");
    let left = playersLeft.map(({ id }) => id);
    let middle = playersMiddle.map(({ id }) => id);
    let right = playersRight.map(({ id }) => id);
    const data = { user: user.id, hate: left, undecided: middle, love: right };
    const response = await apiAuth("chemistry", data, "PUT");
    setDialog(response || "try sending again");
  }

  async function handleGet() {
    const chemistry = (await apiAuth("chemistry", null, "GET")) as Chemistry;
    setPlayersLeft(filterSort(otherPlayers, chemistry.hate));
    setPlayersMiddle(
      otherPlayers.filter(
        (player) =>
          !chemistry.hate.includes(player.id) &&
          !chemistry.love.includes(player.id)
      )
    );
    setPlayersRight(filterSort(otherPlayers, chemistry.love));
  }

  return (
    <>
      <HeaderControl
        onLoad={handleGet}
        onClear={() => {
          setPlayersRight([]);
          setPlayersMiddle(otherPlayers);
          setPlayersLeft([]);
        }}
      />
      <div className="container">
        <div className="box three">
          <h2>😬</h2>
          {playersLeft.length < 1 && (
            <span className="grey hint">
              drag people here that you'd rather not play with
            </span>
          )}
          <PlayerList
            list={playersLeft}
            setList={setPlayersLeft}
            group={"shared"}
            className="dragbox"
          />
        </div>
        <div className="box three">
          <h2>🤷</h2>
          <PlayerList
            list={playersMiddle}
            setList={setPlayersMiddle}
            group={"shared"}
            className="middle dragbox"
          />
        </div>
        <div className="box three">
          <h2>😍</h2>
          {playersRight.length < 1 && (
            <span className="grey hint">
              drag people here that you love playing with from best to ... ok
            </span>
          )}
          <PlayerList
            list={playersRight}
            setList={setPlayersRight}
            group={"shared"}
            className="dragbox"
            orderedList
          />
        </div>
      </div>

      <button className="submit wavering" onClick={() => handleSubmit()}>
        💾 <span className="submit_text">submit</span>
      </button>
      <dialog
        ref={dialogRef}
        id="ChemistryDialog"
        onClick={(event) => {
          event.currentTarget.close();
        }}
      >
        {dialog}
      </dialog>
    </>
  );
}

function MVPDnD({ user, players }: PlayerInfoProps) {
  const [availablePlayers, setAvailablePlayers] = useState<User[]>(players);
  const [rankedPlayers, setRankedPlayers] = useState<User[]>([]);

  useEffect(() => {
    setAvailablePlayers(players);
  }, [players]);

  const [dialog, setDialog] = useState("dialog");
  const dialogRef = useRef<HTMLDialogElement>(null);

  async function handleSubmit() {
    if (dialogRef.current) dialogRef.current.showModal();
    setDialog("sending...");
    let mvps = rankedPlayers.map(({ id }) => id);
    const data = { user: user.id, mvps: mvps };
    const response = await apiAuth("mvps", data, "PUT");
    response ? setDialog(response) : setDialog("try sending again");
  }

  async function handleGet() {
    const mvps = (await apiAuth("mvps", null, "GET")) as MVPRanking;
    setRankedPlayers(filterSort(players, mvps.mvps));
    setAvailablePlayers(players.filter((user) => !mvps.mvps.includes(user.id)));
  }

  return (
    <>
      <HeaderControl
        onLoad={handleGet}
        onClear={() => {
          setAvailablePlayers(players);
          setRankedPlayers([]);
        }}
      />
      <div className="container">
        <div className="box two">
          <h2>🥏🏃</h2>
          {availablePlayers.length < 1 && (
            <span className="grey hint">all sorted 👍</span>
          )}
          <PlayerList
            list={availablePlayers}
            setList={setAvailablePlayers}
            group={{
              name: "mvp-shared",
              pull: function (to) {
                return to.el.classList.contains("putclone") ? "clone" : true;
              },
            }}
            className="dragbox"
          />
        </div>
        <div className="box two">
          <h1>🏆</h1>
          {rankedPlayers.length < 1 && (
            <span className="grey hint">
              carefully place as many of the <i>Most Valuable Players</i>{" "}
              (according to your humble opinion) in this box
            </span>
          )}
          <PlayerList
            list={rankedPlayers}
            setList={setRankedPlayers}
            group={{
              name: "mvp-shared",
              pull: function (to) {
                return to.el.classList.contains("putclone") ? "clone" : true;
              },
            }}
            className="dragbox"
            orderedList
          />
        </div>
      </div>

      <button className="submit wavering" onClick={() => handleSubmit()}>
        💾 <span className="submit_text">submit</span>
      </button>
      <dialog
        ref={dialogRef}
        id="MVPDialog"
        onClick={(event) => {
          event.currentTarget.close();
        }}
      >
        {dialog}
      </dialog>
    </>
  );
}

interface HeaderControlProps {
  onLoad: () => void;
  onClear: () => void;
}
function HeaderControl({ onLoad, onClear }: HeaderControlProps) {
  return (
    <>
      <div>
        <LoadButton onClick={onLoad} />
        <ClearButton onClick={onClear} />
      </div>
      <div>
        <span className="grey">
          assign as many or as few players as you want and don't forget to{" "}
          <b>submit</b> 💾 when you're done :)
        </span>
      </div>
    </>
  );
}

export default function Rankings() {
  const { user } = useSession();
  const [players, setPlayers] = useState<User[] | null>(null);

  async function loadPlayers() {
    try {
      const data = await apiAuth("player/list", null, "GET");
      setPlayers(data as User[]);
    } catch (error) {
      console.error(error);
    }
  }

  useEffect(() => {
    loadPlayers();
  }, [user]);

  const tabs = [
    { id: "Chemistry", label: "🧪 Chemistry" },
    { id: "MVP", label: "🏆 MVP" },
  ];

  return (
    <>
      {user && players ? (
        <TabController tabs={tabs}>
          <ChemistryDnD {...{ user, players }} />
          <MVPDnD {...{ user, players }} />
        </TabController>
      ) : (
        <span className="loader" />
      )}
    </>
  );
}
