import { createReducer, on } from '@ngrx/store';
import {
  EntityState,
  EntityAdapter,
  createEntityAdapter
} from '@ngrx/entity';
import { DELETED_MESSAGE_CONTENT, Message } from '../../shared-kernel';
import { MessagesActions } from './messages.actions';

/** State shape for the messages feature slice, extending NgRx EntityState. */
export interface MessagesState extends EntityState<Message> {
  /** Whether messages are being loaded from the database. */
  loading: boolean;
  /** Whether a peer-to-peer sync cycle is in progress. */
  syncing: boolean;
  /** Most recent error message from message operations. */
  error: string | null;
  /** ID of the room whose messages are currently loaded. */
  currentRoomId: string | null;
}

export const messagesAdapter: EntityAdapter<Message> = createEntityAdapter<Message>({
  selectId: (message) => message.id,
  sortComparer: (messageA, messageB) => messageA.timestamp - messageB.timestamp
});

export const initialState: MessagesState = messagesAdapter.getInitialState({
  loading: false,
  syncing: false,
  error: null,
  currentRoomId: null
});

export const messagesReducer = createReducer(
  initialState,

  // Load messages - clear stale messages when switching to a different room
  on(MessagesActions.loadMessages, (state, { roomId }) => {
    if (state.currentRoomId && state.currentRoomId !== roomId) {
      return messagesAdapter.removeAll({
        ...state,
        loading: true,
        error: null,
        currentRoomId: roomId
      });
    }

    return {
      ...state,
      loading: true,
      error: null,
      currentRoomId: roomId
    };
  }),

  on(MessagesActions.loadMessagesSuccess, (state, { messages }) =>
    messagesAdapter.setAll(messages, {
      ...state,
      loading: false
    })
  ),

  on(MessagesActions.loadMessagesFailure, (state, { error }) => ({
    ...state,
    loading: false,
    error
  })),

  // Send message
  on(MessagesActions.sendMessage, (state) => ({
    ...state,
    loading: true
  })),

  on(MessagesActions.sendMessageSuccess, (state, { message }) =>
    messagesAdapter.addOne(message, {
      ...state,
      loading: false
    })
  ),

  on(MessagesActions.sendMessageFailure, (state, { error }) => ({
    ...state,
    loading: false,
    error
  })),

  // Receive message from peer
  on(MessagesActions.receiveMessage, (state, { message }) =>
    messagesAdapter.upsertOne(message, state)
  ),

  // Edit message
  on(MessagesActions.editMessageSuccess, (state, { messageId, content, editedAt }) =>
    messagesAdapter.updateOne(
      {
        id: messageId,
        changes: { content,
          editedAt }
      },
      state
    )
  ),

  // Delete message
  on(MessagesActions.deleteMessageSuccess, (state, { messageId }) =>
    messagesAdapter.updateOne(
      {
        id: messageId,
        changes: {
          content: DELETED_MESSAGE_CONTENT,
          isDeleted: true,
          reactions: []
        }
      },
      state
    )
  ),

  // Add reaction
  on(MessagesActions.addReactionSuccess, (state, { reaction }) => {
    const message = state.entities[reaction.messageId];

    if (!message)
      return state;

    const existingReaction = message.reactions.find(
      (existing) => existing.emoji === reaction.emoji && existing.userId === reaction.userId
    );

    if (existingReaction)
      return state;

    return messagesAdapter.updateOne(
      {
        id: reaction.messageId,
        changes: {
          reactions: [...message.reactions, reaction]
        }
      },
      state
    );
  }),

  // Remove reaction
  on(MessagesActions.removeReactionSuccess, (state, { messageId, emoji, oderId }) => {
    const message = state.entities[messageId];

    if (!message)
      return state;

    return messagesAdapter.updateOne(
      {
        id: messageId,
        changes: {
          reactions: message.reactions.filter(
            (existingReaction) => !(existingReaction.emoji === emoji && existingReaction.userId === oderId)
          )
        }
      },
      state
    );
  }),

  // Sync lifecycle
  on(MessagesActions.startSync, (state) => ({
    ...state,
    syncing: true
  })),

  on(MessagesActions.syncComplete, (state) => ({
    ...state,
    syncing: false
  })),

  // Sync messages from peer (merge reactions to avoid losing local-only reactions)
  on(MessagesActions.syncMessages, (state, { messages }) => {
    const merged = messages.map(message => {
      const existing = state.entities[message.id];

      if (existing?.reactions?.length) {
        const combined = [...(message.reactions ?? [])];

        for (const existingReaction of existing.reactions) {
          const alreadyExists = combined.some((combinedReaction) =>
            combinedReaction.userId === existingReaction.userId &&
            combinedReaction.emoji === existingReaction.emoji &&
            combinedReaction.messageId === existingReaction.messageId
          );

          if (!alreadyExists) {
            combined.push(existingReaction);
          }
        }

        return { ...message,
          reactions: combined };
      }

      return message;
    });

    return messagesAdapter.upsertMany(merged, {
      ...state,
      syncing: false
    });
  }),

  // Update link metadata on a message
  on(MessagesActions.updateLinkMetadata, (state, { messageId, linkMetadata }) =>
    messagesAdapter.updateOne(
      {
        id: messageId,
        changes: { linkMetadata }
      },
      state
    )
  ),

  // Clear messages
  on(MessagesActions.clearMessages, (state) =>
    messagesAdapter.removeAll({
      ...state,
      currentRoomId: null
    })
  )
);
