import Button from "@/components/Low/Button";
import Checkbox from "@/components/Low/Checkbox";
import Select from "@/components/Low/Select";
import useCodes from "@/hooks/useCodes";
import useUser from "@/hooks/useUser";
import useUsers from "@/hooks/useUsers";
import {Code, User} from "@/interfaces/user";
import {USER_TYPE_LABELS} from "@/resources/user";
import {createColumnHelper, flexRender, getCoreRowModel, useReactTable} from "@tanstack/react-table";
import axios from "axios";
import moment from "moment";
import {useEffect, useState, useMemo} from "react";
import {BsTrash} from "react-icons/bs";
import {toast} from "react-toastify";
import ReactDatePicker from "react-datepicker";
import clsx from "clsx";
import {checkAccess} from "@/utils/permissions";
import usePermissions from "@/hooks/usePermissions";

const columnHelper = createColumnHelper<Code>();

const CreatorCell = ({id, users}: {id: string; users: User[]}) => {
	const [creatorUser, setCreatorUser] = useState<User>();

	useEffect(() => {
		setCreatorUser(users.find((x) => x.id === id));
	}, [id, users]);

	return (
		<>
			{(creatorUser?.type === "corporate" ? creatorUser?.corporateInformation?.companyInformation?.name : creatorUser?.name || "N/A") || "N/A"}{" "}
			{creatorUser && `(${USER_TYPE_LABELS[creatorUser.type]})`}
		</>
	);
};

