/**
 * Test Generator
 * Generates test files for generated API routes and components
 */

import { ApiEndpointDef, PrismaModelDef } from '../../core/schema/generator';

export interface GeneratedTest {
  filename: string;
  content: string;
  type: 'unit' | 'integration' | 'e2e';
}

/**
 * Generate test files for API endpoints
 */
export function generateApiTests(
  endpoints: ApiEndpointDef[],
  models: PrismaModelDef[]
): GeneratedTest[] {
  const tests: GeneratedTest[] = [];
  
  // Group endpoints by model
  const grouped = groupByModel(endpoints);
  
  for (const [modelName, modelEndpoints] of Object.entries(grouped)) {
    const model = models.find(m => m.name === modelName);
    
    tests.push({
      filename: `__tests__/api/${modelName.toLowerCase()}.test.ts`,
      content: generateModelApiTest(modelName, modelEndpoints, model),
      type: 'integration',
    });
  }
  
  return tests;
}

function groupByModel(endpoints: ApiEndpointDef[]): Record<string, ApiEndpointDef[]> {
  const grouped: Record<string, ApiEndpointDef[]> = {};
  
  for (const endpoint of endpoints) {
    const modelName = extractModelName(endpoint.path);
    if (!grouped[modelName]) {
      grouped[modelName] = [];
    }
    grouped[modelName].push(endpoint);
  }
  
  return grouped;
}

function extractModelName(path: string): string {
  const parts = path.replace('/api/', '').split('/')[0];
  return parts
    .split('-')
    .map(p => p.charAt(0).toUpperCase() + p.slice(1))
    .join('');
}

function generateModelApiTest(
  modelName: string,
  endpoints: ApiEndpointDef[],
  model?: PrismaModelDef
): string {
  const modelNameLower = modelName.toLowerCase();
  const basePath = `/api/${toKebabCase(modelName)}`;
  
  // Generate mock data based on model fields
  const mockData = generateMockData(model);
  
  return `/**
 * Auto-generated API tests for ${modelName}
 * Generated by Dark Factory
 */

import { createMocks } from 'node-mocks-http';
import { prisma } from '@/lib/db';

// Mock Prisma
jest.mock('@/lib/db', () => ({
  prisma: {
    ${modelNameLower}: {
      findMany: jest.fn(),
      findUnique: jest.fn(),
      create: jest.fn(),
      update: jest.fn(),
      delete: jest.fn(),
      count: jest.fn(),
    },
  },
}));

const mockPrisma = prisma.${modelNameLower} as jest.Mocked<typeof prisma.${modelNameLower}>;

describe('${modelName} API', () => {
  const mockData = ${JSON.stringify(mockData, null, 2)};

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

  describe('GET ${basePath}', () => {
    it('should return a list of ${modelName} records', async () => {
      mockPrisma.findMany.mockResolvedValue([mockData]);
      mockPrisma.count.mockResolvedValue(1);

      // Test would call the actual route handler
      // This is a placeholder for the actual test implementation
      expect(mockPrisma.findMany).toBeDefined();
    });

    it('should support pagination', async () => {
      mockPrisma.findMany.mockResolvedValue([mockData]);
      mockPrisma.count.mockResolvedValue(100);

      // Test pagination parameters
      expect(mockPrisma.findMany).toBeDefined();
    });
  });

  describe('POST ${basePath}', () => {
    it('should create a new ${modelName}', async () => {
      mockPrisma.create.mockResolvedValue(mockData);

      expect(mockPrisma.create).toBeDefined();
    });

    it('should validate required fields', async () => {
      // Test validation
      expect(true).toBe(true);
    });
  });

  describe('GET ${basePath}/[id]', () => {
    it('should return a single ${modelName}', async () => {
      mockPrisma.findUnique.mockResolvedValue(mockData);

      expect(mockPrisma.findUnique).toBeDefined();
    });

    it('should return 404 for non-existent ${modelName}', async () => {
      mockPrisma.findUnique.mockResolvedValue(null);

      expect(mockPrisma.findUnique).toBeDefined();
    });
  });

  describe('PUT ${basePath}/[id]', () => {
    it('should update a ${modelName}', async () => {
      mockPrisma.update.mockResolvedValue({ ...mockData, updatedAt: new Date() });

      expect(mockPrisma.update).toBeDefined();
    });
  });

  describe('DELETE ${basePath}/[id]', () => {
    it('should delete a ${modelName}', async () => {
      mockPrisma.delete.mockResolvedValue(mockData);

      expect(mockPrisma.delete).toBeDefined();
    });
  });
});
`;
}

function generateMockData(model?: PrismaModelDef): Record<string, unknown> {
  const mockData: Record<string, unknown> = {
    id: 'test-id-123',
    createdAt: new Date().toISOString(),
    updatedAt: new Date().toISOString(),
  };
  
  if (model) {
    for (const field of model.fields) {
      if (['id', 'createdAt', 'updatedAt'].includes(field.name)) continue;
      if (!isPrimitiveField(field.type)) continue;
      
      mockData[field.name] = getMockValue(field.type, field.name);
    }
  }
  
  return mockData;
}

function isPrimitiveField(type: string): boolean {
  const primitives = ['String', 'Int', 'Float', 'Boolean', 'DateTime', 'Json', 'Decimal'];
  return primitives.includes(type);
}

function getMockValue(type: string, fieldName: string): unknown {
  // Use field name hints for better mock data
  if (fieldName.toLowerCase().includes('email')) return 'test@example.com';
  if (fieldName.toLowerCase().includes('name')) return 'Test Name';
  if (fieldName.toLowerCase().includes('status')) return 'active';
  if (fieldName.toLowerCase().includes('price') || fieldName.toLowerCase().includes('amount')) return 99.99;
  if (fieldName.toLowerCase().includes('count') || fieldName.toLowerCase().includes('quantity')) return 10;
  
  // Fall back to type-based defaults
  switch (type) {
    case 'String': return 'test-string';
    case 'Int': return 42;
    case 'Float': 
    case 'Decimal': return 3.14;
    case 'Boolean': return true;
    case 'DateTime': return new Date().toISOString();
    case 'Json': return {};
    default: return null;
  }
}

function toKebabCase(str: string): string {
  return str
    .replace(/([a-z])([A-Z])/g, '$1-$2')
    .toLowerCase();
}
