/* eslint-disable @typescript-eslint/no-explicit-any */
import {
	AddOutlined,
	ArrowBackOutlined,
	DeleteOutline,
	RemoveRedEyeOutlined,
} from "@mui/icons-material";
import {
	Stack,
	TablePagination,
	Table,
	TableHead,
	TableCell,
	TableRow,
	TableBody,
	TableContainer,
	Paper,
} from "@mui/material";
import { FormikValues } from "formik";
import { ChangeEvent, useEffect, useState, JSX } from "react";
import { useTranslation } from "react-i18next";
import { useNavigate } from "react-router-dom";
import { toast } from "react-toastify";

import { useSignalR } from "@/contexts/signalR/useSignalR";

import {
	CREATED_NOTIFICATION,
	DELETED_NOTIFICATION,
	UPDATED_NOTIFICATION,
} from "../../../enums/notifications.enum";
import { http } from "../../../services/http.service";
import { Entity } from "../../models/Entity";
import { HasId } from "../../models/has-id";
import { SortingModel } from "../../models/Sorting";
import { TableStateResponse } from "../../models/TableStateResponse";
import { ButtonWithIcon } from "../ButtonWithIcon";
import { Centroid } from "../Centroid";
import { Loader } from "../Loader";
import { SearchBox } from "../SearchBox";

import { Column } from "./Column";

const PageVariants = [2, 5, 10, 25, 50];

export interface Props<T extends Entity> {
	baseUrl: string;
	title: string;
	columns: Column<T>[];
	notificationMethodPrefix: string;
	getModelTitle: (model: T) => string;
	extraActions?: JSX.Element | null;
	canCreate?: boolean;
	getCanCreate?: () => boolean;
	canRemove?: boolean;
	getCanRemove?: (model: T) => boolean;
	canView?: boolean;
	extraItemActions?: (
		model: T,
		context: MatrixExtraItemActionsContext,
	) => JSX.Element | null;
	filters?: any;
	defaultSort?: SortingModel[];
	useSearch?: boolean;
	searchModel?: T;
}

interface PageState {
	take: number;
	currentPage: number;
	pageCount: number;
}

const rowBackgroundColor = "white";
const rowBackgroundColorEven = "#fafafa";

export interface MatrixExtraItemActionsContext {
	update: () => void;
}

