const mockCompareUlid = jest.fn();
const mockGetTime = jest.fn();
const mockGetDateFromULID = jest.fn();
const mockGetAssetsLabel = jest.fn();
const mockTranslate = jest.fn();

const TRANSLATIONS: Record<string, string> = {
  'chat.messages.forwarded': 'Forwarded',
  'chat.messages.reply': 'Reply to',
  'general.user': 'User',
  'chat.errors.no_send_message': 'Message not sent',
  'general.loading_send': 'Sending',
  'chat.edit.edited': 'Edited',
};

jest.mock('@config/i18n', () => ({
  __esModule: true,
  default: {
    t: (key: string) => mockTranslate(key),
  },
}));

jest.mock('@shared/utils', () => ({
  compareUlid: (...args: unknown[]) => mockCompareUlid(...args),
  asyncMap: jest.fn(),
  getTime: () => mockGetTime(),
}));

jest.mock('../date', () => ({
  getDateFromULID: () => mockGetDateFromULID(),
}));

jest.mock('../files', () => ({
  getAssetsLabel: (...args: unknown[]) => mockGetAssetsLabel(...args),
}));

import type {
  Asset,
  BlockMessageReplied,
  HuDataMember,
  Message,
} from '@modules/chats/interfaces';
import {ChatUserStatus, MessageStatus} from '@modules/chats/interfaces';

let buildMessageAccessibilityLabel: typeof import('../messages').buildMessageAccessibilityLabel;
let sortMessages: typeof import('../messages').sortMessages;

const createMessage = (ts: string): Message =>
  ({
    text: 'hello',
    channel: 'channel-1',
    ts,
    user: 'user-1',
    client_msg_id: `client-${ts}`,
    username: 'User',
    version: 1,
    height: 1,
    highlighted_spans: [],
    formatted_text: 'hello',
  } as Message);

describe('sortMessages', () => {
  beforeEach(() => {
    jest.resetModules();
    mockCompareUlid.mockReset();

    jest.isolateModules(() => {
      // eslint-disable-next-line @typescript-eslint/no-var-requires
      const messages = require('../messages') as typeof import('../messages');
      ({sortMessages} = messages);
    });
  });

  it('merges window messages with sorted pending messages', () => {
    const windowMessages = [createMessage('b'), createMessage('d')];
    const pendingMessages = [createMessage('c'), createMessage('a')];

    mockCompareUlid.mockImplementation((left: string, right: string) =>
      left.localeCompare(right),
    );

    const result = sortMessages(windowMessages, pendingMessages);

    expect(result.map(item => item.ts)).toEqual(['a', 'b', 'c', 'd']);
  });

  it('returns window messages when there are no pending messages', () => {
    const windowMessages = [createMessage('b'), createMessage('d')];

    mockCompareUlid.mockImplementation((left: string, right: string) =>
      left.localeCompare(right),
    );

    const result = sortMessages(windowMessages, []);

    expect(result).toEqual(windowMessages);
  });
});

