import { getAuth } from "@clerk/remix/ssr.server";
import { LoaderFunction, json, redirect } from "@remix-run/node";
import { Link, useParams, useRouteError } from "@remix-run/react";
import {
  CheckCircleIcon,
  CopyIcon,
  CrownIcon,
  DownloadIcon,
  EyeIcon,
  EyeOffIcon,
  HourglassIcon,
  RefreshCwIcon,
  SaveIcon,
  ShieldIcon,
  StarIcon,
} from "lucide-react";
import { useEffect, useState } from "react";
import LoadingIndicator from "~/components/LoadingIndicator";
import { useEventSource } from "remix-utils/sse/react";
import { PresenceItem, RoomResponse, VoteResponse } from "~/services/types";
import { isAdmin, jsonToCsv } from "~/services/helpers";
import { useUser } from "@clerk/remix";
import { db } from "~/services/db.server";
import { rooms } from "~/services/schema";
import { eq } from "drizzle-orm";
import { shitList } from "~/services/consts";

// Loader
export const loader: LoaderFunction = async (args) => {
  const { userId, sessionClaims } = await getAuth(args);

  if (!userId) {
    return redirect("/sign-in");
  }

  const room = await db.query.rooms.findFirst({
    where: eq(rooms.id, args.params.roomId as string),
  });

  if (!room) {
    throw new Response(null, {
      status: 404,
      statusText: "Not Found",
    });
  }

  let isShit = false;
  const email = sessionClaims.email as string;

  shitList.forEach((shitItem) => {
    if (email.includes(shitItem)) {
      isShit = true;
    }
  });

  if (isShit) {
    return redirect(
      "https://www.youtube.com/watch?v=dQw4w9WgXcQ&pp=ygUXbmV2ZXIgZ29ubmEgZ2l2ZSB5b3UgdXA%3D"
    );
  }

  return {};
};

// Checks for 404
export function ErrorBoundary() {
  return (
    <span className="text-center">
      <h1 className="text-5xl font-bold m-2">4️⃣0️⃣4️⃣</h1>
      <h1 className="text-5xl font-bold m-2">
        Oops! This room does not appear to exist, or may have been deleted! 😢
      </h1>
      <Link
        about="Back to home."
        to="/"
        className="btn btn-secondary normal-case text-xl m-2"
      >
        Back to Home
      </Link>
    </span>
  );
}

