import { useState, useCallback } from 'react';
import axios, { AxiosInstance, AxiosRequestConfig } from 'axios';
import { ApiResponse, PaginatedResponse, FilterOptions } from '../types';

export interface ApiServiceConfig {
  baseURL: string;
  defaultHeaders?: Record<string, string>;
  timeout?: number;
}

export interface UseApiServiceReturn<T> {
  data: T[];
  loading: boolean;
  error: string | null;
  total: number;
  hasMore: boolean;
  client: AxiosInstance;
  
  // CRUD operations
  getAll: (filters?: FilterOptions) => Promise<T[]>;
  getById: (id: string) => Promise<T>;
  create: (data: Partial<T>) => Promise<T>;
  update: (id: string, data: Partial<T>) => Promise<T>;
  delete: (id: string) => Promise<void>;
  
  // Utility methods
  clearError: () => void;
  refresh: () => Promise<void>;
}

export const useApiService = <T extends { id: string }>(
  config: ApiServiceConfig,
  endpoint: string
): UseApiServiceReturn<T> => {
  const [data, setData] = useState<T[]>([]);
  const [loading, setLoading] = useState(false);
  const [error, setError] = useState<string | null>(null);
  const [total, setTotal] = useState(0);
  const [hasMore, setHasMore] = useState(false);

  // Create axios instance
  const client = axios.create({
    baseURL: config.baseURL,
    timeout: config.timeout || 10000,
    headers: {
      'Content-Type': 'application/json',
      ...config.defaultHeaders,
    },
  });

  // Add request interceptor for common headers
  client.interceptors.request.use(
    (config) => {
      // Add user email header if available (common pattern in the codebase)
      const userEmail = 'admin@example.com'; // This could come from a context or config
      if (userEmail) {
        config.headers['X-User-Email'] = userEmail;
      }
      return config;
    },
    (error) => Promise.reject(error)
  );

  // Add response interceptor for error handling
  client.interceptors.response.use(
    (response) => response,
    (error) => {
      const errorMessage = error.response?.data?.message || error.message || 'An error occurred';
      setError(errorMessage);
      return Promise.reject(error);
    }
  );

  const clearError = useCallback(() => {
    setError(null);
  }, []);

  const getAll = useCallback(async (filters: FilterOptions = {}): Promise<T[]> => {
    setLoading(true);
    setError(null);
    
    try {
      const params = new URLSearchParams();
      Object.entries(filters).forEach(([key, value]) => {
        if (value !== undefined && value !== null && value !== '') {
          params.append(key, value.toString());
        }
      });

      const response = await client.get<PaginatedResponse<T> | ApiResponse<T[]>>(`${endpoint}?${params.toString()}`);
      
      // Handle both paginated and simple array responses
      if ('data' in response.data && Array.isArray(response.data.data)) {
        // Paginated response
        const paginatedData = response.data as PaginatedResponse<T>;
        setData(paginatedData.data);
        setTotal(paginatedData.total);
        setHasMore(paginatedData.has_more || false);
        return paginatedData.data;
      } else if ('data' in response.data && Array.isArray(response.data.data)) {
        // Simple array response wrapped in ApiResponse
        const apiData = response.data as ApiResponse<T[]>;
        setData(apiData.data);
        setTotal(apiData.data.length);
        setHasMore(false);
        return apiData.data;
      } else if (Array.isArray(response.data)) {
        // Direct array response
        setData(response.data);
        setTotal(response.data.length);
        setHasMore(false);
        return response.data;
      } else {
        throw new Error('Invalid response format');
      }
    } catch (err: any) {
      const errorMessage = err.response?.data?.message || err.message || 'Failed to fetch data';
      setError(errorMessage);
      throw err;
    } finally {
      setLoading(false);
    }
  }, [client, endpoint]);

  const getById = useCallback(async (id: string): Promise<T> => {
    setLoading(true);
    setError(null);
    
    try {
      const response = await client.get<ApiResponse<T> | T>(`${endpoint}/${id}`);
      const item = 'data' in response.data ? response.data.data : response.data;
      return item;
    } catch (err: any) {
      const errorMessage = err.response?.data?.message || err.message || 'Failed to fetch item';
      setError(errorMessage);
      throw err;
    } finally {
      setLoading(false);
    }
  }, [client, endpoint]);

  const create = useCallback(async (itemData: Partial<T>): Promise<T> => {
    setLoading(true);
    setError(null);
    
    try {
      const response = await client.post<ApiResponse<T> | T>(endpoint, itemData);
      const newItem = 'data' in response.data ? response.data.data : response.data;
      
      // Update local data
      setData(prev => [...prev, newItem]);
      setTotal(prev => prev + 1);
      
      return newItem;
    } catch (err: any) {
      const errorMessage = err.response?.data?.message || err.message || 'Failed to create item';
      setError(errorMessage);
      throw err;
    } finally {
      setLoading(false);
    }
  }, [client, endpoint]);

  const update = useCallback(async (id: string, itemData: Partial<T>): Promise<T> => {
    setLoading(true);
    setError(null);
    
    try {
      const response = await client.put<ApiResponse<T> | T>(`${endpoint}/${id}`, itemData);
      const updatedItem = 'data' in response.data ? response.data.data : response.data;
      
      // Update local data
      setData(prev => prev.map(item => item.id === id ? updatedItem : item));
      
      return updatedItem;
    } catch (err: any) {
      const errorMessage = err.response?.data?.message || err.message || 'Failed to update item';
      setError(errorMessage);
      throw err;
    } finally {
      setLoading(false);
    }
  }, [client, endpoint]);

  const deleteItem = useCallback(async (id: string): Promise<void> => {
    setLoading(true);
    setError(null);
    
    try {
      await client.delete(`${endpoint}/${id}`);
      
      // Update local data
      setData(prev => prev.filter(item => item.id !== id));
      setTotal(prev => prev - 1);
    } catch (err: any) {
      const errorMessage = err.response?.data?.message || err.message || 'Failed to delete item';
      setError(errorMessage);
      throw err;
    } finally {
      setLoading(false);
    }
  }, [client, endpoint]);

  const refresh = useCallback(async () => {
    await getAll();
  }, [getAll]);

  return {
    data,
    loading,
    error,
    total,
    hasMore,
    client,
    getAll,
    getById,
    create,
    update,
    delete: deleteItem,
    clearError,
    refresh,
  };
};