import type { DomainAnalysis, ParsedEntity, ParsedMethod, ParsedAdapter, ParsedPorts } from '../utils/domain-file-analyzer';

/**
 * Generate dynamic MSW handlers based on domain analysis
 */
export const generateDynamicMswHandlers = (analysis: DomainAnalysis): string => {
  const { domainName, entities, adapters, ports, primaryEntity } = analysis;
  const capitalizedDomain = domainName.charAt(0).toUpperCase() + domainName.slice(1);
  
  // Get primary adapter or fallback to first adapter
  const primaryAdapter = adapters.find(a => 
    a.name.toLowerCase().includes('api') || 
    a.name.toLowerCase().includes(domainName.toLowerCase())
  ) || adapters[0];
  
  const baseUrl = primaryAdapter?.baseUrl || `/api/${domainName}`;
  const mockDataPath = `../../../domains/${domainName}/mocks/mockData`;
  
  // Generate imports
  const imports = generateImports(entities, mockDataPath, capitalizedDomain);
  
  // Generate handlers based on ports (interface) and adapter for baseUrl
  const primaryPort = ports.find(p => 
    p.name.toLowerCase().includes('port') || 
    p.name.toLowerCase().includes(domainName.toLowerCase())
  ) || ports[0];
  
  const handlers = primaryPort ? 
    generateHandlersFromPort(primaryPort, baseUrl, capitalizedDomain) :
    primaryAdapter ? 
      generateHandlersFromAdapter(primaryAdapter, baseUrl, capitalizedDomain) :
      generateDefaultHandlers(domainName, baseUrl, capitalizedDomain);
  
  return `// ${capitalizedDomain} Domain MSW Handlers
// Auto-generated based on domain analysis
//
// ⚠️  IMPORTANT: This file is generated based on your Port interface methods.
// The endpoints are generic and need to be customized to match your actual API.
//
// TODO: Update the endpoints and logic to match your real API:
// - Modify API_BASE to your actual base URL
// - Update endpoint paths for each method
// - Customize request/response handling
// - Add proper query parameters for search methods

import { http, HttpResponse } from 'msw';
${imports}

const API_BASE = '${baseUrl}';

export const ${domainName}Handlers = [
${handlers}
];
`;
};

/**
 * Generate imports based on entities
 */
const generateImports = (entities: ParsedEntity[], mockDataPath: string, capitalizedDomain: string): string => {
  if (entities.length === 0) {
    return `import { mock${capitalizedDomain}Data } from '${mockDataPath}';`;
  }
  
  const entityImports = entities.map(entity => entity.name).join(', ');
  return `import { ${entityImports} } from '../../../domains/${entities[0]?.name.toLowerCase()}/entities';
import { mock${capitalizedDomain}Data } from '${mockDataPath}';`;
};

/**
 * Generate handlers based on port methods (preferred approach)
 */
const generateHandlersFromPort = (port: ParsedPorts, baseUrl: string, capitalizedDomain: string): string => {
  const handlers: string[] = [];
  
  for (const method of port.methods) {
    const handler = generateHandlerFromMethod(method, baseUrl, capitalizedDomain);
    if (handler) {
      handlers.push(handler);
    }
  }
  
  // If no handlers generated, add default ones
  if (handlers.length === 0) {
    return generateDefaultHandlers(port.name, baseUrl, capitalizedDomain);
  }
  
  return handlers.join(',\n\n');
};

/**
 * Generate handlers based on adapter methods
 */
const generateHandlersFromAdapter = (adapter: ParsedAdapter, baseUrl: string, capitalizedDomain: string): string => {
  const handlers: string[] = [];
  
  for (const method of adapter.methods) {
    const handler = generateHandlerFromMethod(method, baseUrl, capitalizedDomain);
    if (handler) {
      handlers.push(handler);
    }
  }
  
  // If no handlers generated, add default ones
  if (handlers.length === 0) {
    return generateDefaultHandlers(adapter.name, baseUrl, capitalizedDomain);
  }
  
  return handlers.join(',\n\n');
};

/**
 * Generate a single handler from a method
 */
