/**
 * useEntityTable Hook
 */

import { useState, useEffect, useCallback, useMemo } from 'react';
import { useAppDispatch, useAppSelector } from '../stores/hooks';
import type { RootState } from '../stores/store';
import type { AsyncThunk } from '@reduxjs/toolkit';
import type { GridColDef, GridSortModel } from '@mui/x-data-grid';
import type { BaseEntity } from '../types/entities';
import type { FetchParams } from '../types/api';
import type { NotificationState } from '../types/redux';
import type { Filter, FilterItem } from '../types/filters';

interface EntitySliceState<T> {
  loading: boolean;
  count: number;
  refetch: boolean;
  notify: NotificationState;
  [entityName: string]: T[] | boolean | number | NotificationState | unknown;
}

interface UseEntityTableOptions<T extends BaseEntity> {
  entityName: string;
  sliceSelector: (state: RootState) => EntitySliceState<T>;
  fetchAction: AsyncThunk<unknown, FetchParams, { rejectValue: unknown }>;
  updateAction?: AsyncThunk<
    unknown,
    { id: string; data: Partial<T> },
    { rejectValue: unknown }
  >;
  deleteAction: AsyncThunk<unknown, string, { rejectValue: unknown }>;
  deleteByIdsAction?: AsyncThunk<unknown, string[], { rejectValue: unknown }>;
  setRefetchAction: (value: boolean) => { type: string; payload: boolean };
  loadColumnsFunction: (
    handleDelete: (id: string) => void,
    entityPath: string,
    currentUser: unknown,
  ) => Promise<GridColDef[]>;
  filters: Filter[];
  perPage?: number;
}

interface UseEntityTableReturn<T> {
  // Data
  data: T[];
  columns: GridColDef[];
  loading: boolean;
  count: number;

  // Pagination
  currentPage: number;
  setCurrentPage: (page: number) => void;
  numPages: number;

  // Sorting
  sortModel: GridSortModel;
  setSortModel: (model: GridSortModel) => void;

  // Selection
  selectedRows: string[];
  setSelectedRows: (ids: string[]) => void;

  // Filters
  filterItems: FilterItem[];
  setFilterItems: (items: FilterItem[]) => void;
  filterRequest: string;
  handleFilterSubmit: () => void;
  handleFilterReset: () => void;

  // Delete modal
  isDeleteModalActive: boolean;
  deleteTargetId: string | null;
  handleDeleteClick: (id: string) => void;
  handleDeleteConfirm: () => Promise<void>;
  handleDeleteCancel: () => void;
  handleDeleteSelected: () => Promise<void>;

  // Table update
  handleRowUpdate: (id: string, data: Partial<T>) => Promise<void>;

  // Refresh
  loadData: (page?: number, request?: string) => void;
}

/**
 * Hook for managing entity table state and operations
 *
 * @param options - Configuration options
 * @returns Table state and management functions
 */
