import { createContext, useContext, useState, useEffect, ReactNode, useCallback } from 'react';
import { StreamChat } from 'stream-chat';
import { useQuery } from '@apollo/client/react';
import { STREAM_CREDENTIALS_QUERY } from '../api/operations/queries';
import { authManager } from '../api/auth';

// Stream Chat API Key for Feeld - this is a public key
// Found from app traffic analysis (chat.stream-io-api.com requests)
const STREAM_API_KEY = 'y4tp4akjeb49';

// Message type from Stream Chat
export interface StreamMessage {
  id: string;
  text?: string;
  user?: {
    id: string;
    name?: string;
    image?: string;
  };
  created_at?: string;
  type?: string;
  attachments?: Array<{
    type?: string;
    image_url?: string;
    thumb_url?: string;
    asset_url?: string;
  }>;
}

// eslint-disable-next-line @typescript-eslint/no-explicit-any
type StreamClient = any;
// eslint-disable-next-line @typescript-eslint/no-explicit-any
type StreamChannel = any;

interface StreamChatContextType {
  client: StreamClient | null;
  isConnected: boolean;
  isConnecting: boolean;
  error: string | null;
  userId: string | null;
  userName: string | null;
}

const StreamChatContext = createContext<StreamChatContextType>({
  client: null,
  isConnected: false,
  isConnecting: true,
  error: null,
  userId: null,
  userName: null,
});

export function StreamChatProvider({ children }: { children: ReactNode }) {
  const [client, setClient] = useState<StreamClient | null>(null);
  const [isConnected, setIsConnected] = useState(false);
  const [isConnecting, setIsConnecting] = useState(true);
  const [error, setError] = useState<string | null>(null);
  const [userId, setUserId] = useState<string | null>(null);
  const [userName, setUserName] = useState<string | null>(null);

  const { data, loading, error: queryError } = useQuery(STREAM_CREDENTIALS_QUERY, {
    variables: { profileId: authManager.getProfileId() },
  });

  // Extract stable values to avoid reconnecting on every Apollo cache update
  const streamToken = data?.profile?.streamToken;
  const streamUserId = data?.profile?.streamUserId;
  const streamUserName = data?.profile?.imaginaryName;

  useEffect(() => {
    if (loading) return;

    if (queryError) {
      setError(`Failed to get chat credentials: ${queryError.message}`);
      setIsConnecting(false);
      return;
    }

    if (!streamToken || !streamUserId) {
      setError('Stream credentials not available');
      setIsConnecting(false);
      return;
    }

    let cancelled = false;
    const initClient = async () => {
      try {
        setIsConnecting(true);
        setError(null);

        const chatClient = StreamChat.getInstance(STREAM_API_KEY);

        await chatClient.connectUser(
          {
            id: streamUserId,
            name: streamUserName || 'User',
          },
          streamToken
        );

        if (cancelled) {
          chatClient.disconnectUser().catch(console.error);
          return;
        }

        setClient(chatClient);
        setUserId(streamUserId);
        setUserName(streamUserName);
        setIsConnected(true);
        setIsConnecting(false);
      } catch (err) {
        if (cancelled) return;
        console.error('Stream Chat connection error:', err);
        setError(err instanceof Error ? err.message : 'Failed to connect to chat');
        setIsConnecting(false);
      }
    };

    initClient();

    return () => {
      cancelled = true;
      const chatClient = StreamChat.getInstance(STREAM_API_KEY);
      chatClient.disconnectUser().catch(console.error);
    };
  }, [loading, queryError, streamToken, streamUserId, streamUserName]);

  return (
    <StreamChatContext.Provider value={{ client, isConnected, isConnecting, error, userId, userName }}>
      {children}
    </StreamChatContext.Provider>
  );
}

export function useStreamChat() {
  return useContext(StreamChatContext);
}

// Hook to get a specific channel
export function useChannel(channelId: string) {
  const { client, isConnected } = useStreamChat();
  const [channel, setChannel] = useState<StreamChannel | null>(null);
  const [messages, setMessages] = useState<StreamMessage[]>([]);
  const [loading, setLoading] = useState(true);
  const [error, setError] = useState<string | null>(null);

  useEffect(() => {
    if (!client || !isConnected || !channelId) {
      setLoading(false);
      return;
    }

    const initChannel = async () => {
      try {
        setLoading(true);
        setError(null);

        // Get or create the channel
        const ch = client.channel('messaging', channelId);
        await ch.watch();

        setChannel(ch);
        setMessages(ch.state.messages);
        setLoading(false);

        // Listen for new messages
        const handleNewMessage = (event: { message?: StreamMessage }) => {
          if (event.message) {
            setMessages(prev => [...prev, event.message as StreamMessage]);
          }
        };

        ch.on('message.new', handleNewMessage);

        return () => {
          ch.off('message.new', handleNewMessage);
        };
      } catch (err) {
        console.error('Channel init error:', err);
        setError(err instanceof Error ? err.message : 'Failed to load channel');
        setLoading(false);
      }
    };

    initChannel();
  }, [client, isConnected, channelId]);

  const sendMessage = useCallback(async (text: string) => {
    if (!channel) return;
    try {
      await channel.sendMessage({ text });
    } catch (err) {
      console.error('Send message error:', err);
      throw err;
    }
  }, [channel]);

  const loadMoreMessages = useCallback(async () => {
    if (!channel) return;
    try {
      const response = await channel.query({
        messages: { limit: 30, id_lt: messages[0]?.id },
      });
      setMessages(prev => [...(response.messages || []), ...prev]);
    } catch (err) {
      console.error('Load more messages error:', err);
    }
  }, [channel, messages]);

  return { channel, messages, loading, error, sendMessage, loadMoreMessages };
}
