import { useCallback, useMemo, useRef, useState } from 'react';
import axios from 'axios';
import {
  ChatAssistantContextValue,
  ChatDisplayMode,
  ChatUiMessage,
  ChatWebhookResponse,
} from '../types/chatAssistant';
import { useIsDesktopViewport } from './useIsDesktopViewport';

function createMessageId(): string {
  return crypto.randomUUID();
}

function createSessionId(): string {
  return crypto.randomUUID().replace(/-/g, '');
}

function isValidWebhookResponse(data: unknown): data is ChatWebhookResponse {
  if (!data || typeof data !== 'object') {
    return false;
  }

  const payload = data as ChatWebhookResponse;

  return (
    (payload.status === 'success' || payload.status === 'error') &&
    typeof payload.message?.text === 'string' &&
    typeof payload.message?.type === 'string'
  );
}

export function useChatAssistant(): ChatAssistantContextValue {
  const { isDesktop, isWideDesktop } = useIsDesktopViewport();
  const sessionIdRef = useRef<string>(createSessionId());
  const lastFailedInputRef = useRef<string | null>(null);

  const [messages, setMessages] = useState<ChatUiMessage[]>([]);
  const [isOpen, setIsOpen] = useState(false);
  const [isSending, setIsSending] = useState(false);
  const [preferredMode, setPreferredMode] = useState<ChatDisplayMode>('embedded');

  const effectiveMode: ChatDisplayMode = !isWideDesktop && isDesktop ? 'floating' : preferredMode;
  const isEmbeddedOpen = isOpen && effectiveMode === 'embedded';

  const appendMessage = useCallback((message: ChatUiMessage) => {
    setMessages((current) => [...current, message]);
  }, []);

  const dispatchMessage = useCallback(async (messageText: string, addUserBubble: boolean) => {
    if (!messageText || isSending) {
      return;
    }

    lastFailedInputRef.current = null;
    setIsSending(true);

    if (addUserBubble) {
      appendMessage({
        id: createMessageId(),
        role: 'user',
        text: messageText,
        timestamp: Date.now(),
        status: 'sent',
      });
    }

    try {
      const { data } = await axios.post<ChatWebhookResponse>(
        route('chat.send-message'),
        {
          action: 'sendMessage',
          sessionId: sessionIdRef.current,
          chatInput: messageText,
        } satisfies { action: 'sendMessage'; sessionId: string; chatInput: string },
      );

      if (!isValidWebhookResponse(data)) {
        throw new Error('integration');
      }

      if (data.status === 'error') {
        appendMessage({
          id: createMessageId(),
          role: 'error',
          text: data.error?.message ?? 'No se pudo procesar la consulta.',
          timestamp: Date.now(),
          status: 'failed',
        });
        lastFailedInputRef.current = messageText;
        return;
      }

      appendMessage({
        id: createMessageId(),
        role: 'assistant',
        text: data.message.text,
        type: data.message.type,
        data: data.data,
        timestamp: Date.now(),
        status: 'sent',
      });
    } catch {
      appendMessage({
        id: createMessageId(),
        role: 'error',
        text: 'Error de comunicación con el asistente. Inténtalo de nuevo.',
        timestamp: Date.now(),
        status: 'failed',
      });
      lastFailedInputRef.current = messageText;
    } finally {
      setIsSending(false);
    }
  }, [appendMessage, isSending]);

  const sendMessage = useCallback(async (rawText: string) => {
    const chatInput = rawText.trim();

    if (!chatInput) {
      return;
    }

    await dispatchMessage(chatInput, true);
  }, [dispatchMessage]);

  const retryLastMessage = useCallback(async () => {
    if (!lastFailedInputRef.current || isSending) {
      return;
    }

    const text = lastFailedInputRef.current;
    lastFailedInputRef.current = null;
    await dispatchMessage(text, false);
  }, [dispatchMessage, isSending]);

  const startNewConversation = useCallback(() => {
    sessionIdRef.current = createSessionId();
    lastFailedInputRef.current = null;
    setMessages([]);
  }, []);

  const toggleOpen = useCallback(() => {
    setIsOpen((current) => !current);
  }, []);

  const close = useCallback(() => {
    setIsOpen(false);
  }, []);

  return useMemo(
    () => ({
      sessionId: sessionIdRef.current,
      messages,
      isOpen,
      isSending,
      preferredMode,
      effectiveMode,
      isEmbeddedOpen,
      sendMessage,
      retryLastMessage,
      startNewConversation,
      toggleOpen,
      close,
      setPreferredMode,
    }),
    [
      messages,
      isOpen,
      isSending,
      preferredMode,
      effectiveMode,
      isEmbeddedOpen,
      sendMessage,
      retryLastMessage,
      startNewConversation,
      toggleOpen,
      close,
    ],
  );
}
