/**
 * Enhanced API Configuration Generator
 *
 * Generates comprehensive API infrastructure including:
 * - OpenAPI/Swagger documentation (auto-generated from routes)
 * - API versioning (URL path, header, query param strategies)
 * - CORS configuration
 * - Global middleware setup
 * - API deprecation policies
 * - Request/response validation
 */

import * as path from 'path';
import { Generator, GeneratorContext, GenerationResult, GeneratedFile } from '../types';
import { ApiConfiguration, Workflow } from '../../types/workflow';

export class EnhancedApiGenerator implements Generator {
  id = 'enhanced-api';
  name = 'Enhanced API Configuration Generator';
  description = 'Generates comprehensive API configuration including OpenAPI docs, versioning, CORS, and middleware';

  generate(context: GeneratorContext): GenerationResult {
    const { workflow, outputDirectory } = context;
    const files: GeneratedFile[] = [];

    if (!workflow.api) {
      return { files };
    }

    const api = workflow.api;
    const backendDir = this.getBackendDirectory(workflow);
    const baseDir = path.join(outputDirectory, backendDir);

    // Generate API documentation setup
    if (api.documentation) {
      files.push({
        path: path.join(baseDir, 'src/config/api-docs.ts'),
        content: this.generateApiDocsConfig(api),
      });

      files.push({
        path: path.join(baseDir, 'src/middleware/swagger.ts'),
        content: this.generateSwaggerMiddleware(api),
      });

      files.push({
        path: path.join(baseDir, 'src/utils/swagger-generator.ts'),
        content: this.generateSwaggerGenerator(api),
      });
    }

    // Generate versioning support
    if (api.versioning) {
      files.push({
        path: path.join(baseDir, 'src/middleware/versioning.ts'),
        content: this.generateVersioningMiddleware(api),
      });

      files.push({
        path: path.join(baseDir, 'src/utils/version-router.ts'),
        content: this.generateVersionRouter(api),
      });

      if (api.versioning.deprecation_policy) {
        files.push({
          path: path.join(baseDir, 'src/middleware/deprecation.ts'),
          content: this.generateDeprecationMiddleware(api),
        });
      }
    }

    // Generate CORS configuration
    if (api.cors?.enabled) {
      files.push({
        path: path.join(baseDir, 'src/config/cors.ts'),
        content: this.generateCorsConfig(api),
      });

      files.push({
        path: path.join(baseDir, 'src/middleware/cors.ts'),
        content: this.generateCorsMiddleware(api),
      });
    }

    // Generate global middleware setup
    if (api.middleware && api.middleware.length > 0) {
      files.push({
        path: path.join(baseDir, 'src/middleware/index.ts'),
        content: this.generateMiddlewareIndex(api),
      });
    }

    // Generate request validation middleware
    files.push({
      path: path.join(baseDir, 'src/middleware/validation.ts'),
      content: this.generateValidationMiddleware(api),
    });

    // Generate response formatting middleware
    files.push({
      path: path.join(baseDir, 'src/middleware/response-formatter.ts'),
      content: this.generateResponseFormatter(api),
    });

    // Generate API types
    files.push({
      path: path.join(baseDir, 'src/types/api.ts'),
      content: this.generateApiTypes(api),
    });

    // Generate server configuration with all API features
    files.push({
      path: path.join(baseDir, 'src/app.ts'),
      content: this.generateAppSetup(api),
    });

    return { files };
  }

  private getBackendDirectory(workflow: Workflow): string {
    for (const [name, component] of Object.entries(workflow.stack || {})) {
      if (component.type === 'backend') {
        return component.directory || name;
      }
    }
    return 'api';
  }

  private generateApiDocsConfig(api: ApiConfiguration): string {
    const format = api.documentation?.format || 'openapi';

    return `/**
 * API Documentation Configuration
 * Format: ${format}
 */

export const apiDocsConfig = {
  openapi: '3.0.0',
  info: {
    title: 'API Documentation',
    version: '1.0.0',
    description: 'Auto-generated API documentation',
    contact: {
      name: 'API Support',
      email: 'support@example.com',
    },
  },
  servers: [
    {
      url: process.env.API_URL || 'http://localhost:3000',
      description: 'API Server',
    },
  ],
  components: {
    securitySchemes: {
      bearerAuth: {
        type: 'http',
        scheme: 'bearer',
        bearerFormat: 'JWT',
      },
      apiKey: {
        type: 'apiKey',
        in: 'header',
        name: 'X-API-Key',
      },
    },
  },
  security: [
    {
      bearerAuth: [],
    },
  ],
};

export const swaggerUiOptions = {
  customCss: '.swagger-ui .topbar { display: none }',
  customSiteTitle: 'API Documentation',
};
`;
  }

