import { describe, it, expect, vi, beforeEach } from 'vitest';
import path from 'path';
import {
  capitalizeFirstLetter,
  createDomainInfo,
  buildDomainsPath,
  buildContextPath,
  buildServiceContextFilePath,
  buildServiceContextTestFilePath,
  validateDomainStructure,
  filterValidDomains,
  isDirectoryEntry,
  getExistingDomains,
  parseExistingServiceContext,
  findMissingDomains
} from '../src/utils/domain-analyzer';
import type { FileSystem } from '../src/utils/file-operations';

// Mock FileSystem pour les tests
const createMockFileSystem = (
  files: Record<string, string> = {},
  directories: string[] = []
): FileSystem => ({
  writeFile: vi.fn() as any,
  readFile: vi.fn().mockImplementation(async (filePath: string) => {
    if (files[filePath]) {
      return files[filePath];
    }
    throw new Error(`File not found: ${filePath}`);
  }) as any,
  readdir: vi.fn().mockImplementation(async (dirPath: string) => {
    const dirEntries = Object.keys(files)
      .filter(file => path.dirname(file) === dirPath)
      .map(file => path.basename(file));
    
    const subDirs = directories
      .filter(dir => path.dirname(dir) === dirPath && dir !== dirPath)
      .map(dir => path.basename(dir));
    
    return [...new Set([...dirEntries, ...subDirs])];
  }) as any,
  stat: vi.fn().mockImplementation(async (filePath: string) => ({
    isDirectory: () => directories.includes(filePath),
    isFile: () => files[filePath] !== undefined
  })) as any,
  remove: vi.fn() as any,
  mkdir: vi.fn() as any,
  exists: vi.fn().mockImplementation(async (filePath: string) => {
    return files[filePath] !== undefined || directories.includes(filePath);
  }),
  pathExists: vi.fn() as any,
  ensureDir: vi.fn() as any
});

