import { useState, useCallback } from 'react';
import i18n from '@/lib/i18n';
import type { UseEntityHookReturn } from '@/types/base';
import { api } from '@/lib/api';
import { ApiResponse, PaginatedResponse } from '@/types';

function unwrapResponse<T>(response: unknown, fallbackMessage: string): T {
  if (response && typeof response === 'object' && 'success' in response) {
    const wrapped = response as ApiResponse<T>;
    if (wrapped.success === false) {
      throw new Error(wrapped.message || fallbackMessage);
    }
    return wrapped.data as T;
  }

  return response as T;
}

export function createEntityHook<T, TCreate, TUpdate>(
  entityName: string,
  endpoint: string,
): () => UseEntityHookReturn<T, TCreate, TUpdate> {
  return function useEntity(): UseEntityHookReturn<T, TCreate, TUpdate> {
    const [items, setItems] = useState<T[]>([]);
    const [selectedItem, setSelectedItem] = useState<T | null>(null);
    const [error, setError] = useState<string | null>(null);
    const [isLoading, setIsLoading] = useState(false);
    const [loadingSingle, setLoadingSingle] = useState(false);

    const [currentPage, setCurrentPage] = useState(1);
    const [totalCount, setTotalCount] = useState(0);
    const [pageSize, setPageSize] = useState(10);
    const [searchTerm, setSearchTerm] = useState('');
    const [totalPages, setTotalPages] = useState(1);

    const fetchItems = useCallback(async (page: number = 1, size: number = 10, search: string = '', filters?: Record<string, any>) => {
      setIsLoading(true);
      setError(null);

      try {
        const params: Record<string, unknown> = {
          skip: (page - 1) * size,
          limit: size,
          page, // kept for backwards compatibility if any
          page_size: size,
        };
        if (search) {
          params.search = search;
        }
        if (filters) {
          Object.entries(filters).forEach(([k, v]) => {
            if (v !== undefined && v !== null && v !== '') {
              params[k] = v;
            }
          });
        }

        const response = await api.get(endpoint, { params });
        const payload = unwrapResponse<PaginatedResponse<T> | T[]>(
          response,
          i18n.t('hooks.useEntityBase.text_error_fetching', { entityName }),
        );
        const isArray = Array.isArray(payload);
        const dataItems = isArray ? payload : (payload as any).items || [];

        setItems(dataItems);
        setTotalCount(isArray ? dataItems.length : ((payload as any).total || 0));
        setTotalPages(isArray ? 1 : ((payload as any).pages || Math.ceil(((payload as any).total || 0) / size)));
        setCurrentPage(isArray ? 1 : ((payload as any).page || page));
        setPageSize(isArray ? dataItems.length : ((payload as any).page_size || size));
        setSearchTerm(search);
      } catch (err) {
        setError(err instanceof Error ? err.message : i18n.t('hooks.useEntityBase.text_error_fetching', { entityName }));
        setItems([]);
      } finally {
        setIsLoading(false);
      }
    }, [endpoint, entityName]);

    const prepareRequestData = (data: TCreate | TUpdate): FormData | TCreate | TUpdate => {
      const dataRecord = data as Record<string, unknown>;
      const hasFiles = Object.values(dataRecord).some((value) => value instanceof File);

      if (hasFiles) {
        const formData = new FormData();
        Object.entries(dataRecord).forEach(([key, value]) => {
          if (value !== undefined && value !== null) {
            if (value instanceof File) {
              formData.append(key, value);
            } else {
              formData.append(key, String(value));
            }
          }
        });
        return formData;
      }
      return data;
    };

    const createItem = useCallback(async (data: TCreate): Promise<boolean> => {
      setIsLoading(true);
      setError(null);

      try {
        const body = prepareRequestData(data);
        unwrapResponse<T>(await api.post(endpoint, body), i18n.t('hooks.useEntityBase.text_error_creating', { entityName }));
        await fetchItems(currentPage, pageSize, searchTerm);
        return true;
      } catch (err) {
        setError(err instanceof Error ? err.message : i18n.t('hooks.useEntityBase.text_error_creating', { entityName }));
        return false;
      } finally {
        setIsLoading(false);
      }
    }, [endpoint, entityName, fetchItems, currentPage, pageSize, searchTerm]);

    const updateItem = useCallback(async (id: string, data: TUpdate): Promise<boolean> => {
      setIsLoading(true);
      setError(null);

      try {
        const body = prepareRequestData(data);
        unwrapResponse<T>(await api.patch(`${endpoint}/${id}`, body), i18n.t('hooks.useEntityBase.text_error_updating', { entityName }));
        await fetchItems(currentPage, pageSize, searchTerm);
        return true;
      } catch (err) {
        setError(err instanceof Error ? err.message : i18n.t('hooks.useEntityBase.text_error_updating', { entityName }));
        return false;
      } finally {
        setIsLoading(false);
      }
    }, [endpoint, entityName, fetchItems, currentPage, pageSize, searchTerm]);

    const deleteItem = useCallback(async (id: string): Promise<boolean> => {
      setIsLoading(true);
      setError(null);

      try {
        unwrapResponse<void>(await api.del(`${endpoint}/${id}`), i18n.t('hooks.useEntityBase.text_error_deleting', { entityName }));
        await fetchItems(currentPage, pageSize, searchTerm);
        return true;
      } catch (err) {
        setError(err instanceof Error ? err.message : i18n.t('hooks.useEntityBase.text_error_deleting', { entityName }));
        return false;
      } finally {
        setIsLoading(false);
      }
    }, [endpoint, entityName, fetchItems, currentPage, pageSize, searchTerm]);

    const fetchItemById = useCallback(async (id: string) => {
      setLoadingSingle(true);
      setError(null);

      try {
        const response = await api.get(`${endpoint}/${id}`);
        setSelectedItem(unwrapResponse<T>(response, i18n.t('hooks.useEntityBase.text_error_fetching', { entityName })));
      } catch (err) {
        setError(err instanceof Error ? err.message : i18n.t('hooks.useEntityBase.text_error_fetching', { entityName }));
        setSelectedItem(null);
      } finally {
        setLoadingSingle(false);
      }
    }, [endpoint, entityName]);

    return {
      items,
      selectedItem,
      error,
      isLoading,
      loadingSingle,
      currentPage,
      totalCount,
      pageSize,
      searchTerm,
      totalPages,
      fetchItems,
      createItem,
      updateItem,
      deleteItem,
      fetchItemById,
      setSearchTerm,
      setCurrentPage,
      setPageSize,
      setSelectedItem,
      setIsLoading,
      setError,
      setItems,
      setTotalCount,
      setTotalPages,
    };
  };
}