const generateHandlerFromMethod = (method: ParsedMethod, baseUrl: string, capitalizedDomain: string): string | null => {
  const { name, httpMethod, endpoint, parameters, returnType } = method;
  
  if (!httpMethod) return null;
  
  const fullEndpoint = endpoint ? `\${API_BASE}${endpoint}` : `\${API_BASE}`;
  const httpMethodLower = httpMethod.toLowerCase();
  
  // Generate handler based on HTTP method
  switch (httpMethod) {
    case 'GET':
      return generateGetHandler(name, fullEndpoint, returnType, parameters, capitalizedDomain);
    case 'POST':
      return generatePostHandler(name, fullEndpoint, returnType, capitalizedDomain);
    case 'PUT':
      return generatePutHandler(name, fullEndpoint, returnType, capitalizedDomain);
    case 'DELETE':
      return generateDeleteHandler(name, fullEndpoint, returnType, capitalizedDomain);
    case 'PATCH':
      return generatePatchHandler(name, fullEndpoint, returnType, capitalizedDomain);
    default:
      return null;
  }
};

/**
 * Generate GET handler
 */
const generateGetHandler = (methodName: string, endpoint: string, returnType: string, parameters: any[], capitalizedDomain: string): string => {
  const hasIdParam = parameters.some(p => p.name.toLowerCase().includes('id'));
  const isListMethod = methodName.toLowerCase().includes('list') || methodName.toLowerCase().includes('all');
  
  if (hasIdParam && !isListMethod) {
    return `  // GET ${endpoint.replace('${API_BASE}', '')} - ${methodName}
  // TODO: Update endpoint path to match your API (e.g., '/:id', '/by-id/:id', etc.)
  http.get(\`${endpoint}\`, ({ params }) => {
    const { id } = params;
    const item = mock${capitalizedDomain}Data.getById(id as string);
    
    if (!item) {
      return new HttpResponse(null, { 
        status: 404,
        statusText: '${capitalizedDomain} not found'
      });
    }
    
    return HttpResponse.json(item);
  })`;
  } else {
    return `  // GET ${endpoint.replace('${API_BASE}', '')} - ${methodName}
  // TODO: Update endpoint path and add query parameters if needed
  http.get(\`${endpoint}\`, () => {
    return HttpResponse.json(mock${capitalizedDomain}Data.getAll());
  })`;
  }
};

/**
 * Generate POST handler
 */
const generatePostHandler = (methodName: string, endpoint: string, returnType: string, capitalizedDomain: string): string => {
  return `  // POST ${endpoint.replace('${API_BASE}', '')} - ${methodName}
  // TODO: Customize endpoint path and request validation
  http.post(\`${endpoint}\`, async ({ request }) => {
    try {
      const newItem = await request.json();
      const createdItem = mock${capitalizedDomain}Data.create(newItem);
      
      return HttpResponse.json(createdItem, { status: 201 });
    } catch (error) {
      return new HttpResponse(null, { 
        status: 400,
        statusText: 'Invalid ${capitalizedDomain.toLowerCase()} data'
      });
    }
  })`;
};

/**
 * Generate PUT handler
 */
const generatePutHandler = (methodName: string, endpoint: string, returnType: string, capitalizedDomain: string): string => {
  return `  // PUT ${endpoint.replace('${API_BASE}', '')} - ${methodName}
  // TODO: Update endpoint path and validation logic
  http.put(\`${endpoint}\`, async ({ params, request }) => {
    const { id } = params;
    
    try {
      const updates = await request.json();
      const updatedItem = mock${capitalizedDomain}Data.update(id as string, updates);
      
      if (!updatedItem) {
        return new HttpResponse(null, { 
          status: 404,
          statusText: '${capitalizedDomain} not found'
        });
      }
      
      return HttpResponse.json(updatedItem);
    } catch (error) {
      return new HttpResponse(null, { 
        status: 400,
        statusText: 'Invalid ${capitalizedDomain.toLowerCase()} data'
      });
    }
  })`;
};

/**
 * Generate DELETE handler
 */