  private generateSwaggerMiddleware(api: ApiConfiguration): string {
    const _format = api.documentation?.format || 'openapi';

    return `/**
 * Swagger/OpenAPI Middleware
 * Serves interactive API documentation
 */

import swaggerUi from 'swagger-ui-express';
import { apiDocsConfig, swaggerUiOptions } from '../config/api-docs';
import { generateSwaggerDoc } from '../utils/swagger-generator';
import { Router } from 'express';

export function setupSwagger(): Router {
  const router = Router();

  // Auto-generate documentation from routes
  const swaggerDocument = ${api.documentation?.auto_generate ? 'generateSwaggerDoc()' : 'apiDocsConfig'};

  // Serve Swagger UI
  router.use('/docs', swaggerUi.serve);
  router.get('/docs', swaggerUi.setup(swaggerDocument, swaggerUiOptions));

  // Serve raw OpenAPI JSON
  router.get('/docs.json', (req, res) => {
    res.json(swaggerDocument);
  });

  return router;
}
`;
  }

  private generateSwaggerGenerator(_api: ApiConfiguration): string {
    return `/**
 * Auto-generate Swagger/OpenAPI documentation from Express routes
 */

import { Router } from 'express';
import { apiDocsConfig } from '../config/api-docs';

interface RouteInfo {
  path: string;
  method: string;
  handler: string;
  summary?: string;
  tags?: string[];
  parameters?: any[];
  requestBody?: any;
  responses?: any;
}

export function generateSwaggerDoc(): any {
  const paths: Record<string, any> = {};

  // Extract routes from Express app
  // This is a simplified version - in production, use swagger-jsdoc or similar
  const routes = extractRoutes();

  routes.forEach(route => {
    if (!paths[route.path]) {
      paths[route.path] = {};
    }

    paths[route.path][route.method.toLowerCase()] = {
      summary: route.summary || \`\${route.method} \${route.path}\`,
      tags: route.tags || ['default'],
      parameters: route.parameters || [],
      requestBody: route.requestBody,
      responses: route.responses || {
        200: {
          description: 'Successful response',
        },
        400: {
          description: 'Bad request',
        },
        401: {
          description: 'Unauthorized',
        },
        500: {
          description: 'Internal server error',
        },
      },
    };
  });

  return {
    ...apiDocsConfig,
    paths,
  };
}

function extractRoutes(): RouteInfo[] {
  // TODO: Implement route extraction from Express app
  // This could use express-list-endpoints or similar
  return [];
}

/**
 * Decorator for documenting API endpoints
 */
export function ApiEndpoint(options: {
  summary?: string;
  tags?: string[];
  parameters?: any[];
  requestBody?: any;
  responses?: any;
}) {
  return function (target: any, propertyKey: string, descriptor: PropertyDescriptor) {
    // Store metadata for swagger generation
    Reflect.defineMetadata('swagger', options, target, propertyKey);
    return descriptor;
  };
}
`;
  }

  private generateVersioningMiddleware(api: ApiConfiguration): string {
    const strategy = api.versioning?.strategy || 'url_path';

    return `/**
 * API Versioning Middleware
 * Strategy: ${strategy}
 */

import { Request, Response, NextFunction } from 'express';

export interface VersionedRequest extends Request {
  apiVersion?: string;
}

export function versioningMiddleware(req: VersionedRequest, res: Response, next: NextFunction) {
  let version: string | undefined;

  ${strategy === 'url_path' ? `
  // Extract version from URL path (e.g., /api/v1/users)
  const pathMatch = req.path.match(/^\\/api\\/v(\\d+)/);
  if (pathMatch) {
    version = pathMatch[1];
  }
  ` : strategy === 'header' ? `
  // Extract version from header (e.g., X-API-Version: 1)
  version = req.headers['x-api-version'] as string;
  ` : strategy === 'query_param' ? `
  // Extract version from query param (e.g., ?version=1)
  version = req.query.version as string;
  ` : ''}

  // Default to v1 if no version specified
  req.apiVersion = version || '1';

  next();
}

export function requireVersion(minVersion: number) {
  return (req: VersionedRequest, res: Response, next: NextFunction) => {
    const currentVersion = parseInt(req.apiVersion || '1', 10);

    if (currentVersion < minVersion) {
      return res.status(400).json({
        error: 'API_VERSION_TOO_OLD',
        message: \`This endpoint requires API version \${minVersion} or higher\`,
        current_version: currentVersion,
      });
    }

    next();
  };
}
`;
  }

