import type {
  HuDataMember,
  Message,
  ReactionInfo,
  ReactionWithUser,
  UserReaction,
} from '@modules/chats/interfaces';
import {ChatUserStatus} from '@modules/chats/interfaces';

const mockGetReactions = jest.fn();
const mockMapReactionToStore = jest.fn();
const mockMapReactionWithUserToDB = jest.fn();
const mockMapGlobalUserToChatUser = jest.fn();
const mockGetReactionText = jest.fn();
const mockSetReactionsForMessage = jest.fn();

type ReactionsModule = typeof import('../reactions');
let reactionsModule: ReactionsModule;

jest.mock('@modules/chats/services/reactionsServices', () => ({
  getReactions: (...args: unknown[]) => mockGetReactions(...args),
}));

jest.mock('@modules/chats/utils/reactions', () => ({
  getReactionText: (...args: unknown[]) => mockGetReactionText(...args),
}));

jest.mock('@modules/chats/utils/mappers/userMapper', () => ({
  mapGlobalUserToChatUser: (...args: unknown[]) =>
    mockMapGlobalUserToChatUser(...args),
}));

jest.mock('@modules/chats/utils/mappers/reactionMapper', () => ({
  mapReactionToStore: (...args: unknown[]) => mockMapReactionToStore(...args),
  mapReactionWithUserToDB: (...args: unknown[]) =>
    mockMapReactionWithUserToDB(...args),
}));

jest.mock('@modules/chats/db/services/reactions', () => ({
  setReactionsForMessage: (...args: unknown[]) =>
    mockSetReactionsForMessage(...args),
}));

jest.mock('@redux/store', () => () => ({
  store: {
    getState: () => ({
      user: {id: 99, firstName: 'Current', lastName: 'User'},
    }),
  },
}));

const createUser = (id: number): HuDataMember => ({
  id,
  first_name: 'User',
  last_name: `#${id}`,
  employee_internal_id: `EMP-${id}`,
  picture_asset_url: null,
  status: ChatUserStatus.Active,
  email: 'user@test.dev',
  version: 1,
});

const createReactionInfo = (
  overrides: Partial<ReactionInfo> = {},
): ReactionInfo => ({
  summary: [
    {
      name: '👍',
      count: 1,
      text: '👍 1',
      hasUserReaction: true,
    },
  ],
  userReactions: ['👍'],
  list: [
    {
      name: '👍',
      count: 1,
      users: [createUser(10)],
    },
  ],
  ...overrides,
});

