import React, { createContext, useContext, useMemo } from 'react';
import { useChatContext } from './ChatContext';

interface MessagesViewContextValue {
  /** Core conversation data */
  conversation: ReturnType<typeof useChatContext>['conversation'];
  conversationId: string | null | undefined;

  /** Submission and control states */
  isSubmitting: ReturnType<typeof useChatContext>['isSubmitting'];
  abortScroll: ReturnType<typeof useChatContext>['abortScroll'];
  setAbortScroll: ReturnType<typeof useChatContext>['setAbortScroll'];

  /** Message operations */
  ask: ReturnType<typeof useChatContext>['ask'];
  regenerate: ReturnType<typeof useChatContext>['regenerate'];
  handleContinue: ReturnType<typeof useChatContext>['handleContinue'];

  /** Message state management */
  index: ReturnType<typeof useChatContext>['index'];
  latestMessageId: ReturnType<typeof useChatContext>['latestMessageId'];
  latestMessageDepth: ReturnType<typeof useChatContext>['latestMessageDepth'];
  setLatestMessage: ReturnType<typeof useChatContext>['setLatestMessage'];
  getMessages: ReturnType<typeof useChatContext>['getMessages'];
  setMessages: ReturnType<typeof useChatContext>['setMessages'];
}

const MessagesViewContext = createContext<MessagesViewContextValue | undefined>(undefined);

// Export the context so it can be provided by other providers (e.g., ShareMessagesProvider)
export { MessagesViewContext };
export type { MessagesViewContextValue };

export function MessagesViewProvider({ children }: { children: React.ReactNode }) {
  const chatContext = useChatContext();

  const {
    ask,
    index,
    regenerate,
    isSubmitting,
    conversation,
    latestMessageId,
    latestMessageDepth,
    setAbortScroll,
    handleContinue,
    setLatestMessage,
    abortScroll,
    getMessages,
    setMessages,
  } = chatContext;

  /** Memoize conversation-related values */
  const conversationValues = useMemo(
    () => ({
      conversation,
      conversationId: conversation?.conversationId,
    }),
    [conversation],
  );

  /** Memoize submission states */
  const submissionStates = useMemo(
    () => ({
      abortScroll,
      isSubmitting,
      setAbortScroll,
    }),
    [isSubmitting, abortScroll, setAbortScroll],
  );

  /** Memoize message operations (these are typically stable references) */
  const messageOperations = useMemo(
    () => ({
      ask,
      regenerate,
      getMessages,
      setMessages,
      handleContinue,
    }),
    [ask, regenerate, handleContinue, getMessages, setMessages],
  );

  /** Memoize message state values */
  const messageState = useMemo(
    () => ({
      index,
      latestMessageId,
      latestMessageDepth,
      setLatestMessage,
    }),
    [index, latestMessageId, latestMessageDepth, setLatestMessage],
  );

  /** Combine all values into final context value */
  const contextValue = useMemo<MessagesViewContextValue>(
    () => ({
      ...conversationValues,
      ...submissionStates,
      ...messageOperations,
      ...messageState,
    }),
    [conversationValues, submissionStates, messageOperations, messageState],
  );

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

export function useMessagesViewContext() {
  const context = useContext(MessagesViewContext);
  if (!context) {
    throw new Error('useMessagesViewContext must be used within MessagesViewProvider');
  }
  return context;
}

/** Hook for components that only need conversation data */
export function useMessagesConversation() {
  const { conversation, conversationId } = useMessagesViewContext();
  return useMemo(() => ({ conversation, conversationId }), [conversation, conversationId]);
}

/** Hook for components that only need submission states */
export function useMessagesSubmission() {
  const { isSubmitting, abortScroll, setAbortScroll } = useMessagesViewContext();
  return useMemo(
    () => ({ isSubmitting, abortScroll, setAbortScroll }),
    [isSubmitting, abortScroll, setAbortScroll],
  );
}

/** Hook for components that only need message operations */
export function useMessagesOperations() {
  const { ask, regenerate, handleContinue, getMessages, setMessages } = useMessagesViewContext();
  return useMemo(
    () => ({ ask, regenerate, handleContinue, getMessages, setMessages }),
    [ask, regenerate, handleContinue, getMessages, setMessages],
  );
}

type OptionalMessagesOps = Pick<
  MessagesViewContextValue,
  'ask' | 'regenerate' | 'handleContinue' | 'getMessages' | 'setMessages'
>;

const NOOP_OPS: OptionalMessagesOps = {
  ask: () => {},
  regenerate: () => {},
  handleContinue: () => {},
  getMessages: () => undefined,
  setMessages: () => {},
};

/**
 * Hook for components that need message operations but may render outside MessagesViewProvider
 * (e.g. the /search route). Returns no-op stubs when the provider is absent — UI actions will
 * be silently discarded rather than crashing. Callers must use optional chaining on
 * `getMessages()` results, as it returns `undefined` outside the provider.
 */
export function useOptionalMessagesOperations(): OptionalMessagesOps {
  const context = useContext(MessagesViewContext);
  const ask = context?.ask;
  const regenerate = context?.regenerate;
  const handleContinue = context?.handleContinue;
  const getMessages = context?.getMessages;
  const setMessages = context?.setMessages;
  return useMemo(
    () => ({
      ask: ask ?? NOOP_OPS.ask,
      regenerate: regenerate ?? NOOP_OPS.regenerate,
      handleContinue: handleContinue ?? NOOP_OPS.handleContinue,
      getMessages: getMessages ?? NOOP_OPS.getMessages,
      setMessages: setMessages ?? NOOP_OPS.setMessages,
    }),
    [ask, regenerate, handleContinue, getMessages, setMessages],
  );
}

/**
 * Hook for components that need conversation data but may render outside MessagesViewProvider
 * (e.g. the /search route). Returns `undefined` for both fields when the provider is absent.
 */
export function useOptionalMessagesConversation() {
  const context = useContext(MessagesViewContext);
  const conversation = context?.conversation;
  const conversationId = context?.conversationId;
  return useMemo(() => ({ conversation, conversationId }), [conversation, conversationId]);
}

/** Hook for components that only need message state */
export function useMessagesState() {
  const { index, latestMessageId, latestMessageDepth, setLatestMessage } = useMessagesViewContext();
  return useMemo(
    () => ({ index, latestMessageId, latestMessageDepth, setLatestMessage }),
    [index, latestMessageId, latestMessageDepth, setLatestMessage],
  );
}
