import { useMemo } from "react";
import { PERMISSIONS } from "@/constants/userPermissions";
import useExams from "@/hooks/useExams";
import useUsers from "@/hooks/useUsers";
import { Module } from "@/interfaces";
import { Exam } from "@/interfaces/exam";
import { Type, User } from "@/interfaces/user";
import useExamStore from "@/stores/examStore";
import { getExamById } from "@/utils/exams";
import { countExercises } from "@/utils/moduleUtils";
import {
  createColumnHelper,
  flexRender,
  getCoreRowModel,
  useReactTable,
} from "@tanstack/react-table";
import axios from "axios";
import clsx from "clsx";
import { capitalize } from "lodash";
import { useRouter } from "next/router";
import { BsCheck, BsTrash, BsUpload } from "react-icons/bs";
import { toast } from "react-toastify";

const CLASSES: { [key in Module]: string } = {
  reading: "text-ielts-reading",
  listening: "text-ielts-listening",
  speaking: "text-ielts-speaking",
  writing: "text-ielts-writing",
  level: "text-ielts-level",
};

const columnHelper = createColumnHelper<Exam>();

export default function ExamList({ user }: { user: User }) {
  const { exams, reload } = useExams();
  const { users } = useUsers();

  const parsedExams = useMemo(() => {
    return exams.map((exam) => {
      if (exam.createdBy) {
        const user = users.find((u) => u.id === exam.createdBy);
        if (!user) return exam;

        return {
          ...exam,
          createdBy: user.type === "developer" ? "system" : user.name,
        };
      }

      return exam;
    });
  }, [exams, users]);

  const setExams = useExamStore((state) => state.setExams);
  const setSelectedModules = useExamStore((state) => state.setSelectedModules);

  const router = useRouter();

  const loadExam = async (module: Module, examId: string) => {
    const exam = await getExamById(module, examId.trim());
    if (!exam) {
      toast.error(
        "Unknown Exam ID! Please make sure you selected the right module and entered the right exam ID",
        {
          toastId: "invalid-exam-id",
        }
      );

      return;
    }

    setExams([exam]);
    setSelectedModules([module]);

    router.push("/exercises");
  };

  const deleteExam = async (exam: Exam) => {
    if (
      !confirm(
        `Are you sure you want to delete this ${capitalize(exam.module)} exam?`
      )
    )
      return;

    axios
      .delete(`/api/exam/${exam.module}/${exam.id}`)
      .then(() => toast.success(`Deleted the "${exam.id}" exam`))
      .catch((reason) => {
        if (reason.response.status === 404) {
          toast.error("Exam not found!");
          return;
        }

        if (reason.response.status === 403) {
          toast.error("You do not have permission to delete this exam!");
          return;
        }

        toast.error("Something went wrong, please try again later.");
      })
      .finally(reload);
  };

  const getTotalExercises = (exam: Exam) => {
    if (
      exam.module === "reading" ||
      exam.module === "listening" ||
      exam.module === "level"
    ) {
      return countExercises(exam.parts.flatMap((x) => x.exercises));
    }

    return countExercises(exam.exercises);
  };

  const defaultColumns = [
    columnHelper.accessor("id", {
      header: "ID",
      cell: (info) => info.getValue(),
    }),
    columnHelper.accessor("module", {
      header: "Module",
      cell: (info) => (
        <span className={CLASSES[info.getValue()]}>
          {capitalize(info.getValue())}
        </span>
      ),
    }),
    columnHelper.accessor((x) => getTotalExercises(x), {
      header: "Exercises",
      cell: (info) => info.getValue(),
    }),
    columnHelper.accessor("minTimer", {
      header: "Timer",
      cell: (info) => <>{info.getValue()} minute(s)</>,
    }),
    columnHelper.accessor("createdAt", {
      header: "Created At",
      cell: (info) => {
        const value = info.getValue();
        if (value) {
          return new Date(value).toLocaleDateString();
        }

        return null;
      },
    }),
    columnHelper.accessor("createdBy", {
      header: "Created By",
      cell: (info) => info.getValue(),
    }),
    {
      header: "",
      id: "actions",
      cell: ({ row }: { row: { original: Exam } }) => {
        return (
          <div className="flex gap-4">
            <div
              data-tip="Load exam"
              className="cursor-pointer tooltip"
              onClick={async () =>
                await loadExam(row.original.module, row.original.id)
              }
            >
              <BsUpload className="hover:text-mti-purple-light transition ease-in-out duration-300" />
            </div>
            {PERMISSIONS.examManagement.delete.includes(user.type) && (
              <div
                data-tip="Delete"
                className="cursor-pointer tooltip"
                onClick={() => deleteExam(row.original)}
              >
                <BsTrash className="hover:text-mti-purple-light transition ease-in-out duration-300" />
              </div>
            )}
          </div>
        );
      },
    },
  ];

  const table = useReactTable({
    data: parsedExams,
    columns: defaultColumns,
    getCoreRowModel: getCoreRowModel(),
  });

  return (
    <table className="rounded-xl bg-mti-purple-ultralight/40 w-full">
      <thead>
        {table.getHeaderGroups().map((headerGroup) => (
          <tr key={headerGroup.id}>
            {headerGroup.headers.map((header) => (
              <th className="p-4 text-left" key={header.id}>
                {header.isPlaceholder
                  ? null
                  : flexRender(
                      header.column.columnDef.header,
                      header.getContext()
                    )}
              </th>
            ))}
          </tr>
        ))}
      </thead>
      <tbody className="px-2">
        {table.getRowModel().rows.map((row) => (
          <tr
            className="odd:bg-white even:bg-mti-purple-ultralight/40 rounded-lg py-2"
            key={row.id}
          >
            {row.getVisibleCells().map((cell) => (
              <td className="px-4 py-2" key={cell.id}>
                {flexRender(cell.column.columnDef.cell, cell.getContext())}
              </td>
            ))}
          </tr>
        ))}
      </tbody>
    </table>
  );
}