describe('reactions store operations', () => {
  const loadReactionsModule = () => {
    jest.resetModules();
    jest.isolateModules(() => {
      reactionsModule = require('../reactions');
    });
    return reactionsModule;
  };

  beforeEach(() => {
    reactionsModule = loadReactionsModule();
    mockGetReactions.mockReset();
    mockMapReactionToStore.mockReset();
    mockMapReactionWithUserToDB.mockReset();
    mockMapGlobalUserToChatUser.mockReset();
    mockGetReactionText.mockReset();
    mockSetReactionsForMessage.mockReset();
    reactionsModule.clearReactions();
  });

  it('loads reactions when showing the dialog', async () => {
    const response: ReactionWithUser[] = [
      {name: '👍', count: 1, users: [createUser(10)]},
    ];
    const mapped = createReactionInfo();

    mockGetReactions.mockResolvedValue(response);
    mockMapReactionToStore.mockReturnValue(mapped);
    mockMapReactionWithUserToDB.mockReturnValue([{name: '👍', count: 1}]);

    reactionsModule.showReactionList({
      channel: 'channel-1',
      ts: 'message-ts',
      clientMsgId: 'client-1',
    });

    await mockGetReactions.mock.results[0]?.value;

    expect(mockGetReactions).toHaveBeenCalledWith({
      channel: 'channel-1',
      ts: 'message-ts',
      full: true,
    });
    expect(mockMapReactionToStore).toHaveBeenCalledWith({
      reactionsWithUser: response,
    });
    expect(mockSetReactionsForMessage).toHaveBeenCalledWith('client-1', [
      {name: '👍', count: 1},
    ]);
    expect(reactionsModule.getUserReactions('client-1')).toEqual(['👍']);
  });

  it('adds message reactions in batch', () => {
    const first = createReactionInfo({userReactions: ['👍']});
    const second = createReactionInfo({userReactions: ['🔥']});

    reactionsModule.addMessageReactionsBatch([
      {clientMsgId: 'client-1', reactions: first},
      {clientMsgId: 'client-2', reactions: second},
    ]);

    expect(reactionsModule.getUserReactions('client-1')).toEqual(['👍']);
    expect(reactionsModule.getUserReactions('client-2')).toEqual(['🔥']);
  });

  it('sets message reactions from server data', () => {
    const mapped = createReactionInfo({userReactions: ['🎉']});
    const reactions = [
      {name: '🎉', count: 2, users: [createUser(20)]},
    ] as ReactionWithUser[];

    mockMapReactionToStore.mockReturnValue(mapped);

    reactionsModule.setMessageReactions({
      clientMsgId: 'client-1',
      reactions,
    });

    expect(mockMapReactionToStore).toHaveBeenCalledWith({
      reactionsWithUser: reactions,
    });
    expect(reactionsModule.getUserReactions('client-1')).toEqual(['🎉']);
  });

  it('optimistically adds and removes reactions', () => {
    const initial = createReactionInfo({
      summary: [
        {
          name: '👍',
          count: 1,
          text: '👍 1',
          hasUserReaction: false,
        },
      ],
      userReactions: [],
      list: [{name: '👍', count: 1, users: [createUser(1)]}],
    });

    reactionsModule.addMessageReactionsBatch([
      {clientMsgId: 'client-1', reactions: initial},
    ]);
    mockMapGlobalUserToChatUser.mockReturnValue(createUser(99));
    mockGetReactionText.mockImplementation(
      ({name, count}: {name: string; count: number}) => `${name} ${count}`,
    );

    reactionsModule.optimisticUpdateReaction({
      channel: 'channel-1',
      clientMsgId: 'client-1',
      reactionName: '👍',
      userId: 99,
      action: 'add',
    } as UserReaction);

    expect(reactionsModule.getUserReactions('client-1')).toEqual(['👍']);

    reactionsModule.optimisticUpdateReaction({
      channel: 'channel-1',
      clientMsgId: 'client-1',
      reactionName: '👍',
      userId: 99,
      action: 'remove',
    } as UserReaction);

    expect(reactionsModule.getUserReactions('client-1')).toEqual([]);
  });

  it('creates a new reactions entry when missing', () => {
    const mapped = createReactionInfo({userReactions: ['🔥']});
    mockMapReactionToStore.mockReturnValue(mapped);

    reactionsModule.optimisticUpdateReaction({
      channel: 'channel-1',
      clientMsgId: 'client-1',
      reactionName: '🔥',
      userId: 1,
      action: 'add',
    } as UserReaction);

    expect(mockMapReactionToStore).toHaveBeenCalledWith({
      reactions: [{name: '🔥', count: 1, users: ['1']}],
    });
    expect(reactionsModule.getUserReactions('client-1')).toEqual(['🔥']);
  });

  it('adds reactions from message payloads', () => {
    mockMapReactionToStore
      .mockReturnValueOnce(createReactionInfo({userReactions: ['👍']}))
      .mockReturnValueOnce(createReactionInfo({userReactions: ['🎉']}));

    const messages: Message[] = [
      {
        text: 'hello',
        channel: 'channel-1',
        ts: '1',
        user: 'user-1',
        client_msg_id: 'client-1',
        username: 'User',
        version: 1,
        height: 1,
        highlighted_spans: [],
        formatted_text: 'hello',
        reactions: [],
      },
      {
        text: 'hello',
        channel: 'channel-1',
        ts: '2',
        user: 'user-1',
        client_msg_id: 'client-2',
        username: 'User',
        version: 1,
        height: 1,
        highlighted_spans: [],
        formatted_text: 'hello',
        reactions: [],
      },
    ];

    reactionsModule.addReactionsFromStores(messages);

    expect(mockMapReactionToStore).toHaveBeenCalledTimes(2);
    expect(reactionsModule.getUserReactions('client-1')).toEqual(['👍']);
    expect(reactionsModule.getUserReactions('client-2')).toEqual(['🎉']);
  });
});