export function Matrix<T extends FormikValues & HasId>(props: Props<T>) {
	const navigate = useNavigate();
	const [isLoading, setIsLoading] = useState(false);
	const [tableState, setTableState] = useState<PageState>({
		take: 10,
		currentPage: 0,
		pageCount: 1,
	});
	const [items, setItems] = useState<T[]>([]);
	const [total, setTotal] = useState(0);
	const [search, setSearch] = useState("");

	const { subscribe, unsubscribe } = useSignalR();
	const { t } = useTranslation();

	const [sorts /*, setSorts*/] = useState(props.defaultSort);
	const context: MatrixExtraItemActionsContext = {
		update: () => {
			fetchItems();
		},
	};

	useEffect(() => {
		subscribe(
			`${props.notificationMethodPrefix}.${CREATED_NOTIFICATION}`,
			onCreated,
		);
		subscribe(
			`${props.notificationMethodPrefix}.${UPDATED_NOTIFICATION}`,
			onUpdated,
		);
		subscribe(
			`${props.notificationMethodPrefix}.${DELETED_NOTIFICATION}`,
			onDeleted,
		);

		return () => {
			unsubscribe(
				`${props.notificationMethodPrefix}.${CREATED_NOTIFICATION}`,
				onCreated,
			);
			unsubscribe(
				`${props.notificationMethodPrefix}.${UPDATED_NOTIFICATION}`,
				onUpdated,
			);
			unsubscribe(
				`${props.notificationMethodPrefix}.${DELETED_NOTIFICATION}`,
				onDeleted,
			);
		};
	}, []);

	const onCreated = (x: T) => {
		toast.success(
			t("{{title}} is created", { title: props.getModelTitle(x) }),
		);

		fetchItems();
	};
	const onUpdated = (x: T) => {
		toast.success(
			t("{{title}} is updated", { title: props.getModelTitle(x) }),
		);

		fetchItems();
	};
	const onDeleted = (x: T) => {
		toast.success(
			t("{{title}} is removed", { title: props.getModelTitle(x) }),
		);

		fetchItems();
	};

	useEffect(() => {
		fetchItems();
	}, [tableState, sorts, search, props.filters]);

	const fetchItems = () => {
		setIsLoading(true);

		http.get<TableStateResponse<T>>(props.baseUrl, {
			...props.filters,
			sorts,
			skip: tableState.currentPage * tableState.take,
			take: tableState.take,
			search: search || undefined,
		})
			.then((tableState) => {
				setTotal(tableState.total);
				setItems(tableState.items);
			})
			.catch((error) => {
				const message =
					(error && (error as Error).message) ||
					(typeof error === "string" ? error : String(error));
				toast.error(message);
			})
			.finally(() => {
				setIsLoading(false);
			});
	};

	const handleView = (item: T) => {
		navigate(item.id);
	};

	const handleDelete = (item: T) => {
		setIsLoading(true);
		http.remove(`${props.baseUrl}/${item.id}`)
			.then(() => {
				fetchItems();
			})
			.catch((error) => {
				console.error(error);
			})
			.finally(() => {
				setIsLoading(false);
			});
	};

	const handleAdd = () => {
		navigate("new");
	};

	const getValue = (column: Column<T>, item: T) => {
		const value = item[column.name];
		// if (typeof value == "boolean") {
		// 	return <>{value ? "Да" : "Нет"}</>;
		// }

		if (column.convert) {
			return <>{column.convert(value, item)}</>;
		}

		return <>{value}</>;
	};

	if (isLoading) {
		return (
			<Centroid>
				<Loader />
			</Centroid>
		);
	}

	const handleChangePage = (_: unknown, newPage: number) => {
		const newState = {
			...tableState,
			currentPage: newPage,
		};
		setTableState(newState);
	};

	const handleChangeRowsPerPage = (event: ChangeEvent<HTMLInputElement>) => {
		const take = parseInt(event.target.value, 10);
		const newState = {
			...tableState,
			take,
			currentPage: 0,
		};
		setTableState(newState);
	};

	const back = () => {
		navigate("..", {
			relative: "path",
		});
	};

	//const handleSort = (sorts: SortingModel[]) => {
	//	setSorts(sorts);
	//};

	const hasAnyAction = () => {
		return (
			props.canView ||
			props.canRemove ||
			(props.extraItemActions && props.extraItemActions.length > 0)
		);
	};

	return (
		<Stack spacing={3} padding={3}>
			<h3>{t(props.title)}</h3>

			<div className="flex gap-3 items-center">
				<ButtonWithIcon
					icon={<ArrowBackOutlined />}
					onClick={back}
					title={t("Back")}
					color="secondary"
				/>

				{(props.canCreate ||
					(props.getCanCreate && props.getCanCreate())) && (
					<ButtonWithIcon
						icon={<AddOutlined />}
						onClick={handleAdd}
						title={t("Add")}
					/>
				)}

				{props.useSearch && (
					<SearchBox
						onSearch={setSearch}
						labelKey={t("Search")}
						search={search}
					/>
				)}

				{props.extraActions && props.extraActions}
			</div>

			<div>
				<TableContainer component={Paper} elevation={0}>
					<Table stickyHeader aria-label="striped sticky table">
						<TableHead>
							<TableRow>
								{props.columns.map((column, colIndex) => (
									<TableCell
										key={colIndex}
										className="align-middle"
									>
										<Stack
											direction="row"
											justifyContent="space-between"
											alignItems="center"
										>
											<div>{t(column.title)}</div>

											{/* {column.filter && <Filter></Filter>} */}

											{/* {column.sortable && (
												<Sorting
													column={column}
													onSort={handleSort}
												/>
											)} */}
										</Stack>
									</TableCell>
								))}

								{hasAnyAction() && (
									<TableCell
									// style={{
									// 	position: "sticky",
									// 	right: 0,
									// 	background: "white",
									// 	zIndex: 1,
									// }}
									>
										{t("Actions")}
									</TableCell>
								)}
							</TableRow>
						</TableHead>
						<TableBody>
							{items &&
								items.length > 0 &&
								items.map((item, index) => (
									<TableRow
										key={item.id}
										sx={{
											backgroundColor:
												index % 2 === 0
													? rowBackgroundColorEven
													: rowBackgroundColor,
										}}
									>
										{props.columns.map(
											(column, colIndex) => (
												<TableCell
													key={colIndex}
													className="align-middle"
												>
													{getValue(column, item)}
												</TableCell>
											),
										)}
										{hasAnyAction() && (
											<TableCell
												component="th"
												scope="row"
												// style={{
												// 	position: "sticky",
												// 	right: 0,
												// 	background: "white",
												// 	zIndex: 1,
												// 	width: "auto",
												// 	whiteSpace: "nowrap",
												// }}
											>
												<div className="flex gap-2">
													{props.canView && (
														<ButtonWithIcon
															icon={
																<RemoveRedEyeOutlined />
															}
															onClick={() =>
																handleView(item)
															}
															title={t("View")}
														/>
													)}

													{(props.canRemove ||
														(props.getCanRemove &&
															props.getCanRemove(
																item,
															))) && (
														<ButtonWithIcon
															icon={
																<DeleteOutline />
															}
															onClick={() =>
																handleDelete(
																	item,
																)
															}
															title={t("Remove")}
															color="error"
														/>
													)}

													{props.extraItemActions &&
														props.extraItemActions(
															item,
															context,
														)}
												</div>
											</TableCell>
										)}
									</TableRow>
								))}
						</TableBody>
					</Table>
				</TableContainer>
			</div>

			<div className="flex justify-end table-pagination-container">
				<TablePagination
					component="div"
					count={total}
					page={tableState.currentPage}
					onPageChange={handleChangePage}
					rowsPerPage={tableState.take}
					onRowsPerPageChange={handleChangeRowsPerPage}
					rowsPerPageOptions={PageVariants}
					labelRowsPerPage={t("Rows per page")}
					labelDisplayedRows={({ from, to, count }) =>
						t(`ParesFromToOfTotal`, {
							from,
							to,
							count,
						})
					}
				/>
			</div>
		</Stack>
	);
}