export default function Room() {
  const { user } = useUser();
  const params = useParams();
  const roomId = params.roomId;

  let roomFromDb = useEventSource(`/api/room/get/${roomId}`, {
    event: `room-${params.roomId}`,
  });

  let votesFromDb = useEventSource(`/api/votes/get/${roomId}`, {
    event: `votes-${params.roomId}`,
  });

  let presenceData = useEventSource(`/api/room/presence/get/${roomId}`, {
    event: `${user?.id}-${params.roomId}`,
  });

  let roomFromDbParsed = (roomFromDb ? JSON.parse(roomFromDb!) : undefined) as
    | RoomResponse
    | null
    | undefined;

  let votesFromDbParsed = JSON.parse(votesFromDb!) as VoteResponse | undefined;
  let presenceDateParsed = JSON.parse(presenceData!) as
    | PresenceItem[]
    | undefined;

  const [storyNameText, setStoryNameText] = useState<string>("");
  const [roomScale, setRoomScale] = useState<string>("");

  const [copied, setCopied] = useState<boolean>(false);

  // Handlers
  // =================================
  async function setVoteHandler(value: string) {
    if (roomFromDb) {
      await fetch(`/api/vote/set/${roomId}`, {
        cache: "no-cache",
        method: "PUT",
        body: JSON.stringify({
          value,
        }),
      });
    }
  }

  async function setRoomHandler(data: {
    visible: boolean;
    reset: boolean | undefined;
    log: boolean | undefined;
  }) {
    if (roomFromDb) {
      await fetch(`/api/room/set/${roomId}`, {
        cache: "no-cache",
        method: "PUT",
        body: JSON.stringify({
          name: storyNameText,
          visible: data.visible,
          scale: roomScale,
          reset: data.reset ? data.reset : false,
          log: data.log ? data.log : false,
        }),
      });
    }
  }

  // Helpers
  // =================================
  const getVoteForCurrentUser = () => {
    if (roomFromDb) {
      return (
        votesFromDbParsed &&
        votesFromDbParsed.find((vote) => vote.userId === user?.id)
      );
    } else {
      return null;
    }
  };

  const downloadLogs = () => {
    if (roomFromDbParsed && votesFromDbParsed) {
      const jsonObject = roomFromDbParsed?.logs
        .map((item) => {
          return {
            id: item.id,
            created_at: item.created_at,
            userId: item.userId,
            roomId: item.roomId,
            roomName: item.roomName,
            storyName: item.storyName,
            scale: item.scale,
            votes: item.votes,
          };
        })
        .concat({
          id: "LATEST",
          created_at: new Date(),
          userId: roomFromDbParsed.userId,
          roomId: roomFromDbParsed.id,
          roomName: roomFromDbParsed.roomName,
          storyName: storyNameText,
          scale: roomScale,
          votes: votesFromDbParsed?.map((vote) => {
            return {
              value: vote.value,
            };
          }),
        });

      jsonToCsv(jsonObject!, `sp_${roomId}.csv`);
    }
  };

  const copyRoomURLHandler = () => {
    navigator.clipboard
      .writeText(window.location.href)
      .then(() => {
        console.log(`Copied Room Link to Clipboard!`);
        setCopied(true);
        setTimeout(() => {
          setCopied(false);
        }, 2000);
      })
      .catch(() => {
        console.log(`Error Copying Room Link to Clipboard!`);
      });
  };

  const voteString = (
    visible: boolean,
    votes: typeof votesFromDbParsed,
    presenceItem: PresenceItem
  ) => {
    const matchedVote = votes?.find(
      (vote) => vote.userId === presenceItem.userId
    );

    if (visible) {
      if (!!matchedVote) {
        return <div>{matchedVote.value}</div>;
      } else {
        return <HourglassIcon className="text-xl text-error" />;
      }
    } else if (!!matchedVote) {
      return <CheckCircleIcon className="text-xl text-success" />;
    } else {
      return <HourglassIcon className="text-xl animate-spin text-warning" />;
    }
  };

  // Hooks
  // =================================
  useEffect(() => {
    if (roomFromDb) {
      setStoryNameText(roomFromDbParsed?.storyName || "");
      setRoomScale(roomFromDbParsed?.scale || "ERROR");
    }
  }, [roomFromDb]);

  // UI
  // =================================
  // Room is loading
  if (!roomFromDbParsed) {
    return <LoadingIndicator />;
    // Room has been loaded
  } else {
    return (
      <div className="flex flex-col gap-4 text-center justify-center items-center">
        <div className="text-2xl">{roomFromDbParsed?.roomName}</div>
        <div className="flex flex-row flex-wrap text-center justify-center items-center gap-1 text-md">
          <div>ID:</div>
          <div>{roomFromDbParsed?.id}</div>

          <button>
            {copied ? (
              <CheckCircleIcon className="mx-1 text-success animate-bounce" />
            ) : (
              <CopyIcon
                className="mx-1 hover:text-primary"
                onClick={copyRoomURLHandler}
              />
            )}
          </button>
        </div>

        {roomFromDb && (
          <div className="card card-compact bg-base-100 shadow-xl">
            <div className="card-body">
              <h2 className="card-title mx-auto">
                Story: {roomFromDbParsed?.storyName}
              </h2>

              <ul className="p-0 flex flex-row flex-wrap justify-center items-center text-ceter gap-4">
                {presenceData &&
                  presenceDateParsed
                    ?.filter(
                      (value, index, self) =>
                        index ===
                        self.findIndex(
                          (presenceItem) => presenceItem.userId === value.userId
                        )
                    )
                    .map((presenceItem) => {
                      return (
                        <li
                          key={presenceItem.userId}
                          className="flex flex-row items-center justify-center gap-2"
                        >
                          <div className="w-10 rounded-full avatar">
                            <img
                              src={presenceItem.userImageUrl}
                              alt={`${presenceItem.userFullName}'s Profile Picture`}
                              height={32}
                              width={32}
                            />
                          </div>

                          <p className="flex flex-row flex-wrap text-center justify-center items-center gap-1 text-md">
                            {presenceItem.userFullName}{" "}
                            {presenceItem.isAdmin && (
                              <span
                                className="tooltip tooltip-primary"
                                data-tip="Admin"
                              >
                                <ShieldIcon className="inline-block text-primary" />
                              </span>
                            )}{" "}
                            {presenceItem.isVIP && (
                              <span
                                className="tooltip tooltip-secondary"
                                data-tip="VIP"
                              >
                                <StarIcon className="inline-block text-secondary" />
                              </span>
                            )}{" "}
                            {presenceItem.userId ===
                              roomFromDbParsed?.userId && (
                              <span
                                className="tooltip tooltip-warning"
                                data-tip="Room Owner"
                              >
                                <CrownIcon className="inline-block text-yellow-500" />
                              </span>
                            )}
                            {" : "}
                          </p>

                          {roomFromDb &&
                            votesFromDb &&
                            voteString(
                              roomFromDbParsed?.visible!,
                              votesFromDbParsed,
                              presenceItem
                            )}
                        </li>
                      );
                    })}
              </ul>

              <div className="join md:btn-group-horizontal mx-auto">
                {roomFromDbParsed?.scale?.split(",").map((scaleItem, index) => {
                  return (
                    <button
                      key={index}
                      className={`join-item ${
                        getVoteForCurrentUser()?.value === scaleItem
                          ? "btn btn-active btn-primary"
                          : "btn"
                      }`}
                      onClick={() => void setVoteHandler(scaleItem)}
                    >
                      {scaleItem}
                    </button>
                  );
                })}
              </div>
            </div>
          </div>
        )}

        {!!roomFromDbParsed &&
          (roomFromDbParsed.userId === user?.id ||
            isAdmin(user?.publicMetadata)) && (
            <>
              <div className="card card-compact bg-base-100 shadow-xl">
                <div className="card-body flex flex-col flex-wrap">
                  <h2 className="card-title">Room Settings</h2>

                  <label className="label">
                    {"Vote Scale (Comma Separated):"}{" "}
                  </label>

                  <input
                    type="text"
                    placeholder="Scale (Comma Separated)"
                    className="input input-bordered"
                    value={roomScale}
                    onChange={(event) => {
                      setRoomScale(event.target.value);
                    }}
                  />

                  <label className="label">{"Story Name:"} </label>

                  <input
                    type="text"
                    placeholder="Story Name"
                    className="input input-bordered"
                    value={storyNameText}
                    onChange={(event) => {
                      setStoryNameText(event.target.value);
                    }}
                  />

                  <div className="flex flex-row flex-wrap text-center items-center justify-center gap-2">
                    <div>
                      <button
                        onClick={() =>
                          void setRoomHandler({
                            visible: !roomFromDbParsed?.visible,
                            reset: false,
                            log: false,
                          })
                        }
                        className="btn btn-primary inline-flex"
                      >
                        {roomFromDbParsed.visible ? (
                          <>
                            <EyeOffIcon className="text-xl mr-1" />
                            Hide
                          </>
                        ) : (
                          <>
                            <EyeIcon className="text-xl mr-1" />
                            Show
                          </>
                        )}
                      </button>
                    </div>

                    <div>
                      <button
                        onClick={() =>
                          void setRoomHandler({
                            visible: false,
                            reset: true,
                            log:
                              roomFromDbParsed?.storyName === storyNameText ||
                              votesFromDb?.length === 0
                                ? false
                                : true,
                          })
                        }
                        className="btn btn-primary inline-flex"
                        disabled={
                          [...new Set(roomScale.split(","))].filter(
                            (item) => item !== ""
                          ).length <= 1
                        }
                      >
                        {roomFromDbParsed?.storyName === storyNameText ||
                        votesFromDb?.length === 0 ? (
                          <>
                            <RefreshCwIcon className="text-xl mr-1" /> Reset
                          </>
                        ) : (
                          <>
                            <SaveIcon className="text-xl mr-1" /> Save
                          </>
                        )}
                      </button>
                    </div>

                    {votesFromDb &&
                      (roomFromDbParsed?.logs.length > 0 ||
                        votesFromDb.length > 0) && (
                        <div>
                          <button
                            onClick={() => downloadLogs()}
                            className="btn btn-primary inline-flex hover:animate-pulse"
                          >
                            <>
                              <DownloadIcon className="text-xl" />
                            </>
                          </button>
                        </div>
                      )}
                  </div>
                </div>
              </div>
            </>
          )}
      </div>
    );
  }
}
