import Add from "@mui/icons-material/Add";
import DeleteForeverOutlined from "@mui/icons-material/DeleteForeverOutlined";
import Edit from "@mui/icons-material/Edit";
import QrCodeScanner from "@mui/icons-material/QrCodeScanner";
import TapAndPlay from "@mui/icons-material/TapAndPlay";
import { Alert, Box, Button, DialogActions, List, ListItem, ListItemText, Tab, Tabs } from "@mui/material";
import { getLocalizedString, localize } from "@olenbetong/appframe-core";
import { ResponsiveDialog } from "@olenbetong/appframe-mui";
import { useEffect, useState } from "react";
import { flushSync } from "react-dom";
import { BarcodeScanner } from "./BarcodeScanner";
import { type AddElementSource, type ElementDetails, getElement } from "./getElement";
import { AddElementHIDScanner } from "./HIDScanner";
import { AddElementManualForm } from "./Manual/ManualForm";
import { useBeeper } from "./utils/beep";
import Giai from "./utils/Giai";

export type AddElement = {
	element: ElementDetails;
	mode: AddElementSource;
	scanTime?: number;
};

type InitialTabProps = "camera" | "scanner" | "manual";

export type ElementLookupDialogSharedProps = {
	open: boolean;
	onClose: () => void;
	initialTab?: InitialTabProps | null;
	useSearch?: boolean;
	allowEmptyElement?: boolean;
};

export type ElementLookupDialogSingleModeProps = ElementLookupDialogSharedProps & {
	mode: "single" | "search";
	onAdd: (element: AddElement) => Promise<void>;
};

export type ElementLookupDialogMultiModeProps = ElementLookupDialogSharedProps & {
	mode: "multi";
	onAdd: (element: AddElement[]) => Promise<void>;
};

export type ElementLookupDialogProps = ElementLookupDialogSingleModeProps | ElementLookupDialogMultiModeProps;

export function ElementLookupDialog(props: ElementLookupDialogProps) {
	let { open, onClose, initialTab } = props;
	let [tab, setTab] = useState<"camera" | "scanner" | "manual">("camera");
	let [elements, setElements] = useState<Array<AddElement>>([]);
	let [adding, setAdding] = useState(false);
	let beeper = useBeeper();

	useEffect(() => {
		if (open && initialTab) {
			setTab(initialTab);
		}
	}, [open, initialTab]);

	async function handleElementAdd(element: string | ElementDetails, mode: AddElementSource) {
		try {
			let record = typeof element === "string" ? await getElement(element.replace("\u001d", ""), { mode }) : element;

			// Flush changes to DOM to be able to query newly added elements and animate their background
			flushSync(() => {
				setElements((current) => {
					let now = Date.now();
					let exists = current.some((e) => e.element.ElementID === record.ElementID);
					let updated: Array<AddElement>;

					if (exists) {
						updated = current.map((e) => (e.element.ElementID === record.ElementID ? { ...e, scanTime: now } : e));
					} else {
						beeper();
						updated =
							props.mode === "multi"
								? [{ element: record, mode, scanTime: now }, ...current]
								: [{ element: record, mode, scanTime: now }];
					}

					if (props.mode === "multi") {
						updated.sort((a, b) => (b.scanTime ?? 0) - (a.scanTime ?? 0));
					}

					return updated;
				});
			});

			let addedDomElement = document.querySelector(`[data-elementid="${record.ElementID}"]`);
			if (addedDomElement) {
				addedDomElement.animate(
					[
						{ backgroundColor: "var(--brand-light)", offset: 0 },
						{ backgroundColor: "var(--brand-light)", offset: 0.2 },
						{ backgroundColor: "transparent" },
					],
					{
						duration: 1000,
						easing: "ease-out",
					},
				);
			}
		} catch (error) {
			alert((error as Error).message);
		}
	}

	if (tab === "manual" && props.mode === "search") {
		tab = "camera";
	}

	return (
		<ResponsiveDialog
			open={open}
			onClose={onClose}
			slotProps={{
				paper: { sx: { overflow: "hidden" } },
			}}
		>
			<Box sx={{ display: "flex", flexDirection: "column", maxHeight: "100%", overflow: "hidden", flexGrow: 1 }}>
				<Tabs value={tab} onChange={(_, newTab) => setTab(newTab)} variant="fullWidth" sx={{ flexShrink: 0 }}>
					{props.mode !== "search" && <Tab icon={<Edit />} label={getLocalizedString("Manual")} value="manual" />}
					<Tab icon={<TapAndPlay />} label={getLocalizedString("Scanner")} value="scanner" />
					<Tab icon={<QrCodeScanner />} label={getLocalizedString("Camera")} value="camera" />
				</Tabs>
				{tab === "camera" && (
					<Box sx={{ flexBasis: "12rem", flexGrow: 1, flexShrink: 2 }}>
						<BarcodeScanner onRead={(element) => handleElementAdd(element, "camera-barcode")} />
					</Box>
				)}
				{tab === "manual" && (
					<Box sx={{ p: 2 }}>
						<AddElementManualForm
							onAdd={(element) => handleElementAdd(element, "manual")}
							allowEmptyElement={props.allowEmptyElement}
						/>
					</Box>
				)}
				{tab === "scanner" && (
					<Box sx={{ flexBasis: "12rem", flexGrow: 1, flexShrink: 2 }}>
						<AddElementHIDScanner
							onScan={(element) =>
								handleElementAdd(element, element.startsWith(Giai.BARCODE_PREFIX) ? "hid-barcode" : "hid-rfid")
							}
						/>
					</Box>
				)}

				<Box sx={{ flex: "3 1 14rem", overflow: "auto" }}>
					{props.mode === "multi" && (
						<Alert
							icon={false}
							severity="info"
							sx={{ justifyContent: "center", py: 0, position: "sticky", top: 0, zIndex: 1 }}
							slotProps={{
								message: { sx: { py: 0.5 } },
							}}
						>{localize`${elements.length} elements selected`}</Alert>
					)}
					<List disablePadding>
						{elements.map(({ element }) => (
							<ListItem
								key={element.ElementID}
								data-elementid={element.ElementID}
								secondaryAction={
									<Button
										color="error"
										variant="outlined"
										onClick={() => {
											setElements((current) =>
												props.mode === "multi" ? current.filter((e) => e.element.ElementID !== element.ElementID) : [],
											);
										}}
									>
										<DeleteForeverOutlined />
									</Button>
								}
							>
								<ListItemText
									primary={element.DisplayName || element.ProjectName}
									secondary={element.ElementID || element.ProjectID}
								/>
							</ListItem>
						))}
					</List>
				</Box>

				<DialogActions sx={{ flexShrink: 0 }}>
					<Button onClick={onClose}>{getLocalizedString("Cancel")}</Button>
					<Button
						variant="contained"
						loading={adding}
						onClick={async () => {
							try {
								setAdding(true);
								if (props.mode === "multi") {
									await props.onAdd(elements);
								} else {
									await props.onAdd(elements[0]!);
								}
								setElements([]);
								setAdding(false);
								onClose();
							} catch (error) {
								setAdding(false);
								alert((error as Error).message);
							}
						}}
						startIcon={<Add />}
						loadingPosition="start"
						disabled={!elements.length}
					>
						{props.mode === "search" ? getLocalizedString("Search for element") : getLocalizedString("Add element")}
					</Button>
				</DialogActions>
			</Box>
		</ResponsiveDialog>
	);
}