const generateDeleteHandler = (methodName: string, endpoint: string, returnType: string, capitalizedDomain: string): string => {
  return `  // DELETE ${endpoint.replace('${API_BASE}', '')} - ${methodName}
  // TODO: Customize endpoint path and authorization logic
  http.delete(\`${endpoint}\`, ({ params }) => {
    const { id } = params;
    const deleted = mock${capitalizedDomain}Data.delete(id as string);
    
    if (!deleted) {
      return new HttpResponse(null, { 
        status: 404,
        statusText: '${capitalizedDomain} not found'
      });
    }
    
    return new HttpResponse(null, { status: 204 });
  })`;
};

/**
 * Generate PATCH handler
 */
const generatePatchHandler = (methodName: string, endpoint: string, returnType: string, capitalizedDomain: string): string => {
  return `  // PATCH ${endpoint.replace('${API_BASE}', '')} - ${methodName}
  http.patch(\`${endpoint}\`, async ({ params, request }) => {
    const { id } = params;
    
    try {
      const updates = await request.json();
      const updatedItem = mock${capitalizedDomain}Data.update(id as string, updates);
      
      if (!updatedItem) {
        return new HttpResponse(null, { 
          status: 404,
          statusText: '${capitalizedDomain} not found'
        });
      }
      
      return HttpResponse.json(updatedItem);
    } catch (error) {
      return new HttpResponse(null, { 
        status: 400,
        statusText: 'Invalid ${capitalizedDomain.toLowerCase()} data'
      });
    }
  })`;
};

/**
 * Generate default handlers when no adapter methods found
 */
const generateDefaultHandlers = (domainName: string, baseUrl: string, capitalizedDomain: string): string => {
  return `  // GET ${baseUrl.replace('/api', '')} - List all ${domainName} items
  http.get(\`\${API_BASE}\`, () => {
    return HttpResponse.json(mock${capitalizedDomain}Data.getAll());
  }),

  // GET ${baseUrl.replace('/api', '')}/:id - Get single ${domainName} item
  http.get(\`\${API_BASE}/:id\`, ({ params }) => {
    const { id } = params;
    const item = mock${capitalizedDomain}Data.getById(id as string);
    
    if (!item) {
      return new HttpResponse(null, { 
        status: 404,
        statusText: '${capitalizedDomain} not found'
      });
    }
    
    return HttpResponse.json(item);
  }),

  // POST ${baseUrl.replace('/api', '')} - Create new ${domainName} item
  http.post(\`\${API_BASE}\`, async ({ request }) => {
    try {
      const newItem = await request.json();
      const createdItem = mock${capitalizedDomain}Data.create(newItem);
      
      return HttpResponse.json(createdItem, { status: 201 });
    } catch (error) {
      return new HttpResponse(null, { 
        status: 400,
        statusText: 'Invalid ${domainName} data'
      });
    }
  }),

  // PUT ${baseUrl.replace('/api', '')}/:id - Update ${domainName} item
  http.put(\`\${API_BASE}/:id\`, async ({ params, request }) => {
    const { id } = params;
    
    try {
      const updates = await request.json();
      const updatedItem = mock${capitalizedDomain}Data.update(id as string, updates);
      
      if (!updatedItem) {
        return new HttpResponse(null, { 
          status: 404,
          statusText: '${capitalizedDomain} not found'
        });
      }
      
      return HttpResponse.json(updatedItem);
    } catch (error) {
      return new HttpResponse(null, { 
        status: 400,
        statusText: 'Invalid ${domainName} data'
      });
    }
  }),

  // DELETE ${baseUrl.replace('/api', '')}/:id - Delete ${domainName} item
  http.delete(\`\${API_BASE}/:id\`, ({ params }) => {
    const { id } = params;
    const deleted = mock${capitalizedDomain}Data.delete(id as string);
    
    if (!deleted) {
      return new HttpResponse(null, { 
        status: 404,
        statusText: '${capitalizedDomain} not found'
      });
    }
    
    return new HttpResponse(null, { status: 204 });
  })`;
};

/**
 * Generate dynamic mock data based on entities
 */
