import React from "react";
import { useMutation, UseMutationOptions } from "@tanstack/react-query";
import { AxiosError } from "axios";
import { Text } from "@mantine/core";
import { modals } from "@mantine/modals";
import { HttpValidationError } from "@/lib/client";
import { notifications } from "@/lib/notifications";
import { sortByLexorank } from "@/utils/lexorank";
import {
    BaseEntity,
    CreateMutationOptions,
    DeleteMutationOptions,
    UpdateMutationOptions,
} from "./types";

type CrudOperations<TEntity, TUpdate> = {
    onCreate: (name: string) => void;
    onUpdate: (id: number, update: TUpdate) => void;
    onDelete: (entity: TEntity) => void;
};

type UseEntityOperationsProps<TEntity extends BaseEntity, TUpdate, TCreate> = {
    entities: TEntity[];
    setEntities: React.Dispatch<React.SetStateAction<TEntity[]>>;
    refetch: () => void;
    mutations: {
        create: UseMutationOptions<
            any,
            AxiosError<HttpValidationError>,
            CreateMutationOptions
        >;
        update: UseMutationOptions<
            any,
            AxiosError<HttpValidationError>,
            UpdateMutationOptions
        >;
        delete: UseMutationOptions<
            any,
            AxiosError<HttpValidationError>,
            DeleteMutationOptions
        >;
    };
    getCreateEntity: (name: string) => TCreate | null;
    getUpdateEntity: (oldEntity: TEntity, update: TUpdate) => TEntity;
    getDeleteConfirmTitle: (entity: TEntity) => string;
};

const useCrudOperations = <
    TEntity extends BaseEntity,
    TUpdate extends object,
    TCreate extends object,
>({
    entities,
    setEntities,
    refetch,
    mutations,
    getCreateEntity,
    getUpdateEntity,
    getDeleteConfirmTitle,
}: UseEntityOperationsProps<TEntity, TUpdate, TCreate>): CrudOperations<
    TEntity,
    TUpdate
> => {
    const onError = (error: AxiosError<HttpValidationError>) => {
        console.error(error);
        notifications.error({
            message: error.response?.data?.detail as string | undefined,
        });
        refetch();
    };

    const createMutation = useMutation({
        ...mutations.create,
        onError,
        onSuccess: (res: { entity: TEntity }) => {
            setEntities([...entities, res.entity]);
        },
    });

    const updateMutation = useMutation({
        ...mutations.update,
        onError,
    });

    const deleteMutation = useMutation({
        ...mutations.delete,
        onError,
    });

    const onCreate = (name: string) => {
        const entity = getCreateEntity(name);
        if (!entity) return;
        createMutation.mutate({
            body: {
                entity,
            },
            path: undefined,
            query: undefined,
        });
    };

    const onUpdate = (id: number, update: TUpdate) => {
        updateMutation.mutate({
            body: {
                entity: update,
            },
            path: { pk: id },
            query: undefined,
        });
        setEntities(prev => {
            const updated = prev.map(entity =>
                entity.id === id ? getUpdateEntity(entity, update) : entity
            );
            if ("lexorank" in update) {
                return sortByLexorank(
                    updated as (TEntity & { lexorank: string })[]
                );
            }
            return updated;
        });
    };

    const onDelete = (entity: TEntity, onSuccess?: () => void) => {
        modals.openConfirmModal({
            title: getDeleteConfirmTitle(entity),
            children: (
                <Text>Вы уверены, что хотите удалить "{entity.name}"?</Text>
            ),
            labels: { confirm: "Да", cancel: "Нет" },
            confirmProps: { color: "red" },
            onConfirm: () => {
                deleteMutation.mutate({ path: { pk: entity.id } } as any);
                onSuccess && onSuccess();
                setEntities(prev => prev.filter(e => e.id !== entity.id));
            },
        });
    };

    return { onCreate, onUpdate, onDelete };
};

export default useCrudOperations;
