import { useMemo, useState } 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 { User } from "@/interfaces/user";
import useExamStore from "@/stores/exam";
import { getExamById } from "@/utils/exams";
import { countExercises } from "@/utils/moduleUtils";
import {
  createColumnHelper,
  flexRender,
  getCoreRowModel,
  useReactTable,
} from "@tanstack/react-table";
import axios from "axios";
import { capitalize } from "lodash";
import { useRouter } from "next/router";
import { BsPencil, BsTrash, BsUpload } from "react-icons/bs";
import { toast } from "react-toastify";
import { useListSearch } from "@/hooks/useListSearch";
import Modal from "@/components/Modal";
import { checkAccess, findAllowedEntities } from "@/utils/permissions";
import Button from "@/components/Low/Button";
import { EntityWithRoles } from "@/interfaces/entity";
import { BiEdit } from "react-icons/bi";
import { findBy, mapBy } from "@/utils";

const searchFields = [["module"], ["id"], ["createdBy"]];

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,
  entities,
}: {
  user: User;
  entities: EntityWithRoles[];
}) {
  const [selectedExam, setSelectedExam] = useState<Exam>();

  const canViewConfidentialEntities = useMemo(
    () =>
      mapBy(
        findAllowedEntities(user, entities, "view_confidential_exams"),
        "id"
      ),
    [user, entities]
  );

  const { exams, reload, isLoading } = useExams();
  const { users } = useUsers();
  // Pass this permission filter to the backend later
  const filteredExams = useMemo(
    () =>
      ["admin", "developer"].includes(user?.type)
        ? exams
        : exams.filter((item) => {
            if (
              item.access === "confidential" &&
              !canViewConfidentialEntities.find((x) =>
                (item.entities ?? []).includes(x)
              )
            )
              return false;
            return true;
          }),
    [canViewConfidentialEntities, exams, user?.type]
  );

  const parsedExams = useMemo(() => {
    return filteredExams.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;
    });
  }, [filteredExams, users]);

  const { rows: filteredRows, renderSearch } = useListSearch<Exam>(
    searchFields,
    parsedExams
  );

  const dispatch = useExamStore((state) => state.dispatch);

  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;
    }
    dispatch({
      type: "INIT_EXAM",
      payload: { exams: [exam], modules: [module] },
    });

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

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

    axios
      .patch(`/api/exam/${exam.module}/${exam.id}`, { private: !exam.private })
      .then(() => toast.success(`Updated 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 update this exam!");
          return;
        }

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

  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("access", {
      header: "Access",
      cell: (info) => <span>{capitalize(info.getValue())}</span>,
    }),
    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()
          ? "System"
          : findBy(users, "id", info.getValue())?.name || "N/A",
    }),
    {
      header: "",
      id: "actions",
      cell: ({ row }: { row: { original: Exam } }) => {
        return (
          <div className="flex gap-4">
            {(row.original.owners?.includes(user.id) ||
              checkAccess(user, ["admin", "developer"])) && (
              <>
                {checkAccess(user, [
                  "admin",
                  "developer",
                  "mastercorporate",
                ]) && (
                  <button
                    data-tip="Edit exam"
                    onClick={() => setSelectedExam(row.original)}
                    className="cursor-pointer tooltip"
                  >
                    <BsPencil />
                  </button>
                )}
              </>
            )}
            <button
              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" />
            </button>
            {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: filteredRows,
    columns: defaultColumns,
    getCoreRowModel: getCoreRowModel(),
  });

  const handleExamEdit = () => {
    router.push(
      `/generation?id=${selectedExam!.id}&module=${selectedExam!.module}`
    );
  };

  return (
    <div className="flex flex-col gap-4 w-full h-full">
      {renderSearch()}
      <Modal
        isOpen={!!selectedExam}
        onClose={() => setSelectedExam(undefined)}
        maxWidth="max-w-xl"
      >
        {!!selectedExam ? (
          <>
            <div className="p-6">
              <div className="mb-6">
                <div className="flex items-center gap-2 mb-4">
                  <BiEdit className="w-5 h-5 text-gray-600" />
                  <span className="text-gray-600 font-medium">
                    Ready to Edit
                  </span>
                </div>

                <div className="bg-gray-50 rounded-lg p-4 mb-3">
                  <p className="font-medium mb-1">Exam ID: {selectedExam.id}</p>
                </div>

                <p className="text-gray-500 text-sm">
                  Click &apos;Next&apos; to proceed to the exam editor.
                </p>
              </div>

              <div className="flex justify-between gap-4 mt-8">
                <Button
                  color="purple"
                  variant="outline"
                  onClick={() => setSelectedExam(undefined)}
                  className="w-32"
                >
                  Cancel
                </Button>
                <Button
                  color="purple"
                  onClick={handleExamEdit}
                  className="w-32 text-white flex items-center justify-center gap-2"
                >
                  Proceed
                </Button>
              </div>
            </div>
            {/*<ExamOwnerSelector options={filteredCorporates} exam={selectedExam} onSave={(owners) => updateExam(selectedExam, { owners })} />*/}
          </>
        ) : (
          <div />
        )}
      </Modal>
      <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>
      {isLoading ? (
        <div className="min-h-screen flex justify-center items-start">
          <span className="loading loading-infinity w-32" />
        </div>
      ) : (
        filteredRows.length === 0 && (
          <div className="w-full flex justify-center items-start">
            <span className="text-xl text-gray-500">No data found...</span>
          </div>
        )
      )}
    </div>
  );
}