export const generateDynamicMockData = (analysis: DomainAnalysis): string => {
  const { domainName, entities, primaryEntity } = analysis;
  const capitalizedDomain = domainName.charAt(0).toUpperCase() + domainName.slice(1);
  
  if (!primaryEntity || entities.length === 0) {
    // Fallback to generic template
    return generateGenericMockData(domainName, capitalizedDomain);
  }
  
  // Generate interface import
  const entityImports = entities.map(e => `import type { ${e.name} } from '../entities/${e.name}';`).join('\n');
  const interfaceImport = entityImports;
  
  // Generate mock data with real fields
  const mockDataSamples = generateMockDataSamples(primaryEntity, 3);
  const mockDataMethods = generateMockDataMethods(primaryEntity, capitalizedDomain);
  
  return `// ${capitalizedDomain} Domain Mock Data
// Auto-generated based on entity analysis
//
// ⚠️  IMPORTANT: This file is generated based on your Entity definitions.
// The mock data structure follows your entity fields but uses generic sample values.
//
// TODO: Customize the mock data to match your business needs:
// - Update sample data with realistic values for your domain
// - Add domain-specific business logic and validation
// - Customize error scenarios and edge cases
// - Add more sophisticated data relationships

${interfaceImport}

/**
 * Mock ${domainName} data store
 */
class Mock${capitalizedDomain}Data {
  private data: ${primaryEntity.name}[] = [
${mockDataSamples}
  ];

${mockDataMethods}
}

// Export singleton instance
export const mock${capitalizedDomain}Data = new Mock${capitalizedDomain}Data();
`;
};

/**
 * Generate mock data samples based on entity fields
 */
const generateMockDataSamples = (entity: ParsedEntity, count: number): string => {
  const samples: string[] = [];
  
  for (let i = 1; i <= count; i++) {
    const fields = entity.fields.map(field => {
      const value = generateMockValue(field.type, field.name, i);
      return `      ${field.name}: ${value}`;
    }).join(',\n');
    
    samples.push(`    {\n${fields}\n    }`);
  }
  
  return samples.join(',\n');
};

/**
 * Generate mock value based on field type
 */