  private generateVersionRouter(_api: ApiConfiguration): string {
    return `/**
 * Version-based Route Router
 * Routes requests to version-specific handlers
 */

import { Router, Request, Response } from 'express';
import { VersionedRequest } from '../middleware/versioning';

export class VersionRouter {
  private routers: Map<string, Router> = new Map();

  constructor() {
    // Initialize routers for each version
    this.routers.set('1', Router());
    this.routers.set('2', Router());
  }

  getRouter(version: string): Router {
    return this.routers.get(version) || this.routers.get('1')!;
  }

  route(req: VersionedRequest, res: Response) {
    const version = req.apiVersion || '1';
    const router = this.getRouter(version);

    if (!router) {
      return res.status(400).json({
        error: 'UNSUPPORTED_VERSION',
        message: \`API version \${version} is not supported\`,
        supported_versions: Array.from(this.routers.keys()),
      });
    }

    router.handle(req, res, () => {
      res.status(404).json({
        error: 'NOT_FOUND',
        message: 'Route not found',
      });
    });
  }
}

export const versionRouter = new VersionRouter();
`;
  }

  private generateDeprecationMiddleware(api: ApiConfiguration): string {
    const noticePeriod = api.versioning?.deprecation_policy?.notice_period || '90_days';
    const sunsetHeaders = api.versioning?.deprecation_policy?.sunset_headers !== false;

    return `/**
 * API Deprecation Middleware
 * Handles deprecated endpoints with sunset headers
 * Notice period: ${noticePeriod}
 */

import { Request, Response, NextFunction } from 'express';

export interface DeprecationInfo {
  version: string;
  sunsetDate: Date;
  replacement?: string;
  message?: string;
}

const deprecatedEndpoints: Map<string, DeprecationInfo> = new Map();

export function deprecate(options: DeprecationInfo) {
  return (req: Request, res: Response, next: NextFunction) => {
    const key = \`\${req.method}:\${req.path}\`;
    deprecatedEndpoints.set(key, options);

    ${sunsetHeaders ? `
    // Add Sunset header (RFC 8594)
    res.setHeader('Sunset', options.sunsetDate.toUTCString());

    // Add Deprecation header
    res.setHeader('Deprecation', 'true');

    // Add Link header to replacement if available
    if (options.replacement) {
      res.setHeader('Link', \`<\${options.replacement}>; rel="successor-version"\`);
    }
    ` : ''}

    // Add deprecation warning to response
    res.setHeader('X-API-Deprecated', 'true');
    res.setHeader('X-API-Deprecation-Message', options.message || 'This endpoint is deprecated');

    next();
  };
}

export function checkDeprecation(req: Request, res: Response, next: NextFunction) {
  const key = \`\${req.method}:\${req.path}\`;
  const deprecationInfo = deprecatedEndpoints.get(key);

  if (deprecationInfo && new Date() > deprecationInfo.sunsetDate) {
    return res.status(410).json({
      error: 'ENDPOINT_SUNSET',
      message: 'This endpoint has been sunset',
      sunset_date: deprecationInfo.sunsetDate,
      replacement: deprecationInfo.replacement,
    });
  }

  next();
}
`;
  }

  private generateCorsConfig(api: ApiConfiguration): string {
    const origins = api.cors?.origins || ['http://localhost:3000'];
    const methods = api.cors?.methods || ['GET', 'POST', 'PUT', 'DELETE', 'PATCH'];
    const credentials = api.cors?.credentials !== false;

    return `/**
 * CORS Configuration
 */

import { CorsOptions } from 'cors';

const allowedOrigins = ${JSON.stringify(origins, null, 2)};

export const corsConfig: CorsOptions = {
  origin: (origin, callback) => {
    // Allow requests with no origin (mobile apps, Postman, etc.)
    if (!origin) {
      return callback(null, true);
    }

    // Check if origin is in allowed list
    if (allowedOrigins.includes(origin) || allowedOrigins.includes('*')) {
      callback(null, true);
    } else {
      callback(new Error('Not allowed by CORS'));
    }
  },
  methods: ${JSON.stringify(methods)},
  credentials: ${credentials},
  maxAge: 86400, // 24 hours
  allowedHeaders: [
    'Content-Type',
    'Authorization',
    'X-API-Key',
    'X-API-Version',
    'X-Request-ID',
  ],
  exposedHeaders: [
    'X-Total-Count',
    'X-Page-Count',
    'X-RateLimit-Limit',
    'X-RateLimit-Remaining',
  ],
};
`;
  }