export function useEntityTable<T extends BaseEntity>(
  options: UseEntityTableOptions<T>,
): UseEntityTableReturn<T> {
  const {
    entityName,
    sliceSelector,
    fetchAction,
    updateAction,
    deleteAction,
    deleteByIdsAction,
    setRefetchAction,
    loadColumnsFunction,
    filters,
    perPage = 10,
  } = options;

  const dispatch = useAppDispatch();
  const entityState = sliceSelector(
    useAppSelector((state) => state) as RootState,
  );
  const { currentUser } = useAppSelector((state) => state.auth);

  // Extract data from state
  const data = (entityState[entityName] as T[]) || [];
  const { loading, count, refetch, notify } = entityState;

  // Local state
  const [currentPage, setCurrentPage] = useState(0);
  const [columns, setColumns] = useState<GridColDef[]>([]);
  const [filterRequest, setFilterRequest] = useState('');
  const [filterItems, setFilterItems] = useState<FilterItem[]>([]);
  const [selectedRows, setSelectedRows] = useState<string[]>([]);
  const [sortModel, setSortModel] = useState<GridSortModel>([
    { field: '', sort: 'desc' },
  ]);

  // Delete modal state
  const [isDeleteModalActive, setIsDeleteModalActive] = useState(false);
  const [deleteTargetId, setDeleteTargetId] = useState<string | null>(null);

  // Calculate number of pages
  const numPages = useMemo(() => {
    return count === 0 ? 1 : Math.ceil(count / perPage);
  }, [count, perPage]);

  // Load data function
  const loadData = useCallback(
    (page = currentPage, request = filterRequest) => {
      if (page !== currentPage) setCurrentPage(page);
      if (request !== filterRequest) setFilterRequest(request);

      const { sort, field } = sortModel[0] || { sort: 'desc', field: '' };
      const query = `?page=${page}&limit=${perPage}${request}&sort=${sort}&field=${field}`;

      dispatch(fetchAction({ query }));
    },
    [currentPage, filterRequest, sortModel, perPage, dispatch, fetchAction],
  );

  // Load columns when user is available
  useEffect(() => {
    if (!currentUser) return;

    loadColumnsFunction(
      (id: string) => handleDeleteClick(id),
      entityName,
      currentUser,
    ).then(setColumns);
  }, [currentUser, entityName, loadColumnsFunction]);

  // Load data when sort model changes
  useEffect(() => {
    if (!currentUser) return;
    loadData();
  }, [sortModel, currentUser]);

  // Handle refetch flag
  useEffect(() => {
    if (refetch) {
      loadData(0);
      dispatch(setRefetchAction(false));
    }
  }, [refetch, dispatch, setRefetchAction, loadData]);

  // Generate filter request
  const generateFilterRequest = useCallback((): string => {
    let request = '&';

    filterItems.forEach((item) => {
      const filter = filters.find((f) => f.title === item.fields.selectedField);
      if (!filter) return;

      const isRangeFilter = filter.number || filter.date;

      if (isRangeFilter) {
        const from = item.fields.filterValueFrom;
        const to = item.fields.filterValueTo;
        if (from) {
          request += `${item.fields.selectedField}Range=${encodeURIComponent(from)}&`;
        }
        if (to) {
          request += `${item.fields.selectedField}Range=${encodeURIComponent(to)}&`;
        }
      } else {
        const value = item.fields.filterValue;
        if (value) {
          request += `${item.fields.selectedField}=${encodeURIComponent(value)}&`;
        }
      }
    });

    return request;
  }, [filterItems, filters]);

  // Filter handlers
  const handleFilterSubmit = useCallback(() => {
    loadData(0, generateFilterRequest());
  }, [loadData, generateFilterRequest]);

  const handleFilterReset = useCallback(() => {
    setFilterItems([]);
    loadData(0, '');
  }, [loadData]);

  // Delete handlers
  const handleDeleteClick = useCallback((id: string) => {
    setDeleteTargetId(id);
    setIsDeleteModalActive(true);
  }, []);

  const handleDeleteConfirm = useCallback(async () => {
    if (!deleteTargetId) return;

    await dispatch(deleteAction(deleteTargetId));
    await loadData(0);
    setIsDeleteModalActive(false);
    setDeleteTargetId(null);
  }, [deleteTargetId, dispatch, deleteAction, loadData]);

  const handleDeleteCancel = useCallback(() => {
    setIsDeleteModalActive(false);
    setDeleteTargetId(null);
  }, []);

  const handleDeleteSelected = useCallback(async () => {
    if (!deleteByIdsAction || selectedRows.length === 0) return;

    await dispatch(deleteByIdsAction(selectedRows));
    await loadData(0);
    setSelectedRows([]);
  }, [deleteByIdsAction, selectedRows, dispatch, loadData]);

  // Row update handler
  const handleRowUpdate = useCallback(
    async (id: string, rowData: Partial<T>) => {
      if (!updateAction) return;
      await dispatch(updateAction({ id, data: rowData })).unwrap();
    },
    [updateAction, dispatch],
  );

  return {
    // Data
    data,
    columns,
    loading,
    count,

    // Pagination
    currentPage,
    setCurrentPage,
    numPages,

    // Sorting
    sortModel,
    setSortModel,

    // Selection
    selectedRows,
    setSelectedRows,

    // Filters
    filterItems,
    setFilterItems,
    filterRequest,
    handleFilterSubmit,
    handleFilterReset,

    // Delete modal
    isDeleteModalActive,
    deleteTargetId,
    handleDeleteClick,
    handleDeleteConfirm,
    handleDeleteCancel,
    handleDeleteSelected,

    // Table update
    handleRowUpdate,

    // Refresh
    loadData,
  };
}

export default useEntityTable;