const generateMockValue = (type: string, fieldName: string, index: number): string => {
  const lowerFieldName = fieldName.toLowerCase();
  const lowerType = type.toLowerCase();
  
  // Handle specific field names
  if (lowerFieldName.includes('id')) {
    return `'${index}'`;
  }
  if (lowerFieldName.includes('email')) {
    return `'user${index}@example.com'`;
  }
  if (lowerFieldName.includes('name') || lowerFieldName.includes('title')) {
    return `'Sample ${fieldName} ${index}'`;
  }
  if (lowerFieldName.includes('date') || lowerFieldName.includes('time') || lowerType.includes('date')) {
    const date = new Date();
    date.setDate(date.getDate() + index);
    return `new Date('${date.toISOString()}')`;
  }
  if (lowerFieldName.includes('url') || lowerFieldName.includes('link')) {
    return `'https://example.com/${fieldName.toLowerCase()}${index}'`;
  }
  if (lowerFieldName.includes('phone')) {
    return `'+1234567890${index}'`;
  }
  if (lowerFieldName.includes('price') || lowerFieldName.includes('cost') || lowerFieldName.includes('amount')) {
    return `${(index * 10).toFixed(2)}`;
  }
  
  // Handle union types (e.g., 'admin' | 'customer' | 'vendor')
  if (type.includes('|')) {
    const options = type.split('|').map(t => t.trim().replace(/['"]/g, ''));
    const validOptions = options.filter(opt => opt && opt.length > 0);
    if (validOptions.length > 0) {
      const selectedOption = validOptions[(index - 1) % validOptions.length];
      return `'${selectedOption}'`;
    }
  }
  
  // Handle types
  if (lowerType.includes('string')) {
    return `'Sample ${fieldName} ${index}'`;
  }
  if (lowerType.includes('number')) {
    return `${index * 10}`;
  }
  if (lowerType.includes('boolean')) {
    return index % 2 === 0 ? 'true' : 'false';
  }
  if (lowerType.includes('array')) {
    return `[]`;
  }
  if (lowerType.includes('object') || lowerType.includes('{')) {
    return `{}`;
  }
  
  // Default to string
  return `'Sample ${fieldName} ${index}'`;
};

/**
 * Generate mock data methods
 */
const generateMockDataMethods = (entity: ParsedEntity, capitalizedDomain: string): string => {
  return `  /**
   * Get all ${entity.name.toLowerCase()} items
   */
  getAll(): ${entity.name}[] {
    return [...this.data];
  }

  /**
   * Get ${entity.name.toLowerCase()} item by ID
   */
  getById(id: string): ${entity.name} | undefined {
    return this.data.find(item => item.id === id);
  }

  /**
   * Create new ${entity.name.toLowerCase()} item
   */
  create(item: Partial<${entity.name}>): ${entity.name} {
    const newItem: ${entity.name} = {
      id: Date.now().toString(),
      ...this.getDefaultValues(),
      ...item,
    } as ${entity.name};
    
    this.data.push(newItem);
    return newItem;
  }

  /**
   * Update ${entity.name.toLowerCase()} item
   */
  update(id: string, updates: Partial<${entity.name}>): ${entity.name} | undefined {
    const index = this.data.findIndex(item => item.id === id);
    
    if (index === -1) {
      return undefined;
    }
    
    this.data[index] = {
      ...this.data[index],
      ...updates,
    };
    
    return this.data[index];
  }

  /**
   * Delete ${entity.name.toLowerCase()} item
   */
  delete(id: string): boolean {
    const index = this.data.findIndex(item => item.id === id);
    
    if (index === -1) {
      return false;
    }
    
    this.data.splice(index, 1);
    return true;
  }

  /**
   * Reset data to initial state
   */
  reset(): void {
    this.data = [
${generateMockDataSamples(entity, 3)}
    ];
  }

  /**
   * Get default values for new items
   */
  private getDefaultValues(): Partial<${entity.name}> {
    return {
${entity.fields.filter(f => !f.optional).map(f => 
  `      ${f.name}: ${generateMockValue(f.type, f.name, 1)}`
).join(',\n')}
    };
  }`;
};

/**
 * Generate generic mock data when no entities found
 */
const generateGenericMockData = (domainName: string, capitalizedDomain: string): string => {
  return `// ${capitalizedDomain} Domain Mock Data
// Generic template - customize based on your domain entities
//
// ⚠️  IMPORTANT: No entities were found in this domain.
// This is a generic template that should be customized for your needs.
//
// TODO: Customize this template to match your business needs:
// - Replace the generic interface with your actual entity types
// - Update sample data with realistic values for your domain
// - Add domain-specific business logic and validation
// - Import your actual entity types from ../entities/

interface ${capitalizedDomain} {
  id: string;
  name: string;
  createdAt: string;
  updatedAt: string;
  // Add your domain-specific fields here
}

/**
 * Mock ${domainName} data store
 */
class Mock${capitalizedDomain}Data {
  private data: ${capitalizedDomain}[] = [
    {
      id: '1',
      name: 'Sample ${capitalizedDomain} 1',
      createdAt: '2025-01-01T00:00:00Z',
      updatedAt: '2025-01-01T00:00:00Z',
    },
    {
      id: '2',
      name: 'Sample ${capitalizedDomain} 2',
      createdAt: '2025-01-02T00:00:00Z',
      updatedAt: '2025-01-02T00:00:00Z',
    },
    {
      id: '3',
      name: 'Sample ${capitalizedDomain} 3',
      createdAt: '2025-01-03T00:00:00Z',
      updatedAt: '2025-01-03T00:00:00Z',
    },
  ];

  // Standard CRUD methods...
  getAll(): ${capitalizedDomain}[] { return [...this.data]; }
  getById(id: string): ${capitalizedDomain} | undefined { return this.data.find(item => item.id === id); }
  create(item: Partial<${capitalizedDomain}>): ${capitalizedDomain} { /* implementation */ }
  update(id: string, updates: Partial<${capitalizedDomain}>): ${capitalizedDomain} | undefined { /* implementation */ }
  delete(id: string): boolean { /* implementation */ }
  reset(): void { /* implementation */ }
}

// Export singleton instance
export const mock${capitalizedDomain}Data = new Mock${capitalizedDomain}Data();
`;
}; 