  private generateCorsMiddleware(_api: ApiConfiguration): string {
    return `/**
 * CORS Middleware
 */

import cors from 'cors';
import { corsConfig } from '../config/cors';

export const corsMiddleware = cors(corsConfig);

export function corsPreflightHandler() {
  return cors({
    ...corsConfig,
    methods: ['OPTIONS'],
  });
}
`;
  }

  private generateMiddlewareIndex(api: ApiConfiguration): string {
    const _middleware = api.middleware || [];

    return `/**
 * Global Middleware Registry
 * Exports all API middleware for easy composition
 */

${api.documentation ? "import { setupSwagger } from './swagger';" : ''}
${api.versioning ? "import { versioningMiddleware } from './versioning';" : ''}
${api.versioning?.deprecation_policy ? "import { checkDeprecation } from './deprecation';" : ''}
${api.cors?.enabled ? "import { corsMiddleware } from './cors';" : ''}
import { validationMiddleware } from './validation';
import { responseFormatter } from './response-formatter';

export const globalMiddleware = [
  ${api.cors?.enabled ? 'corsMiddleware,' : ''}
  ${api.versioning ? 'versioningMiddleware,' : ''}
  ${api.versioning?.deprecation_policy ? 'checkDeprecation,' : ''}
  responseFormatter,
];

export {
  ${api.documentation ? 'setupSwagger,' : ''}
  ${api.versioning ? 'versioningMiddleware,' : ''}
  ${api.versioning?.deprecation_policy ? 'checkDeprecation,' : ''}
  ${api.cors?.enabled ? 'corsMiddleware,' : ''}
  validationMiddleware,
  responseFormatter,
};
`;
  }

  private generateValidationMiddleware(_api: ApiConfiguration): string {
    return `/**
 * Request Validation Middleware
 * Validates incoming requests against schemas
 */

import { Request, Response, NextFunction } from 'express';
import { z, ZodSchema } from 'zod';

export interface ValidationSchema {
  body?: ZodSchema;
  query?: ZodSchema;
  params?: ZodSchema;
  headers?: ZodSchema;
}

export function validate(schema: ValidationSchema) {
  return async (req: Request, res: Response, next: NextFunction) => {
    try {
      if (schema.body) {
        req.body = await schema.body.parseAsync(req.body);
      }

      if (schema.query) {
        req.query = await schema.query.parseAsync(req.query);
      }

      if (schema.params) {
        req.params = await schema.params.parseAsync(req.params);
      }

      if (schema.headers) {
        req.headers = await schema.headers.parseAsync(req.headers);
      }

      next();
    } catch (error) {
      if (error instanceof z.ZodError) {
        return res.status(400).json({
          error: 'VALIDATION_ERROR',
          message: 'Request validation failed',
          details: error.errors.map(err => ({
            path: err.path.join('.'),
            message: err.message,
            code: err.code,
          })),
        });
      }

      next(error);
    }
  };
}

export const validationMiddleware = (req: Request, res: Response, next: NextFunction) => {
  // Global validation logic if needed
  next();
};
`;
  }

  private generateResponseFormatter(_api: ApiConfiguration): string {
    return `/**
 * Response Formatting Middleware
 * Provides consistent response format across all endpoints
 */

import { Request, Response, NextFunction } from 'express';

export interface ApiResponse<T = any> {
  success: boolean;
  data?: T;
  error?: {
    code: string;
    message: string;
    details?: any;
  };
  meta?: {
    timestamp: string;
    requestId?: string;
    version?: string;
  };
}

export function responseFormatter(req: Request, res: Response, next: NextFunction) {
  // Extend res object with helper methods
  res.success = function <T>(data: T, meta?: Record<string, any>) {
    const response: ApiResponse<T> = {
      success: true,
      data,
      meta: {
        timestamp: new Date().toISOString(),
        requestId: req.headers['x-request-id'] as string,
        version: (req as any).apiVersion,
        ...meta,
      },
    };
    return this.json(response);
  };

  res.error = function (code: string, message: string, details?: any, statusCode = 400) {
    const response: ApiResponse = {
      success: false,
      error: {
        code,
        message,
        details,
      },
      meta: {
        timestamp: new Date().toISOString(),
        requestId: req.headers['x-request-id'] as string,
        version: (req as any).apiVersion,
      },
    };
    return this.status(statusCode).json(response);
  };

  next();
}

// Extend Express Response type
declare global {
  namespace Express {
    interface Response {
      success<T>(data: T, meta?: Record<string, any>): Response;
      error(code: string, message: string, details?: any, statusCode?: number): Response;
    }
  }
}
`;
  }

