import { useState, useEffect, useCallback } from 'react';
import { useApiRequest } from './useApi';
import { getApiBaseUrl } from '../../config/config';
import api from '../api';
import { usePermissions, type UserPermissions } from './usePermissions';
import {
  fetchConnections as fetchConnectionsApi,
  createConnection as createConnectionApi,
  connectService as connectServiceApi,
  disconnectService as disconnectServiceApi,
  deleteConnection as deleteConnectionApi,
  updateConnection as updateConnectionApi,
  refreshMicrosoftToken as refreshMicrosoftTokenApi,
  refreshGoogleToken as refreshGoogleTokenApi,
  type Connection,
  type AttributeDefinition,
  type PaginationParams,
  type CreateConnectionData,
  type ConnectResponse
} from '../api/connectionApi';

// Re-export types for backward compatibility
export type { Connection, AttributeDefinition, PaginationParams, CreateConnectionData, ConnectResponse };

// Hook for managing connections
export function useConnections() {
  const [connections, setConnections] = useState<Connection[]>([]);
  const [attributes, setAttributes] = useState<AttributeDefinition[]>([]);
  const [permissions, setPermissions] = useState<UserPermissions | null>(null);
  const [pagination, setPagination] = useState<{
    currentPage: number;
    pageSize: number;
    totalItems: number;
    totalPages: number;
  } | null>(null);
  const [isConnecting, setIsConnecting] = useState(false);
  const [connectError, setConnectError] = useState<string | null>(null);
  const { request, isLoading, error } = useApiRequest<any, any>();
  const { checkPermission } = usePermissions();

  // Fetch attributes from backend
  const fetchAttributes = useCallback(async () => {
    try {
      const response = await api.get('/api/attributes/UserConnection');
      
      // Extract attributes from response
      let attrs: AttributeDefinition[] = [];
      if (response.data?.attributes && Array.isArray(response.data.attributes)) {
        attrs = response.data.attributes;
      } else if (Array.isArray(response.data)) {
        attrs = response.data;
      } else if (response.data && typeof response.data === 'object') {
        const keys = Object.keys(response.data);
        for (const key of keys) {
          if (Array.isArray(response.data[key])) {
            attrs = response.data[key];
            break;
          }
        }
      }
      
      setAttributes(attrs);
      return attrs;
    } catch (error: any) {
      console.error('Error fetching attributes:', error);
      setAttributes([]);
      return [];
    }
  }, []);

  // Fetch permissions from backend
  const fetchPermissions = useCallback(async () => {
    try {
      const perms = await checkPermission('DATA', 'UserConnection');
      setPermissions(perms);
      return perms;
    } catch (error: any) {
      console.error('Error fetching permissions:', error);
      const defaultPerms: UserPermissions = {
        view: false,
        read: 'n',
        create: 'n',
        update: 'n',
        delete: 'n',
      };
      setPermissions(defaultPerms);
      return defaultPerms;
    }
  }, [checkPermission]);

  // Fetch connections with pagination support
  const fetchConnections = useCallback(async (params?: PaginationParams): Promise<Connection[]> => {
    try {
      const data = await fetchConnectionsApi(request, params);
      
      // Handle paginated response
      if (data && typeof data === 'object' && 'items' in data) {
        const items = Array.isArray(data.items) ? data.items : [];
        setConnections(items);
        if (data.pagination) {
          setPagination(data.pagination);
        }
      } else {
        // Handle non-paginated response (backward compatibility)
        const items = Array.isArray(data) ? data : [];
        setConnections(items);
        setPagination(null);
      }
      
      return Array.isArray(data) ? data : (data?.items || []);
    } catch (error) {
      console.error('Error fetching connections:', error);
      setConnections([]);
      setPagination(null);
      throw error;
    }
  }, [request]);

  // Create a new connection
  const createConnection = async (connectionData: CreateConnectionData): Promise<Connection> => {
    try {
      const data = await createConnectionApi(request, connectionData);
      
      // Update local state
      setConnections(prev => {
        const existing = prev.find(conn => conn.id === data.id);
        if (existing) {
          return prev.map(conn => conn.id === data.id ? data : conn);
        } else {
          return [...prev, data];
        }
      });
      
      return data;
    } catch (error) {
      console.error('Error creating connection:', error);
      throw error;
    }
  };

  // Connect to a service (initiate OAuth)
  const connectService = async (connectionId: string): Promise<ConnectResponse> => {
    try {
      const data = await connectServiceApi(request, connectionId);
      return data;
    } catch (error) {
      console.error('Error connecting service:', error);
      throw error;
    }
  };

  // Disconnect from a service
  const disconnectService = async (connectionId: string): Promise<{ message: string }> => {
    try {
      const data = await disconnectServiceApi(request, connectionId);
      
      // Update local state
      setConnections(prev => 
        prev.map(conn => 
          conn.id === connectionId 
            ? { ...conn, status: 'inactive' as any, lastChecked: Math.floor(Date.now() / 1000) }
            : conn
        )
      );
      
      return data;
    } catch (error) {
      console.error('Error disconnecting service:', error);
      throw error;
    }
  };

  // Delete a connection
  const deleteConnection = async (connectionId: string): Promise<{ message: string }> => {
    try {
      const data = await deleteConnectionApi(request, connectionId);
      
      // Update local state
      setConnections(prev => prev.filter(conn => conn.id !== connectionId));
      
      return data;
    } catch (error) {
      console.error('Error deleting connection:', error);
      throw error;
    }
  };

  // Update a connection
  const updateConnection = async (connectionId: string, updateData: Partial<Connection>): Promise<Connection> => {
    try {
      const data = await updateConnectionApi(request, connectionId, updateData);
      
      // Update local state
      setConnections(prev => 
        prev.map(conn => conn.id === connectionId ? { ...conn, ...data } : conn)
      );
      
      return data;
    } catch (error) {
      console.error('Error updating connection:', error);
      throw error;
    }
  };

  // Refresh Microsoft token
  const refreshMicrosoftToken = async (connectionId: string): Promise<Connection> => {
    try {
      const data = await refreshMicrosoftTokenApi(request, connectionId);
      
      // Update local state
      setConnections(prev => 
        prev.map(conn => conn.id === connectionId ? { ...conn, ...data } : conn)
      );
      
      return data;
    } catch (error) {
      console.error('Error refreshing Microsoft token:', error);
      throw error;
    }
  };

  // Refresh Google token
  const refreshGoogleToken = async (connectionId: string): Promise<Connection> => {
    try {
      const data = await refreshGoogleTokenApi(request, connectionId);
      
      // Update local state
      setConnections(prev => 
        prev.map(conn => conn.id === connectionId ? { ...conn, ...data } : conn)
      );
      
      return data;
    } catch (error) {
      console.error('Error refreshing Google token:', error);
      throw error;
    }
  };

  // Connect with popup (OAuth flow)
  const connectWithPopup = async (connectionId: string): Promise<void> => {
    setIsConnecting(true);
    setConnectError(null);

    try {
      // Get the OAuth URL from backend
      const response = await connectService(connectionId);
      if (!response.authUrl) {
        throw new Error('No OAuth URL received from backend');
      }

      console.log('OAuth URL from backend:', response.authUrl);

      return new Promise((resolve, reject) => {
        // Convert relative URL to absolute URL if needed
        let authUrl = response.authUrl;
        if (authUrl.startsWith('/')) {
          authUrl = `${getApiBaseUrl()}${authUrl}`;
        }

        // Open popup
        const popup = window.open(
          authUrl,
          'oauth-connect',
          'width=500,height=600,scrollbars=yes,resizable=yes'
        );

        if (!popup) {
          setConnectError('Popup was blocked. Please allow popups and try again.');
          setIsConnecting(false);
          reject(new Error('Popup was blocked'));
          return;
        }

        // Handle popup closing without completing auth
        const checkClosed = setInterval(() => {
          if (popup.closed) {
            clearInterval(checkClosed);
            window.removeEventListener('message', messageListener);
            setIsConnecting(false);
            console.log('OAuth popup closed');
            // Refresh connections anyway in case it succeeded
            fetchConnections();
            resolve(); // Resolve instead of reject to avoid error on normal close
          }
        }, 1000);

        // Listen for messages from the popup
        const messageListener = (event: MessageEvent) => {
          // Verify origin for security
          const apiUrl = new URL(getApiBaseUrl());
          if (event.origin !== apiUrl.origin) {
            return;
          }

          if (event.data.type === 'msft_connection_success' || event.data.type === 'google_connection_success') {
            // Clean up
            clearInterval(checkClosed);
            window.removeEventListener('message', messageListener);
            popup.close();
            setIsConnecting(false);
            console.log('OAuth connection successful');
            // Refresh connections
            fetchConnections();
            resolve();
          } else if (event.data.type === 'msft_connection_error' || event.data.type === 'google_connection_error') {
            // Handle error
            clearInterval(checkClosed);
            window.removeEventListener('message', messageListener);
            popup.close();
            setIsConnecting(false);
            setConnectError(event.data.error || 'OAuth connection failed');
            reject(new Error(event.data.error || 'OAuth connection failed'));
          }
        };

        // Add message listener
        window.addEventListener('message', messageListener);
      });
    } catch (error: any) {
      setConnectError(error.message || 'OAuth connection failed');
      setIsConnecting(false);
      throw error;
    }
  };

  // Create Google connection and open OAuth popup
  const createGoogleConnectionAndAuth = async (): Promise<void> => {
    try {
      // Step 1: Create a Google connection
      const newConnection = await createConnection({
        type: 'google',
        authority: 'google'
      });
      
      // Step 2: Get the OAuth URL from backend for this specific connection
      const connectResponse = await connectServiceApi(request, newConnection.id);
      
      if (!connectResponse.authUrl) {
        throw new Error('No OAuth URL received from backend');
      }
      
      // Step 3: Open popup to the OAuth URL
      const apiBaseUrl = getApiBaseUrl();
      let authUrl = connectResponse.authUrl;
      if (authUrl.startsWith('/')) {
        authUrl = `${apiBaseUrl}${authUrl}`;
      }
      
      return new Promise((resolve, reject) => {
        const popup = window.open(
          authUrl,
          'google-connection',
          'width=500,height=600,scrollbars=yes,resizable=yes'
        );

        if (!popup) {
          reject(new Error('Popup was blocked. Please allow popups and try again.'));
          return;
        }

        // Handle popup closing without completing auth
        const checkClosed = setInterval(() => {
          if (popup.closed) {
            clearInterval(checkClosed);
            window.removeEventListener('message', messageListener);
            console.log('Google OAuth popup closed');
            // Refresh connections in case it succeeded
            fetchConnections();
            resolve();
          }
        }, 1000);

        // Listen for messages from the popup
        const messageListener = (event: MessageEvent) => {
          // Verify origin for security
          const apiUrl = new URL(apiBaseUrl);
          if (event.origin !== apiUrl.origin) {
            return;
          }

          if (event.data.type === 'google_connection_success' || event.data.type === 'google_auth_success') {
            clearInterval(checkClosed);
            window.removeEventListener('message', messageListener);
            popup.close();
            console.log('Google connection successful');
            // Refresh connections
            fetchConnections();
            resolve();
          } else if (event.data.type === 'google_connection_error') {
            clearInterval(checkClosed);
            window.removeEventListener('message', messageListener);
            popup.close();
            reject(new Error(event.data.error || 'Google connection failed'));
          }
        };

        window.addEventListener('message', messageListener);
      });
    } catch (error) {
      console.error('Error creating Google connection:', error);
      throw error;
    }
  };

  // Create Microsoft connection and open OAuth popup
  const createMicrosoftConnectionAndAuth = async (): Promise<void> => {
    try {
      // Step 1: Create a Microsoft connection
      const newConnection = await createConnection({
        type: 'msft',
        authority: 'msft'
      });
      
      // Step 2: Get the OAuth URL from backend for this specific connection
      const connectResponse = await connectServiceApi(request, newConnection.id);
      
      if (!connectResponse.authUrl) {
        throw new Error('No OAuth URL received from backend');
      }
      
      // Step 3: Open popup to the OAuth URL
      const apiBaseUrl = getApiBaseUrl();
      let authUrl = connectResponse.authUrl;
      if (authUrl.startsWith('/')) {
        authUrl = `${apiBaseUrl}${authUrl}`;
      }
      
      return new Promise((resolve, reject) => {
        const popup = window.open(
          authUrl,
          'msft-connection',
          'width=500,height=600,scrollbars=yes,resizable=yes'
        );

        if (!popup) {
          reject(new Error('Popup was blocked. Please allow popups and try again.'));
          return;
        }

        // Handle popup closing without completing auth
        const checkClosed = setInterval(() => {
          if (popup.closed) {
            clearInterval(checkClosed);
            window.removeEventListener('message', messageListener);
            console.log('Microsoft OAuth popup closed');
            // Refresh connections in case it succeeded
            fetchConnections();
            resolve();
          }
        }, 1000);

        // Listen for messages from the popup
        const messageListener = (event: MessageEvent) => {
          // Verify origin for security
          const apiUrl = new URL(apiBaseUrl);
          if (event.origin !== apiUrl.origin) {
            return;
          }

          if (event.data.type === 'msft_connection_success' || event.data.type === 'msft_auth_success') {
            clearInterval(checkClosed);
            window.removeEventListener('message', messageListener);
            popup.close();
            console.log('Microsoft connection successful');
            // Refresh connections
            fetchConnections();
            resolve();
          } else if (event.data.type === 'msft_connection_error') {
            clearInterval(checkClosed);
            window.removeEventListener('message', messageListener);
            popup.close();
            reject(new Error(event.data.error || 'Microsoft connection failed'));
          }
        };

        window.addEventListener('message', messageListener);
      });
    } catch (error) {
      console.error('Error creating Microsoft connection:', error);
      throw error;
    }
  };

  // Generate edit fields from attributes dynamically
  const generateEditFieldsFromAttributes = useCallback((): Array<{
    key: string;
    label: string;
    type: 'string' | 'boolean' | 'email' | 'textarea' | 'date' | 'enum' | 'readonly';
    editable?: boolean;
    required?: boolean;
    validator?: (value: any) => string | null;
    minRows?: number;
    maxRows?: number;
  }> => {
    if (!attributes || attributes.length === 0) {
      return [];
    }

    const editableFields = attributes
      .filter(attr => {
        // Filter out non-editable fields
        const nonEditableFields = ['id', 'userId', 'connectedAt', 'lastChecked'];
        return !nonEditableFields.includes(attr.name);
      })
      .map(attr => {
        // Map attribute type to form field type
        let fieldType: 'string' | 'boolean' | 'email' | 'textarea' | 'date' | 'enum' | 'readonly' = 'string';
        
        if (attr.type === 'boolean') {
          fieldType = 'boolean';
        } else if (attr.type === 'date') {
          fieldType = 'date';
        } else if (attr.type === 'enum' && attr.filterOptions) {
          fieldType = 'enum';
        } else if (attr.name.toLowerCase().includes('email')) {
          fieldType = 'email';
        }

        return {
          key: attr.name,
          label: attr.label || attr.name,
          type: fieldType,
          editable: true,
          required: false
        };
      });

    return editableFields;
  }, [attributes]);

  // Ensure attributes are loaded
  const ensureAttributesLoaded = useCallback(async () => {
    if (attributes && attributes.length > 0) {
      return attributes;
    }
    const fetchedAttributes = await fetchAttributes();
    return fetchedAttributes;
  }, [attributes, fetchAttributes]);

  // Fetch attributes and permissions on mount
  useEffect(() => {
    fetchAttributes();
    fetchPermissions();
  }, [fetchAttributes, fetchPermissions]);

  // Initial fetch
  useEffect(() => {
    fetchConnections();
  }, [fetchConnections]);

  // Optimistically update a connection in local state
  const updateOptimistically = useCallback((connectionId: string, updateData: Partial<Connection>) => {
    setConnections(prev => 
      prev.map(conn => conn.id === connectionId ? { ...conn, ...updateData } : conn)
    );
  }, []);

  // Generic inline update handler for FormGeneratorTable
  const handleInlineUpdate = useCallback(async (connectionId: string, changes: Partial<Connection>, existingRow?: any) => {
    if (!existingRow) {
      throw new Error('Existing row data required for inline update');
    }
    
    try {
      const result = await updateConnection(connectionId, changes);
      return { success: true, data: result };
    } catch (error: any) {
      throw new Error(error.message || 'Failed to update');
    }
  }, [updateConnection]);

  // Fetch connection by ID
  const fetchConnectionById = useCallback(async (connectionId: string): Promise<Connection | null> => {
    try {
      // Since there's no individual connection endpoint, find from current list or fetch all
      const existing = connections.find(c => c.id === connectionId);
      if (existing) return existing;
      
      const data = await fetchConnectionsApi(request);
      const items = Array.isArray(data) ? data : (data?.items || []);
      return items.find((c: Connection) => c.id === connectionId) || null;
    } catch (error) {
      console.error('Error fetching connection by ID:', error);
      return null;
    }
  }, [connections, request]);

  return {
    connections,
    data: connections, // Alias for FormGenerator compatibility
    fetchConnections,
    refetch: fetchConnections, // Alias for FormGenerator compatibility
    createConnection,
    updateConnection,
    connectService,
    disconnectService,
    deleteConnection,
    refreshMicrosoftToken,
    refreshGoogleToken,
    connectWithPopup,
    createGoogleConnectionAndAuth,
    createMicrosoftConnectionAndAuth,
    isLoading,
    loading: isLoading, // Alias for FormGenerator compatibility
    isConnecting,
    error: error || connectError,
    // Attributes and permissions for dynamic column/button generation
    attributes,
    permissions,
    pagination,
    generateEditFieldsFromAttributes,
    ensureAttributesLoaded,
    fetchAttributes,
    fetchPermissions,
    // Additional methods for FormGenerator
    updateOptimistically,
    handleInlineUpdate,
    fetchConnectionById
  };
}

