import React, {createContext, useState, useContext, useCallback, useEffect} from 'react';
import {collectionService} from '@/services/collectionService';
import {apiRequestService} from '@/services/apiRequestService';
import {serverService} from '@/services/serverService';
import {authService} from '@/services/authService';
import APIClient from '@/services/APIClient.js';
import {queueService} from '@/services/queueService.js';
import {userService} from '@/services/userService.js';

const APIContext = createContext(null);

export const useAPI = () => {
  const context = useContext(APIContext);
  if (!context) {
    throw new Error('useAPI must be used within an APIProvider');
  }
  return context;
};

export const APIProvider = ({
  children,
  contextPath = '/'
}) => {
  const [apiClient] = useState(() => new APIClient());
  const [collections, setCollections] = useState([]);
  const [apiRequests, setApiRequests] = useState([]);
  const [servers, setServers] = useState([]);
  const [loading, setLoading] = useState(false);
  const [error, setError] = useState(null);
  const [isAuthenticated, setIsAuthenticated] = useState(false);
  const [mockRoutes, setMockRoutes] = useState({
    content: [],
    totalPages: 0,
    number: 0,
    first: true,
    last: true
  });

  const [queues, setQueues] = useState({
    content: [],
    totalPages: 0,
    number: 0,
    first: true,
    last: true
  });

  const [users, setUsers] = useState({
    content: [],
    totalPages: 0,
    number: 0,
    first: true,
    last: true
  });

  const [serverList, setServerList] = useState({
    content: [],
    totalPages: 0,
    number: 0,
    first: true,
    last: true
  });


  // Initialize services
  const collections$ = collectionService(contextPath);
  const apiRequests$ = apiRequestService(contextPath);
  const servers$ = serverService();
  const auth$ = authService();
  const queues$ = queueService(contextPath);
  const users$ = userService(contextPath);

  const handleLogout = useCallback(async () => {
    try {
      setIsAuthenticated(false);
      setCollections([]);
      setApiRequests([]);
      setServers([]);
      return true;
    } catch (err) {
      console.error('Logout failed:', err);
      return false;
    }
  }, []);

  const loadServers = useCallback(async () => {
    setLoading(true);
    setError(null);
    try {
      const validatedServers = await servers$.loadServers();
      setServers(validatedServers);
      return validatedServers;
    } catch (err) {
      setError(err.message);
      return [];
    } finally {
      setLoading(false);
    }
  }, []);

  const getServer = useCallback((serverId) => {
    const server = servers.find(server => server.id === serverId);
    return server;
  }, [servers]);

  const loadCollections = useCallback(async () => {
    setLoading(true);
    setError(null);
    try {
      const processedCollections = await collections$.loadCollections();
      setCollections(processedCollections);
      return processedCollections;
    } catch (err) {
      setError(err.message);
      return [];
    } finally {
      setLoading(false);
    }
  }, []);

  const createCollection = useCallback(async (name) => {
    setLoading(true);
    setError(null);
    try {
      await collections$.createCollection(name);
      const updatedCollections = await collections$.loadCollections();
      setCollections(updatedCollections);
    } catch (err) {
      setError(err.message);
      return null;
    } finally {
      setLoading(false);
    }
  }, []);

  const updateCollection = useCallback(async (id, name) => {
    setLoading(true);
    setError(null);
    try {
      await collections$.updateCollection(id, name);
      setCollections(prev =>
          prev.map(collection =>
              collection.id === id ? {...collection, name} : collection
          )
      );
      return true;
    } catch (err) {
      setError(err.message);
      return false;
    } finally {
      setLoading(false);
    }
  }, []);

  const deleteCollection = useCallback(async (id) => {
    setLoading(true);
    setError(null);
    try {
      await collections$.deleteCollection(id);
      setCollections(prev => prev.filter(collection => collection.id !== id));
      return true;
    } catch (err) {
      setError(err.message);
      return false;
    } finally {
      setLoading(false);
    }
  }, []);

  const createAPIRequest = useCallback(async (collectionId, apiData) => {
    setLoading(true);
    setError(null);
    try {
      const newApiRequest = await apiRequests$.createAPIRequest(collectionId, apiData);

      setCollections(prev =>
          prev.map(collection =>
              collection.id === collectionId
                  ? {...collection, apis: [...(collection.apis || []), newApiRequest]}
                  : collection
          )
      );

      setApiRequests(prev => [...prev, newApiRequest]);
      return newApiRequest;
    } catch (err) {
      setError(err.message);
      return null;
    } finally {
      setLoading(false);
    }
  }, []);

  const updateAPIRequest = useCallback(async (apiRequest) => {
    setLoading(true);
    setError(null);
    try {
      await apiRequests$.updateAPIRequest(apiRequest);

      setCollections(prev =>
          prev.map(collection => {
            if (collection.id === apiRequest.collectionId) {
              return {
                ...collection,
                apis: collection.apis.map(api =>
                    api.id === apiRequest.id ? apiRequest : api
                )
              };
            }
            return collection;
          })
      );

      setApiRequests(prev =>
          prev.map(req => req.id === apiRequest.id ? apiRequest : req)
      );

      return apiRequest;
    } catch (err) {
      setError(err.message);
      return null;
    } finally {
      setLoading(false);
    }
  }, []);

  const deleteAPIRequest = useCallback(async (collectionId, apiId) => {
    setLoading(true);
    setError(null);
    try {
      await apiRequests$.deleteAPIRequest(collectionId, apiId);

      setCollections(prev =>
          prev.map(collection => {
            if (collection.id === collectionId) {
              return {
                ...collection,
                apis: collection.apis.filter(api => api.id !== apiId)
              };
            }
            return collection;
          })
      );

      setApiRequests(prev => prev.filter(req => req.id !== apiId));
      return true;
    } catch (err) {
      setError(err.message);
      return false;
    } finally {
      setLoading(false);
    }
  }, []);

  const sendAPIRequest = useCallback(async (apiRequest, preProcessCallback, consoleOutput) => {
    try {
      return await apiClient.sendAPIRequest(apiRequest, preProcessCallback, consoleOutput);
    } catch (err) {
      setError(err.message);
      return null;
    }
  }, [apiClient]);

  const loadMockRoutes = useCallback(async (params = { name: '', page: 0 }) => {
    setLoading(true);
    setError(null);
    try {
      const data = await apiRequests$.getMockRoutes(params);
      setMockRoutes(data);
      return data;
    } catch (err) {
      setError(err.message);
      return null;
    } finally {
      setLoading(false);
    }
  }, []);

  const deleteMockRoutes = useCallback(async (ids) => {
    setLoading(true);
    setError(null);
    try {
      await apiRequests$.deleteMockRoutes(ids);
      await loadMockRoutes(); // Reload the list after deletion
      return true;
    } catch (err) {
      setError(err.message);
      return false;
    } finally {
      setLoading(false);
    }
  }, [loadMockRoutes]);

  const createMockRoute = useCallback(async (mockRoute) => {
    setLoading(true);
    setError(null);
    try {
      const newMockRoute = await apiRequests$.createMockRoute(mockRoute);
      await loadMockRoutes();
      return newMockRoute;
    } catch (err) {
      setError(err.message);
      return null;
    } finally {
      setLoading(false);
    }
  }, [loadMockRoutes]);

  const updateMockRoute = useCallback(async (mockRoute) => {
    setLoading(true);
    setError(null);
    try {
      const updatedMockRoute = await apiRequests$.updateMockRoute(mockRoute);
      await loadMockRoutes();
      return updatedMockRoute;
    } catch (err) {
      setError(err.message);
      return null;
    } finally {
      setLoading(false);
    }
  }, [loadMockRoutes]);


  const loadQueues = useCallback(async (params = { page: 0 }) => {
    setLoading(true);
    setError(null);
    try {
      const data = await queues$.loadQueues(params);
      setQueues(data);
      return data;
    } catch (err) {
      setError(err.message);
      return null;
    } finally {
      setLoading(false);
    }
  }, []);

  const deleteQueues = useCallback(async (ids) => {
    setLoading(true);
    setError(null);
    try {
      await queues$.deleteQueues(ids);
      await loadQueues();
      return true;
    } catch (err) {
      setError(err.message);
      return false;
    } finally {
      setLoading(false);
    }
  }, [loadQueues]);

  const startQueue = useCallback(async (id) => {
    setLoading(true);
    setError(null);
    try {
      await queues$.startQueue(id);
      await loadQueues();
      return true;
    } catch (err) {
      setError(err.message);
      return false;
    } finally {
      setLoading(false);
    }
  }, [loadQueues]);

  const stopQueue = useCallback(async (id) => {
    setLoading(true);
    setError(null);
    try {
      await queues$.stopQueue(id);
      await loadQueues();
      return true;
    } catch (err) {
      setError(err.message);
      return false;
    } finally {
      setLoading(false);
    }
  }, [loadQueues]);

  const loadUsers = useCallback(async (params = { username: '', page: 0 }) => {
    setLoading(true);
    setError(null);
    try {
      const data = await users$.findAll(params);
      setUsers(data);
      return data;
    } catch (err) {
      setError(err.message);
      return null;
    } finally {
      setLoading(false);
    }
  }, []);

  const deleteUsers = useCallback(async (ids) => {
    setLoading(true);
    setError(null);
    try {
      await users$.deleteUsers(ids);
      await loadUsers();
      return true;
    } catch (err) {
      setError(err.message);
      return false;
    } finally {
      setLoading(false);
    }
  }, [loadUsers]);

  const createUser = useCallback(async (user) => {
    setLoading(true);
    setError(null);
    try {
      const newUser = await users$.create(user);
      await loadUsers();
      return newUser;
    } catch (err) {
      setError(err.message);
      return null;
    } finally {
      setLoading(false);
    }
  }, [loadUsers]);

  const updateUser = useCallback(async (userId, user) => {
    setLoading(true);
    setError(null);
    try {
      const updatedUser = await users$.update(userId, user);
      await loadUsers();
      return updatedUser;
    } catch (err) {
      setError(err.message);
      return null;
    } finally {
      setLoading(false);
    }
  }, [loadUsers]);

  const resetPassword = useCallback(async (userId, passwordData) => {
    setLoading(true);
    setError(null);
    try {
      await users$.resetPassword(userId, passwordData);
      return true;
    } catch (err) {
      setError(err.message);
      return false;
    } finally {
      setLoading(false);
    }
  }, []);

  const loadServerList = useCallback(async (params = { page: 0 }) => {
    setLoading(true);
    setError(null);
    try {
      const data = await servers$.loadServerList(params);
      setServerList(data);
      return data;
    } catch (err) {
      setError(err.message);
      return null;
    } finally {
      setLoading(false);
    }
  }, []);

  const deleteServers = useCallback(async (ids) => {
    setLoading(true);
    setError(null);
    try {
      await servers$.deleteServers(ids);
      await loadServerList();
      return true;
    } catch (err) {
      setError(err.message);
      return false;
    } finally {
      setLoading(false);
    }
  }, [loadServerList]);

  const createServer = useCallback(async (server) => {
    setLoading(true);
    setError(null);
    try {
      const newServer = await servers$.createServer(server);
      await loadServerList();
      return newServer;
    } catch (err) {
      setError(err.message);
      return null;
    } finally {
      setLoading(false);
    }
  }, [loadServerList]);

  const updateServer = useCallback(async (server) => {
    setLoading(true);
    setError(null);
    try {
      const updatedServer = await servers$.updateServer(server.id, server);
      await loadServerList();
      return updatedServer;
    } catch (err) {
      setError(err.message);
      return null;
    } finally {
      setLoading(false);
    }
  }, [loadServerList]);

  const contextValue = {
    apiClient,
    collections,
    apiRequests,
    servers,
    loadServers,
    getServer,
    serverList,
    loadServerList,
    deleteServers,
    createServer,
    updateServer,
    loading,
    error,
    loadCollections,
    createCollection,
    updateCollection,
    deleteCollection,
    createAPIRequest,
    updateAPIRequest,
    deleteAPIRequest,
    sendAPIRequest,
    isAuthenticated,
    setIsAuthenticated,
    handleLogout,
    mockRoutes,
    loadMockRoutes,
    deleteMockRoutes,
    createMockRoute,
    updateMockRoute,
    queues,
    loadQueues,
    deleteQueues,
    startQueue,
    stopQueue,
    users,
    loadUsers,
    deleteUsers,
    createUser,
    updateUser,
    resetPassword
  };

  return (
      <APIContext.Provider value={contextValue}>
        {children}
      </APIContext.Provider>
  );
};

export default APIProvider;