  private generateApiTypes(_api: ApiConfiguration): string {
    return `/**
 * API Type Definitions
 */

export interface PaginationParams {
  page?: number;
  limit?: number;
  offset?: number;
}

export interface SortParams {
  sortBy?: string;
  sortOrder?: 'asc' | 'desc';
}

export interface FilterParams {
  [key: string]: any;
}

export interface PaginatedResponse<T> {
  data: T[];
  pagination: {
    page: number;
    limit: number;
    total: number;
    totalPages: number;
    hasNext: boolean;
    hasPrevious: boolean;
  };
}

export interface ApiError {
  code: string;
  message: string;
  details?: any;
  statusCode: number;
}

export class ApiException extends Error implements ApiError {
  constructor(
    public code: string,
    public message: string,
    public statusCode: number = 400,
    public details?: any
  ) {
    super(message);
    this.name = 'ApiException';
  }
}

export class ValidationException extends ApiException {
  constructor(message: string, details?: any) {
    super('VALIDATION_ERROR', message, 400, details);
    this.name = 'ValidationException';
  }
}

export class UnauthorizedException extends ApiException {
  constructor(message: string = 'Unauthorized') {
    super('UNAUTHORIZED', message, 401);
    this.name = 'UnauthorizedException';
  }
}

export class ForbiddenException extends ApiException {
  constructor(message: string = 'Forbidden') {
    super('FORBIDDEN', message, 403);
    this.name = 'ForbiddenException';
  }
}

export class NotFoundException extends ApiException {
  constructor(message: string = 'Not found') {
    super('NOT_FOUND', message, 404);
    this.name = 'NotFoundException';
  }
}
`;
  }

  private generateAppSetup(api: ApiConfiguration): string {
    return `/**
 * Express Application Setup
 * Configures all API features and middleware
 */

import express, { Application } from 'express';
import helmet from 'helmet';
${api.documentation ? "import { setupSwagger } from './middleware/swagger';" : ''}
${api.cors?.enabled ? "import { corsMiddleware } from './middleware/cors';" : ''}
${api.versioning ? "import { versioningMiddleware } from './middleware/versioning';" : ''}
${api.versioning?.deprecation_policy ? "import { checkDeprecation } from './middleware/deprecation';" : ''}
import { responseFormatter } from './middleware/response-formatter';
import { ApiException } from './types/api';

export function createApp(): Application {
  const app = express();

  // Security headers
  app.use(helmet());

  // Body parsing
  app.use(express.json({ limit: '10mb' }));
  app.use(express.urlencoded({ extended: true, limit: '10mb' }));

  ${api.cors?.enabled ? `
  // CORS
  app.use(corsMiddleware);
  ` : ''}

  ${api.versioning ? `
  // API versioning
  app.use(versioningMiddleware);
  ` : ''}

  ${api.versioning?.deprecation_policy ? `
  // Deprecation checking
  app.use(checkDeprecation);
  ` : ''}

  // Response formatting
  app.use(responseFormatter);

  ${api.documentation ? `
  // API Documentation
  app.use(setupSwagger());
  ` : ''}

  // Health check
  app.get('/health', (req, res) => {
    res.success({ status: 'healthy', timestamp: new Date().toISOString() });
  });

  // API routes will be mounted here
  // app.use('${api.base_path || '/api'}', routes);

  // Error handling
  app.use((err: Error, req: express.Request, res: express.Response, next: express.NextFunction) => {
    if (err instanceof ApiException) {
      return res.error(err.code, err.message, err.details, err.statusCode);
    }

    console.error('Unhandled error:', err);
    return res.error('INTERNAL_ERROR', 'An unexpected error occurred', undefined, 500);
  });

  return app;
}
`;
  }
}