// Hook for OAuth connection popup flow (similar to useMsalAuth)
export function useOAuthConnect() {
  const { connectService, fetchConnections } = useConnections();
  const [isConnecting, setIsConnecting] = useState(false);
  const [connectError, setConnectError] = useState<string | null>(null);

  const connectWithPopup = async (connectionId: string): Promise<void> => {
    setIsConnecting(true);
    setConnectError(null);

    try {
      // Get the OAuth URL from backend
      const response = await connectService(connectionId);
      if (!response.authUrl) {
        throw new Error('No OAuth URL received from backend');
      }

      console.log('OAuth URL from backend:', response.authUrl);

      return new Promise((resolve, reject) => {
        // Convert relative URL to absolute URL if needed
        let authUrl = response.authUrl;
        if (authUrl.startsWith('/')) {
          authUrl = `${getApiBaseUrl()}${authUrl}`;
        }

        // Open popup using the same pattern as useAuthentication.ts
        const popup = window.open(
          authUrl,
          'oauth-connect',
          'width=500,height=600,scrollbars=yes,resizable=yes'
        );

        if (!popup) {
          setConnectError('Popup was blocked. Please allow popups and try again.');
          setIsConnecting(false);
          reject(new Error('Popup was blocked'));
          return;
        }

        // Handle popup closing without completing auth
        const checkClosed = setInterval(() => {
          if (popup.closed) {
            clearInterval(checkClosed);
            window.removeEventListener('message', messageListener);
            setIsConnecting(false);
            console.log('OAuth popup closed');
            // Refresh connections anyway in case it succeeded
            fetchConnections();
            setConnectError('Authentication was cancelled');
            reject(new Error('Authentication was cancelled'));
          }
        }, 1000);

        // Listen for messages from the popup (similar to useMsalAuth)
        const messageListener = (event: MessageEvent) => {
          // Verify origin for security
          const apiUrl = new URL(getApiBaseUrl());
          if (event.origin !== apiUrl.origin) {
            return;
          }

          if (event.data.type === 'msft_connection_success' || event.data.type === 'google_connection_success') {
            // Clean up - IMPORTANT: clear the checkClosed interval first
            clearInterval(checkClosed);
            window.removeEventListener('message', messageListener);
            popup.close();
            setIsConnecting(false);
            console.log('OAuth connection successful');
            // Refresh connections
            fetchConnections();
            resolve();
          } else if (event.data.type === 'msft_connection_error' || event.data.type === 'google_connection_error') {
            // Handle error - also clear the checkClosed interval
            clearInterval(checkClosed);
            window.removeEventListener('message', messageListener);
            popup.close();
            setIsConnecting(false);
            setConnectError(event.data.error || 'OAuth connection failed');
            reject(new Error(event.data.error || 'OAuth connection failed'));
          }
        };

        // Add message listener
        window.addEventListener('message', messageListener);
      });
    } catch (error: any) {
      setConnectError(error.message || 'OAuth connection failed');
      setIsConnecting(false);
      throw error;
    }
  };

  return {
    connectWithPopup,
    isConnecting,
    error: connectError
  };
}

