import { useCallback, useEffect, useRef, useState } from "react";
import { connect } from "react-redux";

import { EmptyState, ErrorToast, FilterTagsBar, MainWrapper, TableList, Toast } from "@ax/components";
import { appActions } from "@ax/containers/App";
import { integrationsActions } from "@ax/containers/Integrations";
import { useBulkSelection, useEmptyState, useModal, usePermission, useToast } from "@ax/hooks";
import type { IEmptyStateProps, IIntegration, IQueryValue, IRootState } from "@ax/types";

import {
	closestCenter,
	DndContext,
	type DragEndEvent,
	KeyboardSensor,
	PointerSensor,
	type UniqueIdentifier,
	useSensor,
	useSensors,
} from "@dnd-kit/core";
import { SortableContext, sortableKeyboardCoordinates, verticalListSortingStrategy } from "@dnd-kit/sortable";

import { DeactivateModal, DeleteModal } from "./atoms";
import BulkHeader from "./BulkHeader";
import { useFilterQuery } from "./hooks";
import IntegrationItem from "./IntegrationItem";

import * as S from "./style";

const Integrations = (props: IIntegrationsProps): JSX.Element => {
	const {
		isLoading,
		getIntegrations,
		integrations,
		totalItems,
		currentSite,
		resetIntegrations,
		deleteIntegration,
		changeIntegrationState,
		setHistoryPush,
		setCurrentIntegration,
		orderIntegration,
	} = props;

	if (!currentSite) {
		throw new Error(`ERROR: User reached Integrations with null site info`);
	}

	const [isBulkLoading, setIsBulkLoading] = useState(false);
	const [isScrolling, setIsScrolling] = useState(false);
	const tableRef = useRef<HTMLDivElement>(null);
	const { setFiltersSelection, resetFilterQuery, filterValues, filterQuery, isFiltered } = useFilterQuery();
	const { isOpen: isOpenDelete, toggleModal: toggleModalDelete } = useModal();
	const { isOpen: isOpenDeactivate, toggleModal: toggleModalDeactivate } = useModal();
	const {
		isVisible: isVisibleDelete,
		toggleToast: toggleToastDelete,
		setIsVisible: setIsVisibleDelete,
		state: stateToastDelete,
	} = useToast();
	const {
		isVisible: isVisibleChange,
		toggleToast: toggleToastChange,
		setIsVisible: setIsVisibleChange,
		state: stateToastChange,
	} = useToast();

	const isAllowedToManageIntegrations = usePermission("general.manageSiteThirdPartyIntegrations");
	const isAllowedToDeactivateIntegrations = usePermission("general.deactivateSiteThirdPartyIntegrations");
	const isAllowedToDeleteIntegrations = usePermission("general.deleteSiteThirdPartyIntegrations");

	const sensors = useSensors(
		useSensor(PointerSensor, {
			activationConstraint: {
				distance: 5,
			},
		}),
		useSensor(KeyboardSensor, {
			coordinateGetter: sortableKeyboardCoordinates,
		}),
	);

	const noElementsProps: IEmptyStateProps = {
		message: "To start using add-ons in your site, create as many Custom Code as you need.",
		button: "Create custom code",
		action: () => setHistoryPush(`/sites/settings/addons/new`),
	};

	const fetchState = { isLoading, isFiltered };
	const { isEmpty, emptyStateProps } = useEmptyState(integrations, fetchState, { noElementsProps });

	const filteredIntegrationsIds = integrations
		?.filter((integration: IIntegration) => integration.editable)
		.map((integration: IIntegration) => integration.id);

	const integrationsIds = integrations.map((integration: IIntegration) => integration.id as UniqueIdentifier);

	const getParams = useCallback(() => {
		const params = {
			pagination: false,
			filter: filterQuery,
		};

		return params;
	}, [filterQuery]);

	// biome-ignore lint/correctness/useExhaustiveDependencies: TODO: fix this
	useEffect(() => {
		const params = getParams();
		getIntegrations(currentSite, params);
		setCurrentIntegration(null);

		return () => {
			resetIntegrations();
		};
	}, [currentSite, filterQuery]);

	const {
		resetBulkSelection,
		selectedItems,
		isSelected,
		areItemsSelected,
		checkState,
		addToBulkSelection,
		selectAllItems,
		setHoverCheck,
	} = useBulkSelection(filteredIntegrationsIds);

	const bulkDelete = async () => {
		setIsBulkLoading(true);
		const params = getParams();
		const deleted = await deleteIntegration(selectedItems.all, params);
		if (deleted) {
			toggleToastDelete({ total: selectedItems.all.length });
		}
		toggleModalDelete();
		resetBulkSelection();
		setIsBulkLoading(false);
	};

	const bulkDeactivate = async () => {
		setIsBulkLoading(true);
		const params = getParams();
		const changed = await changeIntegrationState(selectedItems.all, false, params);
		if (changed) {
			toggleToastChange({ total: selectedItems.all.length, active: false });
		}
		toggleModalDeactivate();
		resetBulkSelection();
		setIsBulkLoading(false);
	};

	const unselectAllItems = () => resetBulkSelection();

	const selectItems = () => (checkState.isAllSelected ? unselectAllItems() : handleSelectAll());

	const handleSelectAll = () => selectAllItems();

	const filterItems = async (filterPointer: string, filtersSelected: IQueryValue[]) =>
		setFiltersSelection(filterPointer, filtersSelected);

	const bulkActions: { icon: string; text: string; action: () => void | Promise<void> }[] =
		isAllowedToDeleteIntegrations
			? [
					{
						icon: "delete",
						text: "delete",
						action: toggleModalDelete,
					},
				]
			: [];

	if (isAllowedToDeactivateIntegrations) {
		bulkActions.push({
			icon: "deactivate",
			text: "Deactivate",
			action: toggleModalDeactivate,
		});
	}

	const TableHeader = (
		<BulkHeader
			showBulk={areItemsSelected(filteredIntegrationsIds)}
			selectAllItems={handleSelectAll}
			totalItems={totalItems}
			selectItems={selectItems}
			checkState={checkState}
			isScrolling={isScrolling}
			filterItems={filterItems}
			filterValues={filterValues}
			bulkActions={bulkActions}
			setHoverCheck={setHoverCheck}
			selectedItems={selectedItems}
			isLoading={isBulkLoading}
		/>
	);

	const onScroll = (e: React.UIEvent<HTMLElement>) => setIsScrolling((e.target as HTMLElement).scrollTop > 0);

	const handleOnDragEnd = (event: DragEndEvent) => {
		const { active, over } = event;

		if (over && active.id !== over.id) {
			const oldIndex = integrations.map((e) => e.id).indexOf(active.id as number);
			const newIndex = integrations.map((e) => e.id).indexOf(over.id as number);

			const newScriptOrder = newIndex ? integrations[newIndex - (newIndex > oldIndex ? 0 : 1)].scriptOrder + 1 : 1;
			const params = getParams();
			orderIntegration(active.id as number, newScriptOrder, params);
		}
	};

	const mainDeleteModalAction = {
		title: isBulkLoading ? "Deleting" : "Delete",
		onClick: bulkDelete,
		disabled: isBulkLoading,
	};
	const secondaryDeleteModalAction = { title: "Cancel", onClick: toggleModalDelete };

	const mainDeactivateModalAction = {
		title: "Deactivate",
		onClick: bulkDeactivate,
	};
	const secondaryDeactivateModalAction = { title: "Cancel", onClick: toggleModalDeactivate };

	const deletedToastProps = {
		setIsVisible: setIsVisibleDelete,
		message: stateToastDelete?.total > 1 ? `${stateToastDelete?.total} add-ons deleted` : "Add-on deleted",
	};

	const changedToastProps = {
		setIsVisible: setIsVisibleChange,
		message: `${stateToastChange?.total} add-on${stateToastChange?.total > 1 ? "s" : ""} ${
			stateToastChange?.active ? "enabled" : "disabled"
		}`,
	};

	const rightButtonProps = isAllowedToManageIntegrations
		? {
				label: "New Custom Code",
				action: () => setHistoryPush(`/sites/settings/addons/new`),
			}
		: undefined;

	const filterLabels: Record<string, string> = {
		filterApplication: "Applied on",
		filterState: "State",
		filterCode: "Code",
	};

	return (
		<MainWrapper backLink={false} title="Add-ons" rightButton={rightButtonProps}>
			<S.Wrapper data-testid="integrations-main-wrapper">
				<S.ContentWrapper>
					<ErrorToast />
					<S.TitleWrapper>
						<S.Title>Custom Code</S.Title>
						<S.Description>Add custom code to the head or body of your site&apos;s pages.</S.Description>
					</S.TitleWrapper>
					<S.TableWrapper>
						<TableList tableHeader={TableHeader} onScroll={onScroll} hasFixedHeader={true} tableRef={tableRef}>
							<S.SearchTags>
								<FilterTagsBar
									filters={filterValues}
									setFilters={setFiltersSelection}
									resetFilters={resetFilterQuery}
									labels={filterLabels}
								/>
							</S.SearchTags>
							{isEmpty ? (
								<S.EmptyWrapper>
									<EmptyState {...emptyStateProps} />
								</S.EmptyWrapper>
							) : (
								<>
									{integrations.length >= 2 && (
										<S.OrderNote>
											You can easily <strong>reorder</strong> the list using <strong>drag and drop</strong>. The order{" "}
											<strong>determines the priority of the add-on</strong>, and the code appears on the page
											accordingly.
										</S.OrderNote>
									)}
									<DndContext sensors={sensors} collisionDetection={closestCenter} onDragEnd={handleOnDragEnd}>
										<SortableContext items={integrationsIds} strategy={verticalListSortingStrategy}>
											{integrations.map((integration: IIntegration) => {
												const isItemSelected = isSelected(integration.id);
												return (
													<IntegrationItem
														key={`${integration.name}${integration.id}`}
														integration={integration}
														isSelected={isItemSelected}
														onChange={addToBulkSelection}
														toggleToastDelete={toggleToastDelete}
														getParams={getParams}
														deleteIntegration={deleteIntegration}
														changeState={changeIntegrationState}
														toggleToastChange={toggleToastChange}
														listLength={integrations.length}
														hoverCheck={checkState.hoverCheck}
													/>
												);
											})}
										</SortableContext>
									</DndContext>
								</>
							)}
						</TableList>
					</S.TableWrapper>
					{isVisibleDelete && <Toast {...deletedToastProps} />}
					{isVisibleChange && <Toast {...changedToastProps} />}
				</S.ContentWrapper>
			</S.Wrapper>
			<DeleteModal
				isOpen={isOpenDelete}
				toggleModal={toggleModalDelete}
				secondaryModalAction={secondaryDeleteModalAction}
				mainModalAction={mainDeleteModalAction}
				integrations={integrations}
				selectedIds={selectedItems.all}
			/>
			<DeactivateModal
				isOpen={isOpenDeactivate}
				toggleModal={toggleModalDeactivate}
				secondaryModalAction={secondaryDeactivateModalAction}
				mainModalAction={mainDeactivateModalAction}
				integrations={integrations}
				selectedIds={selectedItems.all}
			/>
		</MainWrapper>
	);
};

