import { describe, it, expect, beforeEach, vi } from 'vitest';
import fs from 'fs-extra';
import { logger } from '../../src/utils/logger';
import { updateMswDomain } from '../../src/commands/msw';

// Mock all dependencies
vi.mock('fs-extra');
vi.mock('../../src/utils/logger', () => ({
  logger: {
    error: vi.fn(),
    success: vi.fn(),
    info: vi.fn(),
    warning: vi.fn(),
    log: vi.fn(),
  }
}));
vi.mock('../../src/utils/domain-analyzer', () => ({
  getExistingDomains: vi.fn(),
}));
vi.mock('../../src/utils/domain-file-analyzer', () => ({
  analyzeDomain: vi.fn(),
}));

describe('MSW Domain Validation Tests', () => {
  const projectRoot = '/test/project';

  beforeEach(() => {
    vi.clearAllMocks();
  });

  describe('updateMswDomain domain name validation', () => {
    it('should reject domain names with hyphens', async () => {
      const { getExistingDomains } = await import('../../src/utils/domain-analyzer');
      
      (fs.exists as any).mockResolvedValue(true);
      (getExistingDomains as any).mockResolvedValue([
        { name: 'user', path: '/test/domains/user' }
      ]);

      await updateMswDomain('test-analytics', fs, logger, projectRoot, true);

      expect(logger.error).toHaveBeenCalledWith('Domain "test-analytics" not found. Available domains: user');
    });

    it('should reject domain names with spaces', async () => {
      const { getExistingDomains } = await import('../../src/utils/domain-analyzer');
      
      (fs.exists as any).mockResolvedValue(true);
      (getExistingDomains as any).mockResolvedValue([
        { name: 'user', path: '/test/domains/user' }
      ]);

      await updateMswDomain('test analytics', fs, logger, projectRoot, true);

      expect(logger.error).toHaveBeenCalledWith('Domain "test analytics" not found. Available domains: user');
    });

    it('should accept valid camelCase domain names', async () => {
      const { getExistingDomains } = await import('../../src/utils/domain-analyzer');
      const { analyzeDomain } = await import('../../src/utils/domain-file-analyzer');
      
      const mockAnalysis = {
        domainName: 'testAnalytics',
        entities: [{ name: 'TestAnalytics', fields: [], filePath: '' }],
        ports: [{ name: 'TestAnalyticsPort', methods: [], filePath: '' }],
        adapters: [{ name: 'TestAnalyticsAdapter', methods: [], filePath: '' }],
        primaryEntity: { name: 'TestAnalytics', fields: [], filePath: '' }
      };

      (fs.exists as any).mockImplementation((filePath: string) => {
        if (filePath.includes('mocks')) return Promise.resolve(true);
        if (filePath.includes('testAnalytics.ts') || filePath.includes('mockData.ts')) return Promise.resolve(true);
        return Promise.resolve(false);
      });
      (fs.readFile as any).mockResolvedValue('// existing content');
      (fs.writeFile as any).mockResolvedValue(undefined);
      (fs.ensureDir as any).mockResolvedValue(undefined);
      (getExistingDomains as any).mockResolvedValue([
        { name: 'testAnalytics', path: '/test/domains/testAnalytics' }
      ]);
      (analyzeDomain as any).mockResolvedValue(mockAnalysis);

      await updateMswDomain('testAnalytics', fs, logger, projectRoot, true);

      expect(logger.success).toHaveBeenCalledWith('MSW configuration updated for domain "testAnalytics"!');
    });

    it('should accept valid snake_case domain names', async () => {
      const { getExistingDomains } = await import('../../src/utils/domain-analyzer');
      const { analyzeDomain } = await import('../../src/utils/domain-file-analyzer');
      
      const mockAnalysis = {
        domainName: 'test_analytics',
        entities: [{ name: 'TestAnalytics', fields: [], filePath: '' }],
        ports: [{ name: 'TestAnalyticsPort', methods: [], filePath: '' }],
        adapters: [{ name: 'TestAnalyticsAdapter', methods: [], filePath: '' }],
        primaryEntity: { name: 'TestAnalytics', fields: [], filePath: '' }
      };

      (fs.exists as any).mockImplementation((filePath: string) => {
        if (filePath.includes('mocks')) return Promise.resolve(true);
        if (filePath.includes('test_analytics.ts') || filePath.includes('mockData.ts')) return Promise.resolve(true);
        return Promise.resolve(false);
      });
      (fs.readFile as any).mockResolvedValue('// existing content');
      (fs.writeFile as any).mockResolvedValue(undefined);
      (fs.ensureDir as any).mockResolvedValue(undefined);
      (getExistingDomains as any).mockResolvedValue([
        { name: 'test_analytics', path: '/test/domains/test_analytics' }
      ]);
      (analyzeDomain as any).mockResolvedValue(mockAnalysis);

      await updateMswDomain('test_analytics', fs, logger, projectRoot, true);

      expect(logger.success).toHaveBeenCalledWith('MSW configuration updated for domain "test_analytics"!');
    });

    it('should handle empty domain list gracefully', async () => {
      const { getExistingDomains } = await import('../../src/utils/domain-analyzer');
      
      (fs.exists as any).mockResolvedValue(true);
      (getExistingDomains as any).mockResolvedValue([]);

      await updateMswDomain('anyDomain', fs, logger, projectRoot, true);

      expect(logger.error).toHaveBeenCalledWith('Domain "anyDomain" not found. Available domains: ');
    });

    it('should list available domains when target not found', async () => {
      const { getExistingDomains } = await import('../../src/utils/domain-analyzer');
      
      (fs.exists as any).mockResolvedValue(true);
      (getExistingDomains as any).mockResolvedValue([
        { name: 'user', path: '/test/domains/user' },
        { name: 'product', path: '/test/domains/product' },
        { name: 'orderProcessing', path: '/test/domains/orderProcessing' }
      ]);

      await updateMswDomain('nonExistent', fs, logger, projectRoot, true);

      expect(logger.error).toHaveBeenCalledWith('Domain "nonExistent" not found. Available domains: user, product, orderProcessing');
    });
  });

  describe('Error handling edge cases', () => {
    it('should handle very long domain names', async () => {
      const { getExistingDomains } = await import('../../src/utils/domain-analyzer');
      
      const veryLongName = 'a'.repeat(100);
      
      (fs.exists as any).mockResolvedValue(true);
      (getExistingDomains as any).mockResolvedValue([
        { name: 'user', path: '/test/domains/user' }
      ]);

      await updateMswDomain(veryLongName, fs, logger, projectRoot, true);

      expect(logger.error).toHaveBeenCalledWith(`Domain "${veryLongName}" not found. Available domains: user`);
    });

    it('should handle special characters in domain names', async () => {
      const { getExistingDomains } = await import('../../src/utils/domain-analyzer');
      
      const specialCharName = 'test@domain!';
      
      (fs.exists as any).mockResolvedValue(true);
      (getExistingDomains as any).mockResolvedValue([
        { name: 'user', path: '/test/domains/user' }
      ]);

      await updateMswDomain(specialCharName, fs, logger, projectRoot, true);

      expect(logger.error).toHaveBeenCalledWith(`Domain "${specialCharName}" not found. Available domains: user`);
    });

    it('should handle numeric domain names', async () => {
      const { getExistingDomains } = await import('../../src/utils/domain-analyzer');
      
      (fs.exists as any).mockResolvedValue(true);
      (getExistingDomains as any).mockResolvedValue([
        { name: 'user', path: '/test/domains/user' }
      ]);

      await updateMswDomain('123domain', fs, logger, projectRoot, true);

      expect(logger.error).toHaveBeenCalledWith('Domain "123domain" not found. Available domains: user');
    });
  });
}); 