// Hook for disconnecting services
export function useDisconnect() {
  const { disconnectService, fetchConnections } = useConnections();
  const [isDisconnecting, setIsDisconnecting] = useState(false);
  const [disconnectError, setDisconnectError] = useState<string | null>(null);

  const disconnect = async (connectionId: string): Promise<void> => {
    setIsDisconnecting(true);
    setDisconnectError(null);

    try {
      await disconnectService(connectionId);
      console.log('Service disconnected successfully');
      // Refresh connections to update the status
      await fetchConnections();
    } catch (error: any) {
      setDisconnectError(error.message || 'Disconnect failed');
      console.error('Error disconnecting service:', error);
      throw error;
    } finally {
      setIsDisconnecting(false);
    }
  };

  return {
    disconnect,
    isDisconnecting,
    error: disconnectError
  };
}

// Hook for individual connection operations
export function useConnection(connectionId?: string) {
  const [connection, setConnection] = useState<Connection | null>(null);
  const { request, isLoading, error } = useApiRequest<any, Connection[]>();

  const fetchConnection = async (id: string = connectionId!): Promise<Connection | null> => {
    if (!id) return null;
    
    try {
      // Since there's no individual connection endpoint, fetch all and filter
      const data = await fetchConnectionsApi(request);
      const connections = Array.isArray(data) ? data : (data?.items || []);
      
      const foundConnection = connections.find((conn: Connection) => conn.id === id);
      setConnection(foundConnection || null);
      return foundConnection || null;
    } catch (error) {
      console.error('Error fetching connection:', error);
      setConnection(null);
      throw error;
    }
  };

  return {
    connection,
    fetchConnection,
    isLoading,
    error
  };
} 