import { sessionService } from '../app/services';
import Session from '../app/types/session'
import { beforeEach, describe, expect, it, jest } from '@jest/globals';
import SessionStore from '../app/mstore/sessionStore';
import { searchStore } from '../app/mstore/index';
import { checkEventWithFilters } from '../app/components/Session_/Player/Controls/checkEventWithFilters';
import { mockSession } from './mocks/sessionData';

jest.mock('../app/player', () => ({
  createWebPlayer: jest.fn(),
  createIOSPlayer: jest.fn(),
  createClickMapPlayer: jest.fn(),
  createLiveWebPlayer: jest.fn(),
  createClipPlayer: jest.fn()
}));

jest.mock('../app/services', () => ({
  sessionService: {
    getSessions: jest.fn(),
    getLiveSessions: jest.fn(),
    getSessionInfo: jest.fn(),
    getSessionEvents: jest.fn(),
    getSessionNotes: jest.fn(),
    getFavoriteSessions: jest.fn(),
    getSessionClickMap: jest.fn(),
    toggleFavorite: jest.fn(),
    getClickMap: jest.fn(),
    getAutoplayList: jest.fn(),
    getFirstMobUrl: jest.fn(),
  },
}));

jest.mock('App/player/web/network/loadFiles', () => ({
  loadFile: jest.fn(),
}));

jest.mock(
  '@/components/Session_/Player/Controls/checkEventWithFilters',
  () => ({
    checkEventWithFilters: jest.fn(),
  }),
);

jest.mock('../app/mstore/index', () => ({
  searchStore: {
    instance: {
      filters: [],
      events: [],
      toSearch: jest.fn().mockReturnValue({}),
    },
  },
  searchStoreLive: {
    instance: {
      filters: [],
      events: [],
    },
  },
}));

