import React, { createContext, useState, useCallback, ReactNode, useEffect } from 'react';
import { invoke } from "@tauri-apps/api/core";
import { listen } from "@tauri-apps/api/event";
import { Plugin, PotentialPluginMatch } from '../../types/plugin.types';
import { BulkUpdateProgressPayload, SingleUpdateResultPayload } from '../../types/events.types';
import { ScanResult } from '../../types/server.types';
import { canUpdatePlugin } from '../../utils/validators';
import { createUpdateMessage } from '../../utils/formatters';
import { ServerType } from '../../types/server.types';
import { useServerContext } from '../ServerContext/useServerContext';

interface PluginContextProps {
  /**
   * List of plugins installed on the server
   */
  plugins: Plugin[];

  /**
   * Currently selected plugin (for details view)
   */
  selectedPlugin: Plugin | null;

  /**
   * Whether plugin updates are being checked
   */
  isCheckingUpdates: boolean;

  /**
   * Error message specific to plugin operations
   */
  updateError: string | null;

  /**
   * Loading states for individual plugins (keyed by file_path)
   */
  pluginLoadingStates: Record<string, boolean>;

  /**
   * Progress information for bulk update checks
   */
  bulkUpdateProgress: BulkUpdateProgressPayload | null;

  /**
   * Whether a single plugin update check is in progress
   */
  isCheckingSinglePlugin: boolean;

  /**
   * Function to check for updates for all plugins
   */
  checkForUpdates: (serverType?: ServerType) => Promise<void>;

  /**
   * Function to check for updates for a single plugin
   */
  checkSinglePlugin: (plugin: Plugin) => Promise<void>;

  /**
   * Function to update a plugin to the latest version
   */
  updatePlugin: (plugin: Plugin) => Promise<void>;

  /**
   * Function to select a plugin for viewing details
   */
  showPluginDetails: (plugin: Plugin) => void;

  /**
   * Function to close the plugin details view
   */
  closePluginDetails: () => void;

  /**
   * Function to set the plugins array directly
   */
  setPlugins: (plugins: Plugin[]) => void;

  /**
   * Function to clear update errors
   */
  clearUpdateError: () => void;
}

// Create the context with default values
export const PluginContext = createContext<PluginContextProps>({} as PluginContextProps);

interface PluginProviderProps {
  children: ReactNode;
}

/**
 * Provider component for managing plugin-related state
 */