const mapStateToProps = (state: IRootState) => ({
	isLoading: state.app.isLoading,
	integrations: state.integrations.integrations,
	totalItems: state.integrations.totalItems,
	currentSite: state.sites.currentSiteInfo?.id ?? null,
});

const mapDispatchToProps = {
	getIntegrations: integrationsActions.getIntegrations,
	resetIntegrations: integrationsActions.resetIntegrations,
	deleteIntegration: integrationsActions.deleteIntegration,
	changeIntegrationState: integrationsActions.changeIntegrationState,
	setHistoryPush: appActions.setHistoryPush,
	setCurrentIntegration: integrationsActions.setCurrentIntegration,
	orderIntegration: integrationsActions.orderIntegration,
};

interface IStateProps {
	isLoading: boolean;
	integrations: IIntegration[];
	totalItems: number;
	currentSite: number | null;
}

interface IDispatchProps {
	getIntegrations: (site: number, params: any) => Promise<void>;
	resetIntegrations: () => void;
	deleteIntegration: (
		integrationId: number | number[],
		currentParams: { pagination: boolean; filter: any },
	) => Promise<boolean>;
	changeIntegrationState: (integrationId: number | number[], active: boolean, currentParams?: any) => Promise<boolean>;
	setHistoryPush: (path: string) => void;
	setCurrentIntegration: (integration: IIntegration | null) => void;
	orderIntegration(id: number, newOrder: number, currentParams?: any): Promise<boolean>;
}

export type IIntegrationsProps = IStateProps & IDispatchProps;

export default connect(mapStateToProps, mapDispatchToProps)(Integrations);