describe('SessionStore', () => {
  let sessionStore: SessionStore;

  beforeEach(() => {
    jest.clearAllMocks();
    sessionStore = new SessionStore();
  });

  describe('resetUserFilter', () => {
    it('should reset user filter', () => {
      sessionStore.userFilter.update('page', 5);
      expect(sessionStore.userFilter.page).toBe(5);

      sessionStore.resetUserFilter();
      expect(sessionStore.userFilter.page).toBe(1);
    });
  });

  describe('fetchLiveSessions', () => {
    it('should fetch and set live sessions', async () => {
      const mockResponse = {
        sessions: [{ sessionId: 'live-1', userId: 'user1' }],
        total: 1,
      };

      (sessionService.getLiveSessions as jest.Mock).mockResolvedValue(
        mockResponse,
      );

      await sessionStore.fetchLiveSessions({ sort: 'timestamp' });

      expect(sessionService.getLiveSessions).toHaveBeenCalledWith({
        sort: 'timestamp',
      });
      expect(sessionStore.liveSessions.length).toBe(1);
      expect(sessionStore.liveSessions[0].sessionId).toBe('live-1');
      expect(sessionStore.liveSessions[0].live).toBe(true);
      expect(sessionStore.totalLiveSessions).toBe(1);
      expect(sessionStore.loadingLiveSessions).toBe(false);
    });

    it('should handle duration sort by converting to timestamp', async () => {
      (sessionService.getLiveSessions as jest.Mock).mockResolvedValue({
        sessions: [],
        total: 0,
      });

      await sessionStore.fetchLiveSessions({ sort: 'duration', order: 'asc' });

      expect(sessionService.getLiveSessions).toHaveBeenCalledWith({
        sort: 'timestamp',
        order: 'desc',
      });
    });

    it('should handle errors and set loading to false', async () => {
      const mockError = new Error('API error');
      console.error = jest.fn();

      (sessionService.getLiveSessions as jest.Mock).mockRejectedValue(
        mockError,
      );

      await sessionStore.fetchLiveSessions();

      expect(console.error).toHaveBeenCalledWith(mockError);
      expect(sessionStore.loadingLiveSessions).toBe(false);
    });
  });

  describe('fetchSessions', () => {
    it('should fetch and set sessions', async () => {
      const mockResponse = {
        sessions: [
          new Session(mockSession({ sessionId: '1', favorite: true })),
          new Session(mockSession({ sessionId: '2' })),
        ],
        total: 2,
      };

      (sessionService.getSessions as jest.Mock).mockResolvedValue(mockResponse);

      await sessionStore.fetchSessions({ page: 1, filters: [] }, true);

      expect(sessionService.getSessions).toHaveBeenCalledWith({
        page: 1,
        filters: [],
      });
      expect(sessionStore.list.length).toBe(2);
      expect(sessionStore.total).toBe(2);
      expect(sessionStore.sessionIds).toEqual(['1', '2']);
      expect(sessionStore.favoriteList.length).toBe(1);
      expect(sessionStore.favoriteList[0].sessionId).toBe('1');
      expect(sessionStore.loadingSessions).toBe(false);
    });

    it('should handle errors and set loading to false', async () => {
      const mockError = new Error('API error');
      console.error = jest.fn();

      (sessionService.getSessions as jest.Mock).mockRejectedValue(mockError);

      await sessionStore.fetchSessions({ filters: [] }, true);

      expect(console.error).toHaveBeenCalledWith(mockError);
      expect(sessionStore.loadingSessions).toBe(false);
    });
  });

  describe('clearAll', () => {
    it('should clear session list and current session', () => {
      sessionStore.list = [new Session({ sessionId: 'test' })];
      sessionStore.current = new Session({ sessionId: 'current' });

      sessionStore.clearAll();

      expect(sessionStore.list).toEqual([]);
      expect(sessionStore.current.sessionId).toBe('');
    });
  });

  describe('fetchSessionData', () => {
    it('should fetch and set session data with events', async () => {
      const mockSessionId = 'test-session-id';
      const mockSessionInfo = { sessionId: mockSessionId, userId: 'user1' };
      const mockEventsData = {
        events: [
          { type: 'LOCATION', url: 'https://example.com', time: 100 },
          { type: 'CLICK', value: 'button', time: 200 },
        ],
        errors: [],
        crashes: [],
        issues: [],
        resources: [],
        stackEvents: [],
        userEvents: [],
        userTesting: [],
      };

      (sessionService.getSessionInfo as jest.Mock).mockResolvedValue(
        mockSessionInfo,
      );
      (sessionService.getSessionEvents as jest.Mock).mockResolvedValue(
        mockEventsData,
      );
      (checkEventWithFilters as jest.Mock).mockReturnValue(false);

      await sessionStore.fetchSessionData(mockSessionId);

      expect(sessionService.getSessionInfo).toHaveBeenCalledWith(
        mockSessionId,
        false,
      );
      expect(sessionService.getSessionEvents).toHaveBeenCalledWith(
        mockSessionId,
      );
      expect(sessionStore.current.sessionId).toBe(mockSessionId);
      expect(sessionStore.visitedEvents.length).toBe(1);
      expect(sessionStore.visitedEvents[0].url).toBe('https://example.com');
      expect(sessionStore.host).toBe('');
      expect(sessionStore.prefetched).toBe(false);
    });

    it('should handle event filtering based on search filters', async () => {
      const mockSessionId = 'test-session-id';
      const mockSessionInfo = { sessionId: mockSessionId, userId: 'user1' };
      const mockEventsData = {
        events: [
          { type: 'LOCATION', url: 'https://example.com', time: 100 },
          { type: 'CLICK', value: 'button', time: 200 },
        ],
      };

      // Setup search filters
      (searchStore.instance.events as any) = [
        { key: 'LOCATION', operator: 'is', value: 'https://example.com' },
      ];

      (sessionService.getSessionInfo as jest.Mock).mockResolvedValue(
        mockSessionInfo,
      );
      (sessionService.getSessionEvents as jest.Mock).mockResolvedValue(
        mockEventsData,
      );
      (checkEventWithFilters as jest.Mock).mockReturnValue(true);

      await sessionStore.fetchSessionData(mockSessionId);

      expect(sessionStore.eventsIndex).toEqual([0]);
    });

    it('should handle different filter operators', async () => {
      const mockSessionId = 'test-session-id';
      const mockSessionInfo = { sessionId: mockSessionId, userId: 'user1' };
      const mockEventsData = {
        events: [
          { type: 'LOCATION', url: 'https://example.com', time: 100 },
          { type: 'CLICK', value: 'test-button', time: 200 },
        ],
      };

      (searchStore.instance.events as any) = [
        { key: 'CLICK', operator: 'contains', value: 'test' },
      ];

      (sessionService.getSessionInfo as jest.Mock).mockResolvedValue(
        mockSessionInfo,
      );
      (sessionService.getSessionEvents as jest.Mock).mockResolvedValue(
        mockEventsData,
      );
      (checkEventWithFilters as jest.Mock).mockReturnValue(true);

      await sessionStore.fetchSessionData(mockSessionId);

      expect(sessionStore.eventsIndex).toEqual([1]);
    });

    it('should handle errors when fetching events', async () => {
      const mockSessionId = 'test-session-id';
      const mockSessionInfo = { sessionId: mockSessionId, userId: 'user1' };
      const mockError = new Error('API error');
      console.error = jest.fn();

      (sessionService.getSessionInfo as jest.Mock).mockResolvedValue(
        mockSessionInfo,
      );
      (sessionService.getSessionEvents as jest.Mock).mockRejectedValue(
        mockError,
      );

      await sessionStore.fetchSessionData(mockSessionId);

      expect(console.error).toHaveBeenCalledWith(
        'Failed to fetch events',
        mockError,
      );
      expect(sessionStore.current.sessionId).toBe(mockSessionId);
      expect(sessionStore.current.events).toEqual([]);
    });

    it('should handle errors when fetching session info', async () => {
      const mockSessionId = 'test-session-id';
      const mockError = new Error('API error');
      console.error = jest.fn();

      (sessionService.getSessionInfo as jest.Mock).mockRejectedValue(mockError);

      await sessionStore.fetchSessionData(mockSessionId);

      expect(console.error).toHaveBeenCalledWith(mockError);
      expect(sessionStore.fetchFailed).toBe(true);
    });
  });

  describe('sortSessions', () => {
    it('should sort sessions by the specified key in ascending order', () => {
      sessionStore.list = [
        new Session(mockSession({ duration: 3000, sessionId: '1' })),
        new Session(mockSession({ duration: 1000, sessionId: '2' })),
        new Session(mockSession({ duration: 2000, sessionId: '3' })),
      ];
      sessionStore.favoriteList = [
        new Session(mockSession({ duration: 3000, sessionId: '1' })),
        new Session(mockSession({ duration: 2000, sessionId: '3' })),
      ];

      sessionStore.sortSessions('duration', 1);

      expect(sessionStore.list.map((s) => s.sessionId)).toEqual([
        '2',
        '3',
        '1',
      ]);
      expect(sessionStore.favoriteList.map((s) => s.sessionId)).toEqual([
        '3',
        '1',
      ]);
    });

    it('should sort sessions by the specified key in descending order', () => {
      sessionStore.list = [
        new Session(mockSession({ duration: 3000, sessionId: '1' })),
        new Session(mockSession({ duration: 1000, sessionId: '2' })),
        new Session(mockSession({ duration: 2000, sessionId: '3' })),
      ];

      sessionStore.sortSessions('duration', -1);
      expect(sessionStore.list.map((s) => s.sessionId)).toEqual([
        '1',
        '3',
        '2',
      ]);
    });
  });
});
