import {ulid} from 'ulid';

// Storage instance that persists across mock calls
const mockMmkvStorage: Record<string, string> = {};
const mockCreateMMKV = jest.fn();

// Mock PendingMessagesTracker
const mockHasPendingMessagesBefore = jest.fn().mockReturnValue(false);

// Mock definitions as functions for reuse after resetModules (must start with 'mock')
const createMockMMKVInstance = () => ({
  getString: (key: string) => mockMmkvStorage[key],
  getAllKeys: () => Object.keys(mockMmkvStorage),
  set: (key: string, value: string) => {
    mockMmkvStorage[key] = value;
  },
  remove: (key: string) => {
    delete mockMmkvStorage[key];
  },
});

const mockGetMmkv = () => ({
  MMKV: jest.fn(),
  createMMKV: mockCreateMMKV.mockImplementation(() => createMockMMKVInstance()),
});

const mockGetPendingTracker = () => ({
  PendingMessagesTracker: {
    hasPendingMessagesBefore: mockHasPendingMessagesBefore,
  },
});

// Initial mocks
jest.mock('react-native-mmkv', () => mockGetMmkv());
jest.mock('../PendingMessagesTracker', () => mockGetPendingTracker());

// Cut heavy transitive deps (the handler only needs sentryLogs from @shared/utils,
// and sortMessagesByTsHelper from ../../utils — neither is exercised here).
jest.mock('@shared/utils', () => ({
  sentryLogs: {
    error: jest.fn(),
    warn: jest.fn(),
    info: jest.fn(),
    debug: jest.fn(),
  },
}));
jest.mock('../../utils', () => ({
  sortMessagesByTsHelper: (a: {ts: string}, b: {ts: string}) =>
    a.ts.localeCompare(b.ts),
}));

// Helper to generate ULIDs with controlled timestamps
const generateUlid = (timestamp: number) => ulid(timestamp);

// Helper to create fresh instance (resetModules clears mocks, so we reapply them)
const createMessageRangesHandler = () => {
  jest.resetModules();
  jest.mock('react-native-mmkv', () => mockGetMmkv());
  jest.mock('../PendingMessagesTracker', () => mockGetPendingTracker());
  // eslint-disable-next-line @typescript-eslint/no-var-requires
  return require('../MessageRangesHandler').MessageRangesHandler;
};

