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 {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, uniq} from "lodash";
import {useRouter} from "next/router";
import {BsBan, BsBanFill, BsCheck, BsCircle, BsPencil, BsStop, BsTrash, BsUpload, BsX} from "react-icons/bs";
import {toast} from "react-toastify";
import {useListSearch} from "@/hooks/useListSearch";
import Modal from "@/components/Modal";
import {checkAccess} from "@/utils/permissions";
import useGroups from "@/hooks/useGroups";
import Button from "@/components/Low/Button";

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>();

const ExamOwnerSelector = ({options, exam, onSave}: {options: User[]; exam: Exam; onSave: (owners: string[]) => void}) => {
	const [owners, setOwners] = useState(exam.owners || []);

	return (
		<div className="w-full flex flex-col gap-4">
			<div className="grid grid-cols-4 mt-4">
				{options.map((c) => (
					<Button
						variant={owners.includes(c.id) ? "solid" : "outline"}
						onClick={() => setOwners((prev) => (prev.includes(c.id) ? prev.filter((x) => x !== c.id) : [...prev, c.id]))}
						className="max-w-[200px] w-full"
						key={c.id}>
						{c.name}
					</Button>
				))}
			</div>
			<Button onClick={() => onSave(owners)} className="w-full max-w-[200px] self-end">
				Save
			</Button>
		</div>
	);
};

export default function ExamList({user}: {user: User}) {
	const [selectedExam, setSelectedExam] = useState<Exam>();

	const {exams, reload} = useExams();
	const {users} = useUsers();
	const {groups} = useGroups({admin: user?.id, userType: user?.type});

	const filteredCorporates = useMemo(() => {
		const participantsAndAdmins = uniq(groups.flatMap((x) => [...x.participants, x.admin])).filter((x) => x !== user?.id);
		return users.filter((x) => participantsAndAdmins.includes(x.id) && x.type === "corporate");
	}, [users, groups, user]);

	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 {rows: filteredRows, renderSearch} = useListSearch<Exam>(searchFields, parsedExams);

	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 privatizeExam = async (exam: Exam) => {
		if (!confirm(`Are you sure you want to make this ${capitalize(exam.module)} exam ${exam.private ? "public" : "private"}?`)) 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 updateExam = async (exam: Exam, body: object) => {
		if (!confirm(`Are you sure you want to update this ${capitalize(exam.module)} exam?`)) return;

		axios
			.patch(`/api/exam/${exam.module}/${exam.id}`, body)
			.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)
			.finally(() => setSelectedExam(undefined));
	};

	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("private", {
			header: "Private",
			cell: (info) => <span className="w-full flex items-center justify-center">{!info.getValue() ? <BsX /> : <BsCheck />}</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(),
		}),
		{
			header: "",
			id: "actions",
			cell: ({row}: {row: {original: Exam}}) => {
				return (
					<div className="flex gap-4">
						{(row.original.owners?.includes(user.id) || checkAccess(user, ["admin", "developer"])) && (
							<>
								<button
									data-tip={row.original.private ? "Set as public" : "Set as private"}
									onClick={async () => await privatizeExam(row.original)}
									className="cursor-pointer tooltip">
									{row.original.private ? <BsCircle /> : <BsBan />}
								</button>
								{checkAccess(user, ["admin", "developer", "mastercorporate"]) && (
									<button data-tip="Edit owners" 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(),
	});

	return (
		<div className="flex flex-col gap-4 w-full h-full">
			{renderSearch()}
			<Modal isOpen={!!selectedExam} title={`Edit Exam Owners - ${selectedExam?.id}`} onClose={() => setSelectedExam(undefined)}>
				{!!selectedExam ? (
					<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>
		</div>
	);
}