export default function CodeList({user}: {user: User}) {
	const [selectedCodes, setSelectedCodes] = useState<string[]>([]);

	const [filteredCorporate, setFilteredCorporate] = useState<User | undefined>(user?.type === "corporate" ? user : undefined);
	const [filterAvailability, setFilterAvailability] = useState<"in-use" | "unused">();

	const {permissions} = usePermissions(user?.id || "");

	// const [filteredCodes, setFilteredCodes] = useState<Code[]>([]);

	const {users} = useUsers();
	const {codes, reload} = useCodes(user?.type === "corporate" ? user?.id : undefined);

	const [startDate, setStartDate] = useState<Date | null>(moment("01/01/2023").toDate());
	const [endDate, setEndDate] = useState<Date | null>(moment().endOf("day").toDate());
	const filteredCodes = useMemo(() => {
		return codes.filter((x) => {
			// TODO: if the expiry date is missing, it does not make sense to filter by date
			// so we need to find a way to handle this edge case
			if (startDate && endDate && x.expiryDate) {
				const date = moment(x.expiryDate);
				if (date.isBefore(startDate) || date.isAfter(endDate)) {
					return false;
				}
			}
			if (filteredCorporate && x.creator !== filteredCorporate.id) return false;
			if (filterAvailability) {
				if (filterAvailability === "in-use" && !x.userId) return false;
				if (filterAvailability === "unused" && x.userId) return false;
			}

			return true;
		});
	}, [codes, startDate, endDate, filteredCorporate, filterAvailability]);

	const toggleCode = (id: string) => {
		setSelectedCodes((prev) => (prev.includes(id) ? prev.filter((x) => x !== id) : [...prev, id]));
	};

	const toggleAllCodes = (checked: boolean) => {
		if (checked) return setSelectedCodes(filteredCodes.filter((x) => !x.userId).map((x) => x.code));

		return setSelectedCodes([]);
	};

	const deleteCodes = async (codes: string[]) => {
		if (!confirm(`Are you sure you want to delete these ${codes.length} code(s)?`)) return;

		const params = new URLSearchParams();
		codes.forEach((code) => params.append("code", code));

		axios
			.delete(`/api/code?${params.toString()}`)
			.then(() => {
				toast.success(`Deleted the codes!`);
				setSelectedCodes([]);
			})
			.catch((reason) => {
				if (reason.response.status === 404) {
					toast.error("Code not found!");
					return;
				}

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

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

	const deleteCode = async (code: Code) => {
		if (!confirm(`Are you sure you want to delete this "${code.code}" code?`)) return;

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

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

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

	const allowedToDelete = checkAccess(user, ["developer", "admin", "corporate", "mastercorporate"], permissions, "deleteCodes");

	const defaultColumns = [
		columnHelper.accessor("code", {
			id: "codeCheckbox",
			header: () => (
				<Checkbox
					disabled={filteredCodes.filter((x) => !x.userId).length === 0}
					isChecked={
						selectedCodes.length === filteredCodes.filter((x) => !x.userId).length && filteredCodes.filter((x) => !x.userId).length > 0
					}
					onChange={(checked) => toggleAllCodes(checked)}>
					{""}
				</Checkbox>
			),
			cell: (info) =>
				!info.row.original.userId ? (
					<Checkbox isChecked={selectedCodes.includes(info.getValue())} onChange={() => toggleCode(info.getValue())}>
						{""}
					</Checkbox>
				) : null,
		}),
		columnHelper.accessor("code", {
			header: "Code",
			cell: (info) => info.getValue(),
		}),
		columnHelper.accessor("creationDate", {
			header: "Creation Date",
			cell: (info) => (info.getValue() ? moment(info.getValue()).format("DD/MM/YYYY") : "N/A"),
		}),
		columnHelper.accessor("email", {
			header: "Invited E-mail",
			cell: (info) => info.getValue() || "N/A",
		}),
		columnHelper.accessor("creator", {
			header: "Creator",
			cell: (info) => <CreatorCell id={info.getValue()} users={users} />,
		}),
		columnHelper.accessor("userId", {
			header: "Availability",
			cell: (info) =>
				info.getValue() ? (
					<span className="flex gap-1 items-center text-mti-green">
						<div className="w-2 h-2 rounded-full bg-mti-green" /> In Use
					</span>
				) : (
					<span className="flex gap-1 items-center text-mti-red">
						<div className="w-2 h-2 rounded-full bg-mti-red" /> Unused
					</span>
				),
		}),
		{
			header: "",
			id: "actions",
			cell: ({row}: {row: {original: Code}}) => {
				return (
					<div className="flex gap-4">
						{allowedToDelete && !row.original.userId && (
							<div data-tip="Delete" className="cursor-pointer tooltip" onClick={() => deleteCode(row.original)}>
								<BsTrash className="hover:text-mti-purple-light transition ease-in-out duration-300" />
							</div>
						)}
					</div>
				);
			},
		},
	];

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

	return (
		<>
			<div className="flex items-center justify-between pb-4 pt-1">
				<div className="flex items-center gap-4">
					<Select
						className="!w-96 !py-1"
						disabled={user?.type === "corporate"}
						isClearable
						placeholder="Corporate"
						value={
							filteredCorporate
								? {
										label: `${
											filteredCorporate.type === "corporate"
												? filteredCorporate.corporateInformation?.companyInformation?.name || filteredCorporate.name
												: filteredCorporate.name
										} (${USER_TYPE_LABELS[filteredCorporate.type]})`,
										value: filteredCorporate.id,
								  }
								: null
						}
						options={users
							.filter((x) => ["admin", "developer", "corporate"].includes(x.type))
							.map((x) => ({
								label: `${x.type === "corporate" ? x.corporateInformation?.companyInformation?.name || x.name : x.name} (${
									USER_TYPE_LABELS[x.type]
								})`,
								value: x.id,
								user: x,
							}))}
						onChange={(value) => setFilteredCorporate(value ? users.find((x) => x.id === value?.value) : undefined)}
					/>
					<Select
						className="!w-96 !py-1"
						placeholder="Availability"
						isClearable
						options={[
							{label: "In Use", value: "in-use"},
							{label: "Unused", value: "unused"},
						]}
						onChange={(value) => setFilterAvailability(value ? (value.value as typeof filterAvailability) : undefined)}
					/>
					<ReactDatePicker
						dateFormat="dd/MM/yyyy"
						className="px-4 py-6 w-full text-sm text-center font-normal placeholder:text-mti-gray-cool disabled:bg-mti-gray-platinum/40 disabled:text-mti-gray-dim disabled:cursor-not-allowed rounded-full border border-mti-gray-platinum focus:outline-none"
						selected={startDate}
						startDate={startDate}
						endDate={endDate}
						selectsRange
						showMonthDropdown
						filterDate={(date: Date) => moment(date).isSameOrBefore(moment(new Date()))}
						onChange={([initialDate, finalDate]: [Date, Date]) => {
							setStartDate(initialDate ?? moment("01/01/2023").toDate());
							if (finalDate) {
								// basicly selecting a final day works as if I'm selecting the first
								// minute of that day. this way it covers the whole day
								setEndDate(moment(finalDate).endOf("day").toDate());
								return;
							}
							setEndDate(null);
						}}
					/>
				</div>
				{allowedToDelete && (
					<div className="flex gap-4 items-center">
						<span>{selectedCodes.length} code(s) selected</span>
						<Button
							disabled={selectedCodes.length === 0}
							variant="outline"
							color="red"
							className="!py-1 px-10"
							onClick={() => deleteCodes(selectedCodes)}>
							Delete
						</Button>
					</div>
				)}
			</div>
			<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>
		</>
	);
}