describe('buildMessageAccessibilityLabel', () => {
  const createSenderData = (
    firstName: string,
    lastName: string,
  ): HuDataMember => ({
    id: 1,
    first_name: firstName,
    last_name: lastName,
    email: 'test@test.com',
    picture_asset_url: null,
    employee_internal_id: '123',
    status: ChatUserStatus.Active,
    version: 1,
  });

  const createAttachment = (format: string, filename = 'file.jpg'): Asset =>
    ({
      metadata: {format},
      filename,
    } as Asset);

  const createReplyBlock = (
    firstName: string,
    lastName: string,
  ): BlockMessageReplied =>
    ({
      sender_membership: {
        user: {
          hu_data: {
            member: {
              first_name: firstName,
              last_name: lastName,
            },
          },
        },
      },
    } as BlockMessageReplied);

  const defaultTs = '01HQXYZ123';

  beforeEach(() => {
    jest.resetModules();
    mockGetTime.mockReset();
    mockGetDateFromULID.mockReset();
    mockGetAssetsLabel.mockReset();
    mockTranslate.mockReset();

    mockGetTime.mockReturnValue('10:30 AM');
    mockGetDateFromULID.mockReturnValue(new Date('2024-01-15T10:30:00'));
    mockGetAssetsLabel.mockReturnValue({label: 'Image', Icon: jest.fn()});
    mockTranslate.mockImplementation((key: string) => TRANSLATIONS[key] || key);

    jest.isolateModules(() => {
      // eslint-disable-next-line @typescript-eslint/no-var-requires
      const messages = require('../messages') as typeof import('../messages');
      ({buildMessageAccessibilityLabel} = messages);
    });
  });

  it('should return sender name with time for simple text message', () => {
    const result = buildMessageAccessibilityLabel({
      senderData: createSenderData('John', 'Doe'),
      isForwarded: false,
      text: 'Hello world',
      ts: defaultTs,
    });

    expect(result).toBe('John Doe, Hello world, 10:30 AM');
  });

  it('should include forwarded indicator', () => {
    const result = buildMessageAccessibilityLabel({
      senderData: createSenderData('John', 'Doe'),
      isForwarded: true,
      text: 'Hello world',
      ts: defaultTs,
    });

    expect(result).toBe('John Doe, Forwarded, Hello world, 10:30 AM');
  });

  it('should include reply indicator with name', () => {
    const result = buildMessageAccessibilityLabel({
      senderData: createSenderData('John', 'Doe'),
      isForwarded: false,
      replyBlock: createReplyBlock('Jane', 'Smith'),
      text: 'I agree',
      ts: defaultTs,
    });

    expect(result).toBe('John Doe, Reply to: Jane Smith, I agree, 10:30 AM');
  });

  it('should include reply with User when no sender membership', () => {
    const result = buildMessageAccessibilityLabel({
      senderData: createSenderData('John', 'Doe'),
      isForwarded: false,
      replyBlock: {} as BlockMessageReplied,
      text: 'I agree',
      ts: defaultTs,
    });

    expect(result).toBe('John Doe, Reply to: User, I agree, 10:30 AM');
  });

  it('should include attachment label from getAssetsLabel', () => {
    mockGetAssetsLabel.mockReturnValue({label: 'Image', Icon: jest.fn()});

    const result = buildMessageAccessibilityLabel({
      senderData: createSenderData('John', 'Doe'),
      isForwarded: false,
      attachments: [createAttachment('image')],
      text: '',
      ts: defaultTs,
    });

    expect(result).toBe('John Doe, Image, 10:30 AM');
    expect(mockGetAssetsLabel).toHaveBeenCalledWith([
      {format: 'image', filename: 'file.jpg'},
    ]);
  });

  it('should include multiple images attachment', () => {
    mockGetAssetsLabel.mockReturnValue({label: '2 Images', Icon: jest.fn()});

    const result = buildMessageAccessibilityLabel({
      senderData: createSenderData('John', 'Doe'),
      isForwarded: false,
      attachments: [
        createAttachment('image', 'photo1.png'),
        createAttachment('image', 'photo2.jpg'),
      ],
      text: '',
      ts: defaultTs,
    });

    expect(result).toBe('John Doe, 2 Images, 10:30 AM');
  });

  it('should include video attachment', () => {
    mockGetAssetsLabel.mockReturnValue({label: 'Video', Icon: jest.fn()});

    const result = buildMessageAccessibilityLabel({
      senderData: createSenderData('John', 'Doe'),
      isForwarded: false,
      attachments: [createAttachment('video', 'movie.mp4')],
      text: '',
      ts: defaultTs,
    });

    expect(result).toBe('John Doe, Video, 10:30 AM');
  });

  it('should include error status', () => {
    const result = buildMessageAccessibilityLabel({
      senderData: createSenderData('John', 'Doe'),
      isForwarded: false,
      text: 'Hello',
      ts: defaultTs,
      status: MessageStatus.Error,
    });

    expect(result).toBe('John Doe, Hello, 10:30 AM, Message not sent');
  });

  it('should include sending status', () => {
    const result = buildMessageAccessibilityLabel({
      senderData: createSenderData('John', 'Doe'),
      isForwarded: false,
      text: 'Hello',
      ts: defaultTs,
      status: MessageStatus.Sending,
    });

    expect(result).toBe('John Doe, Hello, 10:30 AM, Sending');
  });

  it('should include edited indicator', () => {
    const result = buildMessageAccessibilityLabel({
      senderData: createSenderData('John', 'Doe'),
      isForwarded: false,
      text: 'Hello updated',
      ts: defaultTs,
      edited: true,
    });

    expect(result).toBe('John Doe, Hello updated, 10:30 AM, Edited');
  });

  it('should handle complete message with all features', () => {
    mockGetAssetsLabel.mockReturnValue({label: '2 Images', Icon: jest.fn()});

    const result = buildMessageAccessibilityLabel({
      senderData: createSenderData('John', 'Doe'),
      isForwarded: true,
      replyBlock: createReplyBlock('Jane', 'Smith'),
      attachments: [
        createAttachment('image', 'photo1.jpg'),
        createAttachment('image', 'photo2.png'),
      ],
      text: 'Check these photos',
      ts: defaultTs,
      edited: true,
    });

    expect(result).toBe(
      'John Doe, Forwarded, Reply to: Jane Smith, 2 Images, Check these photos, 10:30 AM, Edited',
    );
  });

  it('should handle message with only sender and time', () => {
    const result = buildMessageAccessibilityLabel({
      senderData: createSenderData('John', 'Doe'),
      isForwarded: false,
      text: '',
      ts: defaultTs,
    });

    expect(result).toBe('John Doe, 10:30 AM');
  });

  it('should handle sender with only first name', () => {
    const result = buildMessageAccessibilityLabel({
      senderData: createSenderData('John', ''),
      isForwarded: false,
      text: 'Hello',
      ts: defaultTs,
    });

    expect(result).toBe('John, Hello, 10:30 AM');
  });

  it('should handle sender with only last name', () => {
    const result = buildMessageAccessibilityLabel({
      senderData: createSenderData('', 'Doe'),
      isForwarded: false,
      text: 'Hello',
      ts: defaultTs,
    });

    expect(result).toBe('Doe, Hello, 10:30 AM');
  });

  it('should pass correct data to getAssetsLabel', () => {
    mockGetAssetsLabel.mockReturnValue({label: 'Document', Icon: jest.fn()});

    buildMessageAccessibilityLabel({
      senderData: createSenderData('John', 'Doe'),
      isForwarded: false,
      attachments: [createAttachment('document', 'report.pdf')],
      text: '',
      ts: defaultTs,
    });

    expect(mockGetAssetsLabel).toHaveBeenCalledWith([
      {format: 'document', filename: 'report.pdf'},
    ]);
  });

  it('should handle mixed media attachments', () => {
    mockGetAssetsLabel.mockReturnValue({
      label: '2 Images, 1 Video',
      Icon: jest.fn(),
    });

    const result = buildMessageAccessibilityLabel({
      senderData: createSenderData('John', 'Doe'),
      isForwarded: false,
      attachments: [
        createAttachment('image', 'photo1.jpg'),
        createAttachment('image', 'photo2.png'),
        createAttachment('video', 'movie.mp4'),
      ],
      text: '',
      ts: defaultTs,
    });

    expect(result).toBe('John Doe, 2 Images, 1 Video, 10:30 AM');
  });

  it('should format time correctly using getTime and getDateFromULID', () => {
    mockGetTime.mockReturnValue('3:45 PM');

    const result = buildMessageAccessibilityLabel({
      senderData: createSenderData('John', 'Doe'),
      isForwarded: false,
      text: 'Test',
      ts: '01ABCDEF',
    });

    expect(result).toBe('John Doe, Test, 3:45 PM');
    expect(mockGetDateFromULID).toHaveBeenCalled();
  });
});
