import { IngressPointService } from '../services/IngressPointService';
import { JupyterFrontEnd, LabShell } from '@jupyterlab/application';
import { Panel } from '@lumino/widgets';
import { TOGGLE_AI_CHAT_MESSAGE } from '../constants';

// Mock the getSMEnvironmentMetadata function
jest.mock('../utils', () => ({
  ...jest.requireActual('../utils'),
  getSMEnvironmentMetadata: jest.fn()
}));

import { getSMEnvironmentMetadata } from '../utils';

describe('IngressPointService', () => {
  let service: IngressPointService;
  let mockApp: jest.Mocked<JupyterFrontEnd>;
  let mockChatPanel: jest.Mocked<Panel>;
  let mockShell: jest.Mocked<LabShell>;

  beforeEach(() => {
    jest.clearAllMocks();
    
    // Mock Panel
    mockChatPanel = {
      id: 'test-chat-panel'
    } as jest.Mocked<Panel>;

    // Mock LabShell
    mockShell = {
      widgets: jest.fn(),
      activateById: jest.fn(),
      collapseLeft: jest.fn(),
      collapseRight: jest.fn()
    } as unknown as jest.Mocked<LabShell>;

    // Mock JupyterFrontEnd
    mockApp = {
      shell: mockShell
    } as unknown as jest.Mocked<JupyterFrontEnd>;

    service = new IngressPointService(mockApp, mockChatPanel);
  });

  afterEach(() => {
    window.removeEventListener('message', (service as any).messageListener);
    jest.clearAllMocks();
  });

  describe('initialize', () => {
    it('should add message event listener', async () => {
      const addEventListenerSpy = jest.spyOn(window, 'addEventListener');
      
      await service.initialize();
      
      expect(addEventListenerSpy).toHaveBeenCalledWith('message', expect.any(Function));
    });
    
    it('should initialize origin validation with environment metadata', async () => {
      const mockMetadata = {
        domain_id: 'd-test123',
        aws_region: 'us-east-1',
        dz_stage: 'prod',
        sm_domain_id: 'd-test123',
        project_id: 'p-test',
        environment_id: 'env-test',
        repository_name: 'test-repo',
        user_id: 'test-user',
        sm_space_name: 'test-space',
        sm_user_profile_name: 'test-profile',
        sm_project_path: '/test'
      };
      
      (getSMEnvironmentMetadata as jest.Mock).mockResolvedValue(mockMetadata);
      
      // Create service instance (triggers async initialization)
      new IngressPointService(mockApp, mockChatPanel);
      
      // Wait for async initialization to complete
      await new Promise(resolve => setTimeout(resolve, 100));
      
      // Verify metadata was fetched
      expect(getSMEnvironmentMetadata).toHaveBeenCalled();
    });
  });

  describe('messageListener', () => {
    beforeEach(async () => {
      const mockMetadata = {
        domain_id: 'd-test123',
        aws_region: 'us-east-1',
        dz_stage: 'prod',
        sm_domain_id: 'd-test123',
        project_id: 'p-test',
        environment_id: 'env-test',
        repository_name: 'test-repo',
        user_id: 'test-user',
        sm_space_name: 'test-space',
        sm_user_profile_name: 'test-profile',
        sm_project_path: '/test'
      };
      
      (getSMEnvironmentMetadata as jest.Mock).mockResolvedValue(mockMetadata);
      
      await service.initialize();
      // Wait for async initialization
      await new Promise(resolve => setTimeout(resolve, 100));
    });

    it('should ignore messages from invalid origins', async () => {
      const toggleSpy = jest.spyOn(service as any, 'toggleQChat');
      
      const event = new MessageEvent('message', {
        data: TOGGLE_AI_CHAT_MESSAGE,
        origin: 'https://malicious.com'
      });

      window.dispatchEvent(event);

      expect(toggleSpy).not.toHaveBeenCalled();
    });

    it('should handle toggle message from valid origin', async () => {
      const toggleSpy = jest.spyOn(service as any, 'toggleQChat');
      
      const event = new MessageEvent('message', {
        data: TOGGLE_AI_CHAT_MESSAGE,
        origin: 'https://d-test123.sagemaker.us-east-1.on.aws'
      });

      window.dispatchEvent(event);

      expect(toggleSpy).toHaveBeenCalledWith(mockApp, mockChatPanel);
    });

    it('should ignore non-toggle messages', async () => {
      const toggleSpy = jest.spyOn(service as any, 'toggleQChat');
      
      const event = new MessageEvent('message', {
        data: 'other-message',
        origin: 'https://d-test123.sagemaker.us-east-1.on.aws'
      });

      window.dispatchEvent(event);

      expect(toggleSpy).not.toHaveBeenCalled();
    });
  });

  describe('isMessageOriginValid', () => {
    it('should return true for valid SageMaker domains with dynamic patterns', async () => {
      const mockMetadata = {
        domain_id: 'd-test123',
        aws_region: 'us-east-1',
        dz_stage: 'prod',
        sm_domain_id: 'd-test123',
        project_id: 'p-test',
        environment_id: 'env-test',
        repository_name: 'test-repo',
        user_id: 'test-user',
        sm_space_name: 'test-space',
        sm_user_profile_name: 'test-profile',
        sm_project_path: '/test'
      };
      
      (getSMEnvironmentMetadata as jest.Mock).mockResolvedValue(mockMetadata);
      
      const testService = new IngressPointService(mockApp, mockChatPanel);
      await new Promise(resolve => setTimeout(resolve, 100));

      const event = new MessageEvent('message', {
        origin: 'https://d-test123.sagemaker.us-east-1.on.aws'
      });

      const result = (testService as any).isMessageOriginValid(event);

      expect(result).toBe(true);
    });

    it('should return false for invalid domains', async () => {
      const mockMetadata = {
        domain_id: 'd-test123',
        aws_region: 'us-east-1',
        dz_stage: 'prod',
        sm_domain_id: 'd-test123',
        project_id: 'p-test',
        environment_id: 'env-test',
        repository_name: 'test-repo',
        user_id: 'test-user',
        sm_space_name: 'test-space',
        sm_user_profile_name: 'test-profile',
        sm_project_path: '/test'
      };
      
      (getSMEnvironmentMetadata as jest.Mock).mockResolvedValue(mockMetadata);
      
      const testService = new IngressPointService(mockApp, mockChatPanel);
      await new Promise(resolve => setTimeout(resolve, 100));

      const event = new MessageEvent('message', {
        origin: 'https://malicious.com'
      });

      const result = (testService as any).isMessageOriginValid(event);

      expect(result).toBe(false);
    });
    
    it('should fall back to same-origin validation when metadata fetch fails', async () => {
      (getSMEnvironmentMetadata as jest.Mock).mockRejectedValue(new Error('API unavailable'));
      
      const testService = new IngressPointService(mockApp, mockChatPanel);
      await new Promise(resolve => setTimeout(resolve, 100));

      // Mock window.location.origin
      Object.defineProperty(window, 'location', {
        value: { origin: 'https://test.origin.com' },
        writable: true
      });

      const validEvent = new MessageEvent('message', {
        origin: 'https://test.origin.com'
      });
      
      const invalidEvent = new MessageEvent('message', {
        origin: 'https://other.origin.com'
      });

      expect((testService as any).isMessageOriginValid(validEvent)).toBe(true);
      expect((testService as any).isMessageOriginValid(invalidEvent)).toBe(false);
    });
    
    it('should support multiple domains when sm_domain_id differs', async () => {
      const mockMetadata = {
        domain_id: 'd-primary',
        aws_region: 'us-west-2',
        dz_stage: 'gamma',
        sm_domain_id: 'd-secondary',  // Different
        project_id: 'p-test',
        environment_id: 'env-test',
        repository_name: 'test-repo',
        user_id: 'test-user',
        sm_space_name: 'test-space',
        sm_user_profile_name: 'test-profile',
        sm_project_path: '/test'
      };
      
      (getSMEnvironmentMetadata as jest.Mock).mockResolvedValue(mockMetadata);
      
      const testService = new IngressPointService(mockApp, mockChatPanel);
      await new Promise(resolve => setTimeout(resolve, 100));

      const event1 = new MessageEvent('message', {
        origin: 'https://d-primary.sagemaker-gamma.us-west-2.on.aws'
      });
      
      const event2 = new MessageEvent('message', {
        origin: 'https://d-secondary.sagemaker-gamma.us-west-2.on.aws'
      });

      expect((testService as any).isMessageOriginValid(event1)).toBe(true);
      expect((testService as any).isMessageOriginValid(event2)).toBe(true);
    });
  });

  describe('toggleQChat', () => {
    it('should throw error when widget has no id', () => {
      const panelWithoutId = { id: undefined } as unknown as Panel;
      
      expect(() => {
        (service as any).toggleQChat(mockApp, panelWithoutId);
      }).toThrow('Chat widget not found.');
    });
  });
});