describe('Domain Analyzer', () => {
  describe('Pure functions', () => {
    describe('capitalizeFirstLetter', () => {
      it('should capitalize first letter of lowercase string', () => {
        expect(capitalizeFirstLetter('user')).toBe('User');
        expect(capitalizeFirstLetter('product')).toBe('Product');
      });

      it('should handle already capitalized string', () => {
        expect(capitalizeFirstLetter('User')).toBe('User');
      });

      it('should handle empty string', () => {
        expect(capitalizeFirstLetter('')).toBe('');
      });

      it('should handle single character', () => {
        expect(capitalizeFirstLetter('a')).toBe('A');
        expect(capitalizeFirstLetter('A')).toBe('A');
      });
    });

    describe('createDomainInfo', () => {
      it('should create correct domain info object', () => {
        const result = createDomainInfo('user');
        expect(result).toEqual({
          name: 'user',
          capitalizedName: 'User'
        });
      });

      it('should handle complex domain names', () => {
        const result = createDomainInfo('userAccount');
        expect(result).toEqual({
          name: 'userAccount',
          capitalizedName: 'UserAccount'
        });
      });
    });

    describe('Path builders', () => {
      const projectRoot = '/test-project';

      it('should build domains path correctly', () => {
        const result = buildDomainsPath(projectRoot);
        expect(result).toBe('/test-project/src/domains');
      });

      it('should build context path correctly', () => {
        const result = buildContextPath(projectRoot);
        expect(result).toBe('/test-project/src/context');
      });

      it('should build service context file path correctly', () => {
        const result = buildServiceContextFilePath(projectRoot);
        expect(result).toBe('/test-project/src/context/ServiceContext.tsx');
      });

      it('should build service context test file path correctly', () => {
        const result = buildServiceContextTestFilePath(projectRoot);
        expect(result).toBe('/test-project/src/context/ServiceContext.test.tsx');
      });

      it('should use process.cwd() as default project root', () => {
        const result = buildDomainsPath();
        expect(result).toBe(path.join(process.cwd(), 'src', 'domains'));
      });
    });
  });

  describe('File system operations', () => {
    describe('isDirectoryEntry', () => {
      it('should return true for directories', async () => {
        const directories = ['/test/path'];
        const fileSystem = createMockFileSystem({}, directories);

        const result = await isDirectoryEntry(fileSystem, '/test/path');
        expect(result).toBe(true);
      });

      it('should return false for files', async () => {
        const files = { '/test/file.ts': 'content' };
        const fileSystem = createMockFileSystem(files);

        const result = await isDirectoryEntry(fileSystem, '/test/file.ts');
        expect(result).toBe(false);
      });

      it('should return false for non-existent paths', async () => {
        const fileSystem = createMockFileSystem();

        const result = await isDirectoryEntry(fileSystem, '/non/existent');
        expect(result).toBe(false);
      });
    });

    describe('validateDomainStructure', () => {
      it('should return true for valid domain structure', async () => {
        const files = {
          '/project/src/domains/user/services/UserService.ts': 'export class UserService {}',
          '/project/src/domains/user/adapters/UserAdapter.ts': 'export class UserAdapter {}'
        };
        
        const fileSystem = createMockFileSystem(files);
        
        const result = await validateDomainStructure(
          fileSystem,
          '/project/src/domains/user',
          'user'
        );
        
        expect(result).toBe(true);
      });

      it('should return false for incomplete domain structure', async () => {
        const files = {
          '/project/src/domains/user/services/UserService.ts': 'export class UserService {}'
          // Missing UserAdapter.ts
        };
        
        const fileSystem = createMockFileSystem(files);
        
        const result = await validateDomainStructure(
          fileSystem,
          '/project/src/domains/user',
          'user'
        );
        
        expect(result).toBe(false);
      });
    });

    describe('filterValidDomains', () => {
      it('should filter and return only valid domains', async () => {
        const files = {
          // Valid domain
          '/project/src/domains/user/services/UserService.ts': 'content',
          '/project/src/domains/user/adapters/UserAdapter.ts': 'content',
          
          // Invalid domain (missing adapter)
          '/project/src/domains/product/services/ProductService.ts': 'content',
          
          // File instead of directory - should be filtered out
          '/project/src/domains/somefile.txt': 'content'
        };
        
        const directories = [
          '/project/src/domains',
          '/project/src/domains/user',
          '/project/src/domains/user/services',
          '/project/src/domains/user/adapters',
          '/project/src/domains/product',
          '/project/src/domains/product/services'
        ];
        
        const fileSystem = createMockFileSystem(files, directories);
        
        const result = await filterValidDomains(
          fileSystem,
          '/project/src/domains',
          ['user', 'product', 'somefile.txt']
        );
        
        expect(result).toEqual(['user']);
      });

      it('should return empty array when no valid domains exist', async () => {
        const directories = ['/project/src/domains'];
        const fileSystem = createMockFileSystem({}, directories);
        
        const result = await filterValidDomains(
          fileSystem,
          '/project/src/domains',
          ['invalidDomain']
        );
        
        expect(result).toEqual([]);
      });
    });
  });

  describe('Domain discovery', () => {
    describe('getExistingDomains', () => {
      it('should return all valid domains', async () => {
        const files = {
          '/project/src/domains/user/services/UserService.ts': 'content',
          '/project/src/domains/user/adapters/UserAdapter.ts': 'content',
          '/project/src/domains/product/services/ProductService.ts': 'content',
          '/project/src/domains/product/adapters/ProductAdapter.ts': 'content'
        };
        
        const directories = [
          '/project/src/domains',
          '/project/src/domains/user',
          '/project/src/domains/user/services',
          '/project/src/domains/user/adapters',
          '/project/src/domains/product',
          '/project/src/domains/product/services',
          '/project/src/domains/product/adapters'
        ];
        
        const fileSystem = createMockFileSystem(files, directories);
        
        const result = await getExistingDomains(fileSystem, '/project');
        
        expect(result).toEqual([
          { name: 'user', capitalizedName: 'User' },
          { name: 'product', capitalizedName: 'Product' }
        ]);
      });

      it('should return empty array when domains directory does not exist', async () => {
        const fileSystem = createMockFileSystem();
        
        const result = await getExistingDomains(fileSystem, '/project');
        
        expect(result).toEqual([]);
      });
    });
  });

  describe('Service context parsing', () => {
    describe('parseExistingServiceContext', () => {
      it('should parse domains from existing service context', async () => {
        const serviceContextContent = `
interface Services {
  userService: UserService;
  productService: ProductService;
}
        `;
        
        const files = {
          '/project/src/context/ServiceContext.tsx': serviceContextContent
        };
        
        const fileSystem = createMockFileSystem(files);
        
        const result = await parseExistingServiceContext(fileSystem, '/project');
        
        expect(result).toEqual([
          { name: 'user', capitalizedName: 'User' },
          { name: 'product', capitalizedName: 'Product' }
        ]);
      });

      it('should return empty array when service context does not exist', async () => {
        const fileSystem = createMockFileSystem();
        
        const result = await parseExistingServiceContext(fileSystem, '/project');
        
        expect(result).toEqual([]);
      });
    });
  });

  describe('Missing domains detection', () => {
    describe('findMissingDomains', () => {
      it('should find domains missing from service context', async () => {
        // Existing domains in filesystem
        const files = {
          '/project/src/domains/user/services/UserService.ts': 'content',
          '/project/src/domains/user/adapters/UserAdapter.ts': 'content',
          '/project/src/domains/order/services/OrderService.ts': 'content',
          '/project/src/domains/order/adapters/OrderAdapter.ts': 'content',
          
          // Service context only includes user
          '/project/src/context/ServiceContext.tsx': `
interface Services {
  userService: UserService;
}
          `
        };
        
        const directories = [
          '/project/src/domains',
          '/project/src/domains/user',
          '/project/src/domains/user/services',
          '/project/src/domains/user/adapters',
          '/project/src/domains/order',
          '/project/src/domains/order/services',
          '/project/src/domains/order/adapters'
        ];
        
        const fileSystem = createMockFileSystem(files, directories);
        
        const result = await findMissingDomains(fileSystem, '/project');
        
        expect(result).toEqual([
          { name: 'order', capitalizedName: 'Order' }
        ]);
      });

      it('should return empty array when all domains are in service context', async () => {
        const files = {
          '/project/src/domains/user/services/UserService.ts': 'content',
          '/project/src/domains/user/adapters/UserAdapter.ts': 'content',
          '/project/src/context/ServiceContext.tsx': `
interface Services {
  userService: UserService;
}
          `
        };
        
        const directories = [
          '/project/src/domains',
          '/project/src/domains/user',
          '/project/src/domains/user/services',
          '/project/src/domains/user/adapters'
        ];
        
        const fileSystem = createMockFileSystem(files, directories);
        
        const result = await findMissingDomains(fileSystem, '/project');
        
        expect(result).toEqual([]);
      });
    });
  });
}); 