describe('MessageRangesHandler', () => {
  const USER_ID = 'user-123';
  const CHANNEL_ID = 'channel-1';
  const CHANNEL_ID_2 = 'channel-2';

  let baseTimestamp: number;
  // eslint-disable-next-line @typescript-eslint/no-explicit-any
  let MessageRangesHandler: any;

  beforeEach(() => {
    // Clear storage
    Object.keys(mockMmkvStorage).forEach(key => delete mockMmkvStorage[key]);
    mockCreateMMKV.mockClear();
    mockHasPendingMessagesBefore.mockClear();
    mockHasPendingMessagesBefore.mockReturnValue(false); //We test normal conditions in most scenarios

    // Fresh instance
    MessageRangesHandler = createMessageRangesHandler();
    MessageRangesHandler.setUserId(USER_ID);

    // Base timestamp
    baseTimestamp = Date.now() - 100000;
  });

  describe('Basic Operations', () => {
    it('should return empty array when no ranges exist', () => {
      const ranges = MessageRangesHandler.get(CHANNEL_ID);
      expect(ranges).toEqual([]);
    });

    it('should store and retrieve ranges', () => {
      const ts1 = generateUlid(baseTimestamp);
      const ts2 = generateUlid(baseTimestamp + 10000);

      MessageRangesHandler.addMessagesAndUpdateRanges(
        CHANNEL_ID,
        [{ts: ts1}, {ts: ts2}],
        {isStart: true, isEnd: true},
      );

      const ranges = MessageRangesHandler.get(CHANNEL_ID);
      expect(ranges).toHaveLength(1);
      expect(ranges[0].oldestTs).toBe(ts1);
      expect(ranges[0].newestTs).toBe(ts2);
      expect(ranges[0].isStart).toBe(true);
      expect(ranges[0].isEnd).toBe(true);
    });

    it('should keep ranges separate per conversation', () => {
      const ts1 = generateUlid(baseTimestamp);
      const ts2 = generateUlid(baseTimestamp + 10000);

      MessageRangesHandler.addMessagesAndUpdateRanges(CHANNEL_ID, [{ts: ts1}], {
        isEnd: true,
      });
      MessageRangesHandler.addMessagesAndUpdateRanges(
        CHANNEL_ID_2,
        [{ts: ts2}],
        {isEnd: true},
      );

      expect(MessageRangesHandler.get(CHANNEL_ID)).toHaveLength(1);
      expect(MessageRangesHandler.get(CHANNEL_ID_2)).toHaveLength(1);
      expect(MessageRangesHandler.get(CHANNEL_ID)[0].oldestTs).toBe(ts1);
      expect(MessageRangesHandler.get(CHANNEL_ID_2)[0].oldestTs).toBe(ts2);
    });
  });

  describe('findRangeForTs', () => {
    it('should find range containing a timestamp', () => {
      const ts1 = generateUlid(baseTimestamp);
      const ts2 = generateUlid(baseTimestamp + 10000);
      const tsMiddle = generateUlid(baseTimestamp + 5000);

      MessageRangesHandler.addMessagesAndUpdateRanges(
        CHANNEL_ID,
        [{ts: ts1}, {ts: ts2}],
        {isEnd: true},
      );

      const range = MessageRangesHandler.findRangeForTs(CHANNEL_ID, tsMiddle);
      expect(range).toBeDefined();
      expect(range.oldestTs).toBe(ts1);
      expect(range.newestTs).toBe(ts2);
    });

    it('should return undefined for timestamp outside ranges', () => {
      const ts1 = generateUlid(baseTimestamp);
      const ts2 = generateUlid(baseTimestamp + 10000);
      const tsOutside = generateUlid(baseTimestamp + 50000);

      MessageRangesHandler.addMessagesAndUpdateRanges(
        CHANNEL_ID,
        [{ts: ts1}, {ts: ts2}],
        {isEnd: true},
      );

      const range = MessageRangesHandler.findRangeForTs(CHANNEL_ID, tsOutside);
      expect(range).toBeUndefined();
    });

    it('should find correct range when multiple ranges exist', () => {
      const ts1 = generateUlid(baseTimestamp);
      const ts2 = generateUlid(baseTimestamp + 10000);
      const ts3 = generateUlid(baseTimestamp + 50000);
      const ts4 = generateUlid(baseTimestamp + 60000);

      // Create first range
      MessageRangesHandler.addMessagesAndUpdateRanges(
        CHANNEL_ID,
        [{ts: ts1}, {ts: ts2}],
        {isStart: true},
      );

      // Create second range (gap between)
      MessageRangesHandler.addMessagesAndUpdateRanges(
        CHANNEL_ID,
        [{ts: ts3}, {ts: ts4}],
        {isEnd: true},
      );

      const ranges = MessageRangesHandler.get(CHANNEL_ID);
      expect(ranges).toHaveLength(2);

      // Find in first range
      const range1 = MessageRangesHandler.findRangeForTs(CHANNEL_ID, ts1);
      expect(range1?.isStart).toBe(true);
      expect(range1?.isEnd).toBe(false);

      // Find in second range
      const range2 = MessageRangesHandler.findRangeForTs(CHANNEL_ID, ts4);
      expect(range2?.isEnd).toBe(true);
      expect(range2?.isStart).toBe(false);

      // Gap should return undefined
      const gapTs = generateUlid(baseTimestamp + 30000);
      const rangeGap = MessageRangesHandler.findRangeForTs(CHANNEL_ID, gapTs);
      expect(rangeGap).toBeUndefined();
    });
  });

  describe('isInLoadedRange', () => {
    it('should return true for timestamp in range', () => {
      const ts1 = generateUlid(baseTimestamp);
      const ts2 = generateUlid(baseTimestamp + 10000);

      MessageRangesHandler.addMessagesAndUpdateRanges(
        CHANNEL_ID,
        [{ts: ts1}, {ts: ts2}],
        {isEnd: true},
      );

      expect(MessageRangesHandler.isInLoadedRange(CHANNEL_ID, ts1)).toBe(true);
      expect(MessageRangesHandler.isInLoadedRange(CHANNEL_ID, ts2)).toBe(true);
    });

    it('should return false for timestamp outside range', () => {
      const ts1 = generateUlid(baseTimestamp);
      const ts2 = generateUlid(baseTimestamp + 10000);
      const tsOutside = generateUlid(baseTimestamp + 50000);

      MessageRangesHandler.addMessagesAndUpdateRanges(
        CHANNEL_ID,
        [{ts: ts1}, {ts: ts2}],
        {isEnd: true},
      );

      expect(MessageRangesHandler.isInLoadedRange(CHANNEL_ID, tsOutside)).toBe(
        false,
      );
    });
  });

  describe('Range Consolidation', () => {
    it('should merge overlapping ranges', () => {
      const ts1 = generateUlid(baseTimestamp);
      const ts2 = generateUlid(baseTimestamp + 10000);
      const ts3 = generateUlid(baseTimestamp + 5000); // Overlaps with first
      const ts4 = generateUlid(baseTimestamp + 15000);

      // First range: ts1 to ts2
      MessageRangesHandler.addMessagesAndUpdateRanges(
        CHANNEL_ID,
        [{ts: ts1}, {ts: ts2}],
        {isStart: true},
      );

      // Second range: ts3 to ts4 (overlaps)
      MessageRangesHandler.addMessagesAndUpdateRanges(
        CHANNEL_ID,
        [{ts: ts3}, {ts: ts4}],
        {isEnd: true},
      );

      const ranges = MessageRangesHandler.get(CHANNEL_ID);
      expect(ranges).toHaveLength(1);
      expect(ranges[0].oldestTs).toBe(ts1);
      expect(ranges[0].newestTs).toBe(ts4);
      expect(ranges[0].isStart).toBe(true);
      expect(ranges[0].isEnd).toBe(true);
    });

    it('should merge adjacent ranges', () => {
      const ts1 = generateUlid(baseTimestamp);
      const ts2 = generateUlid(baseTimestamp + 10000);
      const ts3 = generateUlid(baseTimestamp + 20000);

      // First range: ts1 to ts2
      MessageRangesHandler.addMessagesAndUpdateRanges(
        CHANNEL_ID,
        [{ts: ts1}, {ts: ts2}],
        {isStart: true},
      );

      // Second range: ts2 (same as first range's newest) to ts3
      // Using bridgeOldestTs to connect at ts2
      MessageRangesHandler.addMessagesAndUpdateRanges(CHANNEL_ID, [{ts: ts3}], {
        isEnd: true,
        bridgeOldestTs: ts2,
      });

      const ranges = MessageRangesHandler.get(CHANNEL_ID);
      expect(ranges).toHaveLength(1);
      expect(ranges[0].oldestTs).toBe(ts1);
      expect(ranges[0].newestTs).toBe(ts3);
      expect(ranges[0].isStart).toBe(true);
      expect(ranges[0].isEnd).toBe(true);
    });

    it('should not merge non-overlapping ranges', () => {
      const ts1 = generateUlid(baseTimestamp);
      const ts2 = generateUlid(baseTimestamp + 10000);
      const ts3 = generateUlid(baseTimestamp + 50000); // Gap
      const ts4 = generateUlid(baseTimestamp + 60000);

      MessageRangesHandler.addMessagesAndUpdateRanges(
        CHANNEL_ID,
        [{ts: ts1}, {ts: ts2}],
        {isStart: true},
      );

      MessageRangesHandler.addMessagesAndUpdateRanges(
        CHANNEL_ID,
        [{ts: ts3}, {ts: ts4}],
        {isEnd: true},
      );

      const ranges = MessageRangesHandler.get(CHANNEL_ID);
      expect(ranges).toHaveLength(2);
    });
  });

  describe('Bridge Functionality', () => {
    it('should use bridgeNewestTs to connect with existing range (loadAbove)', () => {
      const anchorTs = generateUlid(baseTimestamp + 50000);
      const ts1 = generateUlid(baseTimestamp);
      const ts2 = generateUlid(baseTimestamp + 10000);

      // Messages loaded above, using anchorTs as bridge
      MessageRangesHandler.addMessagesAndUpdateRanges(
        CHANNEL_ID,
        [{ts: ts1}, {ts: ts2}],
        {
          isStart: true,
          bridgeNewestTs: anchorTs,
        },
      );

      const ranges = MessageRangesHandler.get(CHANNEL_ID);
      expect(ranges).toHaveLength(1);
      // newestTs should be bridged to anchorTs
      expect(ranges[0].newestTs).toBe(anchorTs);
      expect(ranges[0].oldestTs).toBe(ts1);
    });

    it('should use bridgeOldestTs to connect with existing range (loadBelow)', () => {
      const anchorTs = generateUlid(baseTimestamp);
      const ts1 = generateUlid(baseTimestamp + 50000);
      const ts2 = generateUlid(baseTimestamp + 60000);

      // Messages loaded below, using anchorTs as bridge
      MessageRangesHandler.addMessagesAndUpdateRanges(
        CHANNEL_ID,
        [{ts: ts1}, {ts: ts2}],
        {
          isEnd: true,
          bridgeOldestTs: anchorTs,
        },
      );

      const ranges = MessageRangesHandler.get(CHANNEL_ID);
      expect(ranges).toHaveLength(1);
      // oldestTs should be bridged to anchorTs
      expect(ranges[0].oldestTs).toBe(anchorTs);
      expect(ranges[0].newestTs).toBe(ts2);
    });

    it('should not use bridgeNewestTs if messages are newer than bridge', () => {
      const anchorTs = generateUlid(baseTimestamp + 50000);
      const ts1 = generateUlid(baseTimestamp + 60000); // Newer than anchor
      const ts2 = generateUlid(baseTimestamp + 70000);

      // bridgeNewestTs is older than messages, should not be used
      MessageRangesHandler.addMessagesAndUpdateRanges(
        CHANNEL_ID,
        [{ts: ts1}, {ts: ts2}],
        {
          isEnd: true,
          bridgeNewestTs: anchorTs, // This is older, won't be used
        },
      );

      const ranges = MessageRangesHandler.get(CHANNEL_ID);
      expect(ranges[0].newestTs).toBe(ts2); // Not anchorTs
    });

    it('should not use bridgeOldestTs if messages are older than bridge', () => {
      const anchorTs = generateUlid(baseTimestamp + 50000);
      const ts1 = generateUlid(baseTimestamp); // Older than anchor
      const ts2 = generateUlid(baseTimestamp + 10000);

      // bridgeOldestTs is newer than messages, should not be used
      MessageRangesHandler.addMessagesAndUpdateRanges(
        CHANNEL_ID,
        [{ts: ts1}, {ts: ts2}],
        {
          isStart: true,
          bridgeOldestTs: anchorTs, // This is newer, won't be used
        },
      );

      const ranges = MessageRangesHandler.get(CHANNEL_ID);
      expect(ranges[0].oldestTs).toBe(ts1); // Not anchorTs
    });

    it('should consolidate ranges using bridgeNewestTs (loadAbove connecting to existing)', () => {
      // Existing range at live edge
      const existingTs1 = generateUlid(baseTimestamp + 50000);
      const existingTs2 = generateUlid(baseTimestamp + 60000);

      MessageRangesHandler.addMessagesAndUpdateRanges(
        CHANNEL_ID,
        [{ts: existingTs1}, {ts: existingTs2}],
        {isEnd: true},
      );

      // Now load above and bridge to the existing range
      const olderTs1 = generateUlid(baseTimestamp);
      const olderTs2 = generateUlid(baseTimestamp + 10000);

      MessageRangesHandler.addMessagesAndUpdateRanges(
        CHANNEL_ID,
        [{ts: olderTs1}, {ts: olderTs2}],
        {
          isStart: true,
          bridgeNewestTs: existingTs1, // Connect to existing range
        },
      );

      // Ranges should be consolidated into one
      const ranges = MessageRangesHandler.get(CHANNEL_ID);
      expect(ranges).toHaveLength(1);
      expect(ranges[0].oldestTs).toBe(olderTs1);
      expect(ranges[0].newestTs).toBe(existingTs2);
      expect(ranges[0].isStart).toBe(true);
      expect(ranges[0].isEnd).toBe(true);
    });

    it('should consolidate ranges using bridgeOldestTs (loadBelow connecting to existing)', () => {
      // Existing range at conversation start
      const existingTs1 = generateUlid(baseTimestamp);
      const existingTs2 = generateUlid(baseTimestamp + 10000);

      MessageRangesHandler.addMessagesAndUpdateRanges(
        CHANNEL_ID,
        [{ts: existingTs1}, {ts: existingTs2}],
        {isStart: true},
      );

      // Now load below and bridge to the existing range
      const newerTs1 = generateUlid(baseTimestamp + 50000);
      const newerTs2 = generateUlid(baseTimestamp + 60000);

      MessageRangesHandler.addMessagesAndUpdateRanges(
        CHANNEL_ID,
        [{ts: newerTs1}, {ts: newerTs2}],
        {
          isEnd: true,
          bridgeOldestTs: existingTs2, // Connect to existing range
        },
      );

      // Ranges should be consolidated into one
      const ranges = MessageRangesHandler.get(CHANNEL_ID);
      expect(ranges).toHaveLength(1);
      expect(ranges[0].oldestTs).toBe(existingTs1);
      expect(ranges[0].newestTs).toBe(newerTs2);
      expect(ranges[0].isStart).toBe(true);
      expect(ranges[0].isEnd).toBe(true);
    });

    it('should consolidate two disconnected ranges using both bridges', () => {
      // Range 1 (old messages)
      const range1Ts1 = generateUlid(baseTimestamp);
      const range1Ts2 = generateUlid(baseTimestamp + 10000);

      MessageRangesHandler.addMessagesAndUpdateRanges(
        CHANNEL_ID,
        [{ts: range1Ts1}, {ts: range1Ts2}],
        {isStart: true},
      );

      // Range 2 (new messages, with gap)
      const range2Ts1 = generateUlid(baseTimestamp + 50000);
      const range2Ts2 = generateUlid(baseTimestamp + 60000);

      MessageRangesHandler.addMessagesAndUpdateRanges(
        CHANNEL_ID,
        [{ts: range2Ts1}, {ts: range2Ts2}],
        {isEnd: true},
      );

      // Should have 2 disconnected ranges
      expect(MessageRangesHandler.get(CHANNEL_ID)).toHaveLength(2);

      // Fill the gap with bridges on both ends
      const gapTs1 = generateUlid(baseTimestamp + 20000);
      const gapTs2 = generateUlid(baseTimestamp + 30000);

      MessageRangesHandler.addMessagesAndUpdateRanges(
        CHANNEL_ID,
        [{ts: gapTs1}, {ts: gapTs2}],
        {
          bridgeOldestTs: range1Ts2, // Connect to range 1
          bridgeNewestTs: range2Ts1, // Connect to range 2
        },
      );

      // All ranges should be consolidated into one
      const ranges = MessageRangesHandler.get(CHANNEL_ID);
      expect(ranges).toHaveLength(1);
      expect(ranges[0].oldestTs).toBe(range1Ts1);
      expect(ranges[0].newestTs).toBe(range2Ts2);
      expect(ranges[0].isStart).toBe(true);
      expect(ranges[0].isEnd).toBe(true);
    });
  });

  describe('Pending Messages Check', () => {
    it('should block range update when pending messages exist before', () => {
      mockHasPendingMessagesBefore.mockReturnValue(true);

      const ts1 = generateUlid(baseTimestamp);

      MessageRangesHandler.addMessagesAndUpdateRanges(CHANNEL_ID, [{ts: ts1}], {
        isEnd: true,
      });

      // Should not have created a range
      expect(MessageRangesHandler.get(CHANNEL_ID)).toHaveLength(0);
    });

    it('should allow range update when skipPendingCheck is true', () => {
      mockHasPendingMessagesBefore.mockReturnValue(true);

      const ts1 = generateUlid(baseTimestamp);

      MessageRangesHandler.addMessagesAndUpdateRanges(CHANNEL_ID, [{ts: ts1}], {
        isEnd: true,
        skipPendingCheck: true,
      });

      // Should have created a range despite pending messages
      expect(MessageRangesHandler.get(CHANNEL_ID)).toHaveLength(1);
    });
  });

  describe('isEnd Flag Management', () => {
    it('should ensure only one range has isEnd: true', () => {
      const ts1 = generateUlid(baseTimestamp);
      const ts2 = generateUlid(baseTimestamp + 10000);
      const ts3 = generateUlid(baseTimestamp + 50000);
      const ts4 = generateUlid(baseTimestamp + 60000);

      // Create first range with isEnd
      MessageRangesHandler.addMessagesAndUpdateRanges(
        CHANNEL_ID,
        [{ts: ts1}, {ts: ts2}],
        {isStart: true, isEnd: true},
      );

      // Create second range with isEnd (gap between)
      MessageRangesHandler.addMessagesAndUpdateRanges(
        CHANNEL_ID,
        [{ts: ts3}, {ts: ts4}],
        {isEnd: true},
      );

      const ranges = MessageRangesHandler.get(CHANNEL_ID);
      expect(ranges).toHaveLength(2);

      // Only the range with highest newestTs should have isEnd
      const rangesWithIsEnd = ranges.filter((r: {isEnd: boolean}) => r.isEnd);
      expect(rangesWithIsEnd).toHaveLength(1);
      expect(rangesWithIsEnd[0].newestTs).toBe(ts4);
    });

    it('should ensure only one range has isStart: true', () => {
      const ts1 = generateUlid(baseTimestamp);
      const ts2 = generateUlid(baseTimestamp + 10000);
      const ts3 = generateUlid(baseTimestamp + 50000);
      const ts4 = generateUlid(baseTimestamp + 60000);

      // Create first range with isStart
      MessageRangesHandler.addMessagesAndUpdateRanges(
        CHANNEL_ID,
        [{ts: ts1}, {ts: ts2}],
        {isStart: true},
      );

      // Create second range with isStart (gap between)
      MessageRangesHandler.addMessagesAndUpdateRanges(
        CHANNEL_ID,
        [{ts: ts3}, {ts: ts4}],
        {isStart: true, isEnd: true},
      );

      const ranges = MessageRangesHandler.get(CHANNEL_ID);
      expect(ranges).toHaveLength(2);

      // Only the range with lowest oldestTs should have isStart
      const rangesWithIsStart = ranges.filter(
        (r: {isStart: boolean}) => r.isStart,
      );
      expect(rangesWithIsStart).toHaveLength(1);
      expect(rangesWithIsStart[0].oldestTs).toBe(ts1);
    });
  });

  describe('markAsStart and markAsEnd', () => {
    it('should mark range as start when single range exists', () => {
      const ts1 = generateUlid(baseTimestamp);
      const ts2 = generateUlid(baseTimestamp + 10000);

      MessageRangesHandler.addMessagesAndUpdateRanges(
        CHANNEL_ID,
        [{ts: ts1}, {ts: ts2}],
        {isEnd: true},
      );

      const ranges = MessageRangesHandler.get(CHANNEL_ID);
      expect(ranges).toHaveLength(1);
      expect(ranges[0].isStart).toBe(false);

      MessageRangesHandler.markAsStart(CHANNEL_ID, ts1);

      const updatedRanges = MessageRangesHandler.get(CHANNEL_ID);
      expect(updatedRanges).toHaveLength(1);
      expect(updatedRanges[0].isStart).toBe(true);
    });

    it('should mark correct range as start when multiple ranges exist', () => {
      const ts1 = generateUlid(baseTimestamp);
      const ts2 = generateUlid(baseTimestamp + 10000);
      const ts3 = generateUlid(baseTimestamp + 50000);
      const ts4 = generateUlid(baseTimestamp + 60000);

      // Create two disconnected ranges
      MessageRangesHandler.addMessagesAndUpdateRanges(
        CHANNEL_ID,
        [{ts: ts1}, {ts: ts2}],
        {},
      );
      MessageRangesHandler.addMessagesAndUpdateRanges(
        CHANNEL_ID,
        [{ts: ts3}, {ts: ts4}],
        {isEnd: true},
      );

      const ranges = MessageRangesHandler.get(CHANNEL_ID);
      expect(ranges).toHaveLength(2);

      // Mark the oldest range as start
      MessageRangesHandler.markAsStart(CHANNEL_ID, ts1);

      const updatedRanges = MessageRangesHandler.get(CHANNEL_ID);
      // Only the oldest range should have isStart
      const oldestRange = updatedRanges.find(
        (r: {oldestTs: string}) => r.oldestTs === ts1,
      );
      const newestRange = updatedRanges.find(
        (r: {oldestTs: string}) => r.oldestTs === ts3,
      );
      expect(oldestRange?.isStart).toBe(true);
      expect(newestRange?.isStart).toBe(false);
    });

    it('should mark range as end', () => {
      const ts1 = generateUlid(baseTimestamp);
      const ts2 = generateUlid(baseTimestamp + 10000);

      MessageRangesHandler.addMessagesAndUpdateRanges(
        CHANNEL_ID,
        [{ts: ts1}, {ts: ts2}],
        {isStart: true},
      );

      expect(MessageRangesHandler.get(CHANNEL_ID)[0].isEnd).toBe(false);

      MessageRangesHandler.markAsEnd(CHANNEL_ID, ts2);

      expect(MessageRangesHandler.get(CHANNEL_ID)[0].isEnd).toBe(true);
    });
  });

  describe('isAtConversationStart', () => {
    it('should return true when range is marked as start', () => {
      const ts1 = generateUlid(baseTimestamp);
      const ts2 = generateUlid(baseTimestamp + 10000);

      MessageRangesHandler.addMessagesAndUpdateRanges(
        CHANNEL_ID,
        [{ts: ts1}, {ts: ts2}],
        {isStart: true, isEnd: true},
      );

      expect(MessageRangesHandler.isAtConversationStart(CHANNEL_ID, ts1)).toBe(
        true,
      );
    });

    it('should return false when range is not marked as start', () => {
      const ts1 = generateUlid(baseTimestamp);
      const ts2 = generateUlid(baseTimestamp + 10000);

      MessageRangesHandler.addMessagesAndUpdateRanges(
        CHANNEL_ID,
        [{ts: ts1}, {ts: ts2}],
        {isEnd: true},
      );

      expect(MessageRangesHandler.isAtConversationStart(CHANNEL_ID, ts1)).toBe(
        false,
      );
    });
  });

  describe('User Switching', () => {
    it('should clear cache when user changes', () => {
      const ts1 = generateUlid(baseTimestamp);

      MessageRangesHandler.addMessagesAndUpdateRanges(CHANNEL_ID, [{ts: ts1}], {
        isEnd: true,
      });

      expect(MessageRangesHandler.get(CHANNEL_ID)).toHaveLength(1);

      // Switch user
      MessageRangesHandler.setUserId('different-user');

      // Cache should be cleared, different storage key
      expect(MessageRangesHandler.get(CHANNEL_ID)).toHaveLength(0);
    });
  });

  describe('Edge Cases', () => {
    it('should handle empty messages array', () => {
      MessageRangesHandler.addMessagesAndUpdateRanges(CHANNEL_ID, [], {
        isEnd: true,
      });

      expect(MessageRangesHandler.get(CHANNEL_ID)).toHaveLength(0);
    });

    it('should handle single message', () => {
      const ts = generateUlid(baseTimestamp);

      MessageRangesHandler.addMessagesAndUpdateRanges(CHANNEL_ID, [{ts}], {
        isStart: true,
        isEnd: true,
      });

      const ranges = MessageRangesHandler.get(CHANNEL_ID);
      expect(ranges).toHaveLength(1);
      expect(ranges[0].oldestTs).toBe(ts);
      expect(ranges[0].newestTs).toBe(ts);
    });

    it('should handle unsorted messages', () => {
      const ts1 = generateUlid(baseTimestamp);
      const ts2 = generateUlid(baseTimestamp + 20000);
      const ts3 = generateUlid(baseTimestamp + 10000);

      // Pass in unsorted order
      MessageRangesHandler.addMessagesAndUpdateRanges(
        CHANNEL_ID,
        [{ts: ts2}, {ts: ts1}, {ts: ts3}],
        {isEnd: true},
      );

      const ranges = MessageRangesHandler.get(CHANNEL_ID);
      expect(ranges).toHaveLength(1);
      expect(ranges[0].oldestTs).toBe(ts1);
      expect(ranges[0].newestTs).toBe(ts2);
    });
  });

  describe('Pagination Scenarios', () => {
    describe('loadAbove (scrolling up for older messages)', () => {
      it('should extend range when loading older messages', () => {
        // Initial load
        const ts1 = generateUlid(baseTimestamp + 50000);
        const ts2 = generateUlid(baseTimestamp + 60000);

        MessageRangesHandler.addMessagesAndUpdateRanges(
          CHANNEL_ID,
          [{ts: ts1}, {ts: ts2}],
          {isEnd: true},
        );

        // Load above (older messages)
        const ts3 = generateUlid(baseTimestamp + 30000);
        const ts4 = generateUlid(baseTimestamp + 40000);

        MessageRangesHandler.addMessagesAndUpdateRanges(
          CHANNEL_ID,
          [{ts: ts3}, {ts: ts4}],
          {
            bridgeNewestTs: ts1, // Connect to existing range
          },
        );

        const ranges = MessageRangesHandler.get(CHANNEL_ID);
        expect(ranges).toHaveLength(1);
        expect(ranges[0].oldestTs).toBe(ts3);
        expect(ranges[0].newestTs).toBe(ts2);
      });

      it('should mark isStart when reaching conversation beginning', () => {
        const ts1 = generateUlid(baseTimestamp);
        const ts2 = generateUlid(baseTimestamp + 10000);

        MessageRangesHandler.addMessagesAndUpdateRanges(
          CHANNEL_ID,
          [{ts: ts1}, {ts: ts2}],
          {
            isStart: true, // Server said no more messages
            isEnd: true,
          },
        );

        expect(
          MessageRangesHandler.isAtConversationStart(CHANNEL_ID, ts1),
        ).toBe(true);
      });
    });

    describe('loadBelow (scrolling down for newer messages)', () => {
      it('should extend range when loading newer messages', () => {
        // Initial load
        const ts1 = generateUlid(baseTimestamp);
        const ts2 = generateUlid(baseTimestamp + 10000);

        MessageRangesHandler.addMessagesAndUpdateRanges(
          CHANNEL_ID,
          [{ts: ts1}, {ts: ts2}],
          {isStart: true},
        );

        // Load below (newer messages)
        const ts3 = generateUlid(baseTimestamp + 20000);
        const ts4 = generateUlid(baseTimestamp + 30000);

        MessageRangesHandler.addMessagesAndUpdateRanges(
          CHANNEL_ID,
          [{ts: ts3}, {ts: ts4}],
          {
            bridgeOldestTs: ts2, // Connect to existing range
            isEnd: true,
          },
        );

        const ranges = MessageRangesHandler.get(CHANNEL_ID);
        expect(ranges).toHaveLength(1);
        expect(ranges[0].oldestTs).toBe(ts1);
        expect(ranges[0].newestTs).toBe(ts4);
        expect(ranges[0].isEnd).toBe(true);
      });
    });

    describe('Jump to reply (loadAround)', () => {
      it('should create new disconnected range when jumping to far message', () => {
        // Live edge range
        const ts1 = generateUlid(baseTimestamp + 90000);
        const ts2 = generateUlid(baseTimestamp + 100000);

        MessageRangesHandler.addMessagesAndUpdateRanges(
          CHANNEL_ID,
          [{ts: ts1}, {ts: ts2}],
          {isEnd: true},
        );

        // Jump to old reply (creates gap)
        const ts3 = generateUlid(baseTimestamp);
        const ts4 = generateUlid(baseTimestamp + 10000);

        MessageRangesHandler.addMessagesAndUpdateRanges(
          CHANNEL_ID,
          [{ts: ts3}, {ts: ts4}],
          {isStart: true},
        );

        const ranges = MessageRangesHandler.get(CHANNEL_ID);
        expect(ranges).toHaveLength(2);

        // First range (old reply)
        expect(ranges[0].isStart).toBe(true);
        expect(ranges[0].oldestTs).toBe(ts3);

        // Second range (live edge)
        expect(ranges[1].isEnd).toBe(true);
        expect(ranges[1].newestTs).toBe(ts2);
      });

      it('should merge ranges when they eventually connect', () => {
        // First range (old)
        const ts1 = generateUlid(baseTimestamp);
        const ts2 = generateUlid(baseTimestamp + 10000);

        MessageRangesHandler.addMessagesAndUpdateRanges(
          CHANNEL_ID,
          [{ts: ts1}, {ts: ts2}],
          {isStart: true},
        );

        // Second range (new, with gap)
        const ts5 = generateUlid(baseTimestamp + 40000);
        const ts6 = generateUlid(baseTimestamp + 50000);

        MessageRangesHandler.addMessagesAndUpdateRanges(
          CHANNEL_ID,
          [{ts: ts5}, {ts: ts6}],
          {isEnd: true},
        );

        expect(MessageRangesHandler.get(CHANNEL_ID)).toHaveLength(2);

        // Fill the gap
        const ts3 = generateUlid(baseTimestamp + 20000);
        const ts4 = generateUlid(baseTimestamp + 30000);

        MessageRangesHandler.addMessagesAndUpdateRanges(
          CHANNEL_ID,
          [{ts: ts3}, {ts: ts4}],
          {
            bridgeOldestTs: ts2,
            bridgeNewestTs: ts5,
          },
        );

        const ranges = MessageRangesHandler.get(CHANNEL_ID);
        expect(ranges).toHaveLength(1);
        expect(ranges[0].oldestTs).toBe(ts1);
        expect(ranges[0].newestTs).toBe(ts6);
        expect(ranges[0].isStart).toBe(true);
        expect(ranges[0].isEnd).toBe(true);
      });
    });
  });
});