export const PluginProvider: React.FC<PluginProviderProps> = ({
  children
}) => {
  // Get server context directly
  const { serverPath, serverInfo } = useServerContext();
  const serverType = serverInfo?.server_type;

  const [plugins, setPluginsState] = useState<Plugin[]>([]);
  const [selectedPlugin, setSelectedPlugin] = useState<Plugin | null>(null);
  const [isCheckingUpdates, setIsCheckingUpdates] = useState<boolean>(false);
  const [updateError, setUpdateError] = useState<string | null>(null);
  const [pluginLoadingStates, setPluginLoadingStates] = useState<Record<string, boolean>>({});
  const [bulkUpdateProgress, setBulkUpdateProgress] = useState<BulkUpdateProgressPayload | null>(null);
  const [isCheckingSinglePlugin, setIsCheckingSinglePlugin] = useState<boolean>(false);

  // Setup event listeners
  useEffect(() => {
    const unlisteners: (() => void)[] = [];

    // Listen for scan-completed events to update plugins
    listen('scan_completed', (event) => {
      console.log("Received scan_completed event in PluginContext:", event.payload);
      const result = event.payload as ScanResult;

      // Update the plugins state with the scanned plugins
      setPluginsState(result.plugins);
      console.log(`Updated plugins state with ${result.plugins.length} plugins`);
    }).then(unlisten => unlisteners.push(unlisten));

    // Listen for update check progress
    listen<BulkUpdateProgressPayload>("update_check_progress", (event) => {
      console.log("Update check progress event received:", event.payload);
      setBulkUpdateProgress(event.payload);
    }).then(unlisten => unlisteners.push(unlisten));

    // Listen for single update check completed
    listen<SingleUpdateResultPayload>("single_update_check_completed", (eventData) => {
      const { original_file_path, plugin: updatedPlugin, error } = eventData.payload;
      console.log("Single update check completed event received:", original_file_path);

      if (error) {
        console.error("Error checking plugin for updates:", error);
        setUpdateError(error);
      }

      if (updatedPlugin) {
        console.log("Plugin update check result:", updatedPlugin);

        // Update the plugin in the list
        setPluginsState(currentPlugins => {
          return currentPlugins.map(p => {
            if (p.file_path === original_file_path) {
              return updatedPlugin;
            }
            return p;
          });
        });
      }

      // Clear loading state for this plugin - IMPORTANT: always clear the loading state
      console.log(`Clearing loading state for plugin path: ${original_file_path}`);
      setPluginLoadingStates(prev => {
        const newState = { ...prev };
        delete newState[original_file_path];
        return newState;
      });

      // Also notify the UI that the check is complete
      const customEvent = new CustomEvent('single_plugin_check_completed', {
        detail: {
          plugin_path: original_file_path,
          success: !error,
          error: error
        }
      });
      window.dispatchEvent(customEvent);
    }).then(unlisten => unlisteners.push(unlisten));

    // Listen for update check complete
    listen("update_check_complete", (event) => {
      console.log("Update check complete event received:", event);

      // Optionally handle any additional logic needed when bulk update is complete
    }).then(unlisten => unlisteners.push(unlisten));

    // Cleanup function
    return () => {
      unlisteners.forEach(unlisten => unlisten());
    };
  }, []);

  // Set plugins directly
  const setPlugins = useCallback((newPlugins: Plugin[]) => {
    setPluginsState(newPlugins);
  }, []);

  // Clear update error
  const clearUpdateError = useCallback(() => {
    setUpdateError(null);
  }, []);

  // Log when serverPath changes
  useEffect(() => {
    console.log(`PluginContext: serverPath changed to ${serverPath}`);
  }, [serverPath]);

  // Check for updates for all plugins
  const checkForUpdates = useCallback(async (currentServerType?: ServerType) => {
    const currentServerPath = serverPath;
    console.log(`checkForUpdates called with serverPath: ${currentServerPath}`);

    if (!plugins.length) {
      console.error('No plugins to check for updates');
      setUpdateError('No plugins to check for updates');
      return;
    }

    if (isCheckingUpdates) {
      console.warn('Update check already in progress');
      return;
    }

    if (!currentServerPath) {
      console.error('No server path available in PluginContext');
      setUpdateError('No server path available');
      return;
    }

    console.log(`Starting update check with serverPath: ${currentServerPath}`);
    console.log(`Total plugins to check: ${plugins.length}`);
    console.log(`Server type: ${currentServerType || serverType || 'Unknown'}`);

    setIsCheckingUpdates(true);
    setUpdateError(null);
    setBulkUpdateProgress(null);
    console.log("Invoking bulk check_plugin_updates...");

    try {
      // Include all repositories to check
      const repositoriesToCheck = ['hangarmc', 'spigotmc', 'modrinth', 'github'];

      // Prepare plugins data with correct structure
      const pluginsToSend = plugins.map(p => ({
        name: p.name,
        version: p.version,
        authors: p.authors || [],
        file_path: p.file_path,
        file_hash: p.file_hash,
        website: p.website,
        description: p.description,
        api_version: p.api_version,
        main_class: p.main_class,
        depend: p.depend,
        soft_depend: p.soft_depend,
        load_before: p.load_before,
        commands: p.commands,
        permissions: p.permissions,
        has_update: p.has_update || false,
        repository_source: p.repository_source,
        repository_id: p.repository_id,
        repository_url: p.repository_url,
      }));

      console.log("Sending plugin data to backend, count:", pluginsToSend.length);
      console.log("Using repositories:", repositoriesToCheck);
      console.log("Sample plugin data:", pluginsToSend[0]);

      const updatedPlugins = await invoke<Plugin[]>("check_plugin_updates", {
        plugins: pluginsToSend,
        repositories: repositoriesToCheck,
      });

      console.log("Bulk update check completed successfully, updating state.");
      console.log(`Received ${updatedPlugins.length} updated plugins from backend`);

      // Ensure up-to-date plugins have a latest_version
      const processedPlugins = updatedPlugins.map(plugin => {
        if (!plugin.latest_version && !plugin.has_update) {
          return {
            ...plugin,
            latest_version: plugin.version
          };
        }
        return plugin;
      });

      setPlugins(processedPlugins);

      // Emit an update check complete event if not emitted by backend
      let updatedCount = processedPlugins.filter(p => p.has_update).length;
      console.log(`Update check complete: ${updatedCount} plugins need updates`);

      // Send a custom update_check_complete event to ensure the UI is updated
      const event = new CustomEvent('update_check_complete', {
        detail: {
          outdated_count: updatedCount,
          total_checked: processedPlugins.length,
          success: true
        }
      });
      window.dispatchEvent(event);

      if (currentServerPath) {
        try {
          console.log("[checkForUpdates] Saving plugin data...");
          await invoke("save_plugin_data", { plugins: processedPlugins, serverPath: currentServerPath });
          console.log("[checkForUpdates] Plugin data saved successfully.");
        } catch (saveError) {
          console.error("Error saving plugin data after bulk update:", saveError);
          setUpdateError(`Update check complete, but failed to save plugin data: ${saveError}`);
        }
      }
    } catch (error) {
      console.error("Error checking for updates:", error);
      setUpdateError(`Failed to check for updates: ${error}`);
    } finally {
      setIsCheckingUpdates(false);
      setBulkUpdateProgress(null);
    }
  }, [plugins, isCheckingUpdates, serverPath, setPlugins, serverType]);

  // Check for updates for a single plugin
  const checkSinglePlugin = useCallback(async (plugin: Plugin) => {
    const currentServerPath = serverPath;
    console.log(`checkSinglePlugin called with serverPath: ${currentServerPath}`);

    if (!currentServerPath) {
      console.error('No server path available for checking single plugin');
      setUpdateError('No server path available');
      return;
    }

    setIsCheckingSinglePlugin(true);
    setUpdateError(null);
    setPluginLoadingStates(prev => ({
      ...prev,
      [plugin.file_path]: true
    }));

    try {
      console.log(`Checking for updates for plugin: ${plugin.name}`);

      // Use all lowercase repository names to match backend expectations
      const repositories = ["hangarmc", "spigotmc", "modrinth", "github"];

      console.log(`Repositories to check: ${repositories.join(', ')}`);

      await invoke("check_single_plugin_update_command", {
        plugin: plugin,
        repositories: repositories
      });

      console.log(`Single plugin update check initiated for ${plugin.name}`);
    } catch (err) {
      console.error(`Error checking plugin ${plugin.name} for updates:`, err);
      setUpdateError(`Failed to check ${plugin.name} for updates: ${err}`);

      // Clear loading state for this plugin
      setPluginLoadingStates(prev => {
        const newState = { ...prev };
        delete newState[plugin.file_path];
        return newState;
      });
    } finally {
      setIsCheckingSinglePlugin(false);
    }
  }, [serverPath]);

  // Update a plugin to the latest version
  const updatePlugin = useCallback(async (plugin: Plugin) => {
    const currentServerPath = serverPath;
    const currentServerType = serverType;

    console.log(`updatePlugin called with serverPath: ${currentServerPath}`);

    if (!canUpdatePlugin(plugin)) {
      setUpdateError(`Cannot update ${plugin.name}: Missing required update information`);
      return;
    }

    if (!currentServerPath) {
      console.error('No server path available for updating plugin');
      setUpdateError('No server path available');
      return;
    }

    // Set loading state for this plugin
    setPluginLoadingStates(prev => ({ ...prev, [plugin.file_path]: true }));
    setUpdateError(null);

    try {
      console.log(`Updating plugin: ${plugin.name} to version ${plugin.latest_version}`);

      // Create update message
      const updateMessage = createUpdateMessage(
        plugin.name,
        plugin.latest_version,
        plugin.platform_compatibility
      );

      // This is simplified as the actual UI feedback would be handled by the UIContext
      console.log(updateMessage);

      const newFilePath = await invoke<string>("update_plugin", {
        pluginId: plugin.repository_id,
        version: plugin.latest_version,
        repository: plugin.repository_source,
        currentFilePath: plugin.file_path,
        serverTypeStr: currentServerType
      });

      console.log(`Update successful for ${plugin.name}, new file path: ${newFilePath}`);

      // Update the plugins array with the updated plugin
      const updatedPlugins = plugins.map(p => {
        if (p.file_path === plugin.file_path) {
          return {
            ...p,
            version: p.latest_version || p.version,
            has_update: false,
            latest_version: p.latest_version,
            file_path: newFilePath
          };
        }
        return p;
      });

      setPluginsState(updatedPlugins);

      // Save updated plugins data
      if (currentServerPath) {
        try {
          await invoke("save_plugin_data", {
            plugins: updatedPlugins,
            serverPath: currentServerPath
          });
          console.log(`Plugin data saved successfully after updating ${plugin.name}`);
        } catch (saveError) {
          console.error(`Error saving plugin data after update: ${saveError}`);
          setUpdateError(`Plugin updated, but failed to save plugin data: ${saveError}`);
        }
      }

      // Dispatch custom event for plugin update completed
      const event = new CustomEvent('single_plugin_updated', {
        detail: {
          plugin_name: plugin.name,
          old_path: plugin.file_path,
          new_path: newFilePath,
          success: true
        }
      });
      window.dispatchEvent(event);
    } catch (error) {
      console.error(`Error updating plugin: ${error}`);
      setUpdateError(`Failed to update ${plugin.name}: ${error}`);

      // Dispatch custom event for plugin update failed
      const event = new CustomEvent('single_plugin_update_failed', {
        detail: {
          plugin_name: plugin.name,
          error: String(error)
        }
      });
      window.dispatchEvent(event);
    } finally {
      // Clear loading state for this plugin
      setPluginLoadingStates(prev => {
        const newState = { ...prev };
        delete newState[plugin.file_path];
        return newState;
      });
    }
  }, [plugins, serverPath, serverType]);

  // Show plugin details
  const showPluginDetails = useCallback((plugin: Plugin) => {
    setSelectedPlugin(plugin);
  }, []);

  // Close plugin details
  const closePluginDetails = useCallback(() => {
    setSelectedPlugin(null);
  }, []);

  // Define the context value
  const contextValue: PluginContextProps = {
    plugins,
    selectedPlugin,
    isCheckingUpdates,
    updateError,
    pluginLoadingStates,
    bulkUpdateProgress,
    isCheckingSinglePlugin,
    checkForUpdates,
    checkSinglePlugin,
    updatePlugin,
    showPluginDetails,
    closePluginDetails,
    setPlugins,
    clearUpdateError
  };

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

export default PluginProvider;