/**
 * API Documentation
 * OpenAPI/Swagger specification and API documentation
 */

export interface APIEndpoint {
  path: string;
  method: 'GET' | 'POST' | 'PUT' | 'DELETE' | 'PATCH';
  summary: string;
  description: string;
  tags: string[];
  parameters?: APIParameter[];
  requestBody?: APIRequestBody;
  responses: Record<number, APIResponse>;
  authentication?: 'bearer' | 'api_key' | 'none';
  rateLimit?: {
    requests: number;
    window: string; // e.g., "1m", "1h"
  };
  examples?: APIExample[];
}

export interface APIParameter {
  name: string;
  in: 'path' | 'query' | 'header';
  required: boolean;
  schema: {
    type: string;
    description: string;
    example?: any;
  };
}

export interface APIRequestBody {
  required: boolean;
  content: {
    'application/json': {
      schema: Record<string, any>;
      example?: any;
    };
  };
}

export interface APIResponse {
  description: string;
  content?: {
    'application/json': {
      schema: Record<string, any>;
      example?: any;
    };
  };
}

export interface APIExample {
  name: string;
  description: string;
  request: any;
  response: any;
}

export interface OpenAPISpec {
  openapi: string;
  info: {
    title: string;
    version: string;
    description: string;
    contact: {
      name: string;
      email: string;
      url: string;
    };
    license: {
      name: string;
      url: string;
    };
  };
  servers: Array<{
    url: string;
    description: string;
    variables?: Record<string, any>;
  }>;
  paths: Record<string, Record<string, any>>;
  components: {
    schemas: Record<string, any>;
    securitySchemes: Record<string, any>;
  };
  tags: Array<{
    name: string;
    description: string;
  }>;
}

/**
 * Generate OpenAPI specification
 */
export function generateOpenAPISpec(
  title: string,
  version: string,
  description: string,
  endpoints: APIEndpoint[]
): OpenAPISpec {
  const tags = new Set<string>();
  endpoints.forEach((ep) => ep.tags.forEach((tag) => tags.add(tag)));

  const paths: Record<string, Record<string, any>> = {};

  endpoints.forEach((endpoint) => {
    if (!paths[endpoint.path]) {
      paths[endpoint.path] = {};
    }

    const methodKey = endpoint.method.toLowerCase();
    paths[endpoint.path][methodKey] = {
      summary: endpoint.summary,
      description: endpoint.description,
      tags: endpoint.tags,
      parameters: endpoint.parameters,
      requestBody: endpoint.requestBody,
      responses: endpoint.responses,
      security:
        endpoint.authentication && endpoint.authentication !== 'none'
          ? [{ [endpoint.authentication]: [] }]
          : [],
    };
  });

  return {
    openapi: '3.0.0',
    info: {
      title,
      version,
      description,
      contact: {
        name: 'CoinKrazy Support',
        email: 'support@coinkrazy.com',
        url: 'https://coinkrazy.com/support',
      },
      license: {
        name: 'Proprietary',
        url: 'https://coinkrazy.com/license',
      },
    },
    servers: [
      {
        url: 'https://api.coinkrazy.com/v1',
        description: 'Production API',
      },
      {
        url: 'https://staging-api.coinkrazy.com/v1',
        description: 'Staging API',
      },
      {
        url: 'http://localhost:3000/api/v1',
        description: 'Local development',
      },
    ],
    paths,
    components: {
      schemas: {
        Player: {
          type: 'object',
          properties: {
            id: { type: 'number' },
            username: { type: 'string' },
            email: { type: 'string' },
            balance: { type: 'number' },
            level: { type: 'number' },
            createdAt: { type: 'string', format: 'date-time' },
          },
        },
        Game: {
          type: 'object',
          properties: {
            id: { type: 'string' },
            name: { type: 'string' },
            category: { type: 'string' },
            rtp: { type: 'number' },
            volatility: { type: 'string', enum: ['low', 'medium', 'high'] },
          },
        },
        Tournament: {
          type: 'object',
          properties: {
            id: { type: 'string' },
            name: { type: 'string' },
            startDate: { type: 'string', format: 'date-time' },
            endDate: { type: 'string', format: 'date-time' },
            prizePool: { type: 'number' },
            participants: { type: 'number' },
          },
        },
      },
      securitySchemes: {
        bearer: {
          type: 'http',
          scheme: 'bearer',
          bearerFormat: 'JWT',
        },
        api_key: {
          type: 'apiKey',
          in: 'header',
          name: 'X-API-Key',
        },
      },
    },
    tags: Array.from(tags).map((tag) => ({
      name: tag,
      description: `${tag} related endpoints`,
    })),
  };
}

/**
 * API Documentation Generator
 */
export class APIDocumentationGenerator {
  private endpoints: APIEndpoint[] = [];

  addEndpoint(endpoint: APIEndpoint): void {
    this.endpoints.push(endpoint);
  }

  addEndpoints(endpoints: APIEndpoint[]): void {
    this.endpoints.push(...endpoints);
  }

  generateOpenAPI(title: string, version: string, description: string): OpenAPISpec {
    return generateOpenAPISpec(title, version, description, this.endpoints);
  }

  generateMarkdown(): string {
    let markdown = '# API Documentation\n\n';

    const tags = new Set<string>();
    this.endpoints.forEach((ep) => ep.tags.forEach((tag) => tags.add(tag)));

    Array.from(tags).forEach((tag) => {
      markdown += `## ${tag}\n\n`;

      this.endpoints
        .filter((ep) => ep.tags.includes(tag))
        .forEach((endpoint) => {
          markdown += `### ${endpoint.summary}\n\n`;
          markdown += `**${endpoint.method}** \`${endpoint.path}\`\n\n`;
          markdown += `${endpoint.description}\n\n`;

          if (endpoint.parameters && endpoint.parameters.length > 0) {
            markdown += '#### Parameters\n\n';
            markdown += '| Name | Type | Required | Description |\n';
            markdown += '|------|------|----------|-------------|\n';
            endpoint.parameters.forEach((param) => {
              markdown += `| ${param.name} | ${param.schema.type} | ${param.required ? 'Yes' : 'No'} | ${param.schema.description} |\n`;
            });
            markdown += '\n';
          }

          if (endpoint.examples && endpoint.examples.length > 0) {
            markdown += '#### Examples\n\n';
            endpoint.examples.forEach((example) => {
              markdown += `**${example.name}**\n\n`;
              markdown += '```json\n';
              markdown += JSON.stringify(example.request, null, 2);
              markdown += '\n```\n\n';
            });
          }

          markdown += '\n';
        });
    });

    return markdown;
  }

  getEndpointCount(): number {
    return this.endpoints.length;
  }

  getEndpointsByTag(tag: string): APIEndpoint[] {
    return this.endpoints.filter((ep) => ep.tags.includes(tag));
  }
}

/**
 * Predefined API endpoints
 */
export const GAME_ENDPOINTS: APIEndpoint[] = [
  {
    path: '/games',
    method: 'GET',
    summary: 'List all games',
    description: 'Retrieve a paginated list of all available games',
    tags: ['Games'],
    parameters: [
      {
        name: 'page',
        in: 'query',
        required: false,
        schema: { type: 'integer', description: 'Page number (default: 1)' },
      },
      {
        name: 'limit',
        in: 'query',
        required: false,
        schema: { type: 'integer', description: 'Items per page (default: 20)' },
      },
    ],
    responses: {
      200: {
        description: 'List of games',
        content: {
          'application/json': {
            schema: {
              type: 'object',
              properties: {
                games: { type: 'array' },
                total: { type: 'number' },
                page: { type: 'number' },
              },
            },
          },
        },
      },
    },
    authentication: 'none',
    rateLimit: { requests: 100, window: '1m' },
  },
  {
    path: '/games/{gameId}',
    method: 'GET',
    summary: 'Get game details',
    description: 'Retrieve detailed information about a specific game',
    tags: ['Games'],
    parameters: [
      {
        name: 'gameId',
        in: 'path',
        required: true,
        schema: { type: 'string', description: 'Game ID' },
      },
    ],
    responses: {
      200: {
        description: 'Game details',
      },
      404: {
        description: 'Game not found',
      },
    },
    authentication: 'none',
  },
];

export const PLAYER_ENDPOINTS: APIEndpoint[] = [
  {
    path: '/players/me',
    method: 'GET',
    summary: 'Get current player profile',
    description: 'Retrieve the authenticated player profile',
    tags: ['Players'],
    responses: {
      200: {
        description: 'Player profile',
      },
      401: {
        description: 'Unauthorized',
      },
    },
    authentication: 'bearer',
  },
  {
    path: '/players/me/balance',
    method: 'GET',
    summary: 'Get player balance',
    description: 'Retrieve current player balance',
    tags: ['Players'],
    responses: {
      200: {
        description: 'Player balance',
      },
    },
    authentication: 'bearer',
  },
];

export const TOURNAMENT_ENDPOINTS: APIEndpoint[] = [
  {
    path: '/tournaments',
    method: 'GET',
    summary: 'List tournaments',
    description: 'Retrieve active and upcoming tournaments',
    tags: ['Tournaments'],
    responses: {
      200: {
        description: 'List of tournaments',
      },
    },
    authentication: 'none',
    rateLimit: { requests: 50, window: '1m' },
  },
  {
    path: '/tournaments/{tournamentId}/leaderboard',
    method: 'GET',
    summary: 'Get tournament leaderboard',
    description: 'Retrieve leaderboard for a specific tournament',
    tags: ['Tournaments'],
    parameters: [
      {
        name: 'tournamentId',
        in: 'path',
        required: true,
        schema: { type: 'string', description: 'Tournament ID' },
      },
    ],
    responses: {
      200: {
        description: 'Tournament leaderboard',
      },
    },
    authentication: 'none',
  },
];

/**
 * API versioning
 */
export interface APIVersion {
  version: string;
  releaseDate: Date;
  status: 'stable' | 'beta' | 'deprecated';
  deprecationDate?: Date;
  changes: string[];
  endpoints: APIEndpoint[];
}

export function createAPIVersion(
  version: string,
  status: 'stable' | 'beta' | 'deprecated',
  changes: string[],
  endpoints: APIEndpoint[]
): APIVersion {
  return {
    version,
    releaseDate: new Date(),
    status,
    changes,
    endpoints,
  };
}

/**
 * API usage statistics
 */
export interface APIUsageStats {
  totalRequests: number;
  successfulRequests: number;
  failedRequests: number;
  averageResponseTime: number;
  errorRate: number;
  topEndpoints: Array<{
    path: string;
    method: string;
    requests: number;
  }>;
  topErrors: Array<{
    status: number;
    message: string;
    count: number;
  }>;
}

export function calculateAPIUsageStats(
  totalRequests: number,
  successfulRequests: number,
  failedRequests: number,
  totalResponseTime: number,
  topEndpoints: Array<{ path: string; method: string; requests: number }>,
  topErrors: Array<{ status: number; message: string; count: number }>
): APIUsageStats {
  return {
    totalRequests,
    successfulRequests,
    failedRequests,
    averageResponseTime: totalResponseTime / totalRequests,
    errorRate: (failedRequests / totalRequests) * 100,
    topEndpoints: topEndpoints.slice(0, 10),
    topErrors: topErrors.slice(0, 10),
  };
}

/**
 * API client SDK generation
 */
export interface SDKConfig {
  language: 'javascript' | 'python' | 'go' | 'java' | 'csharp';
  packageName: string;
  version: string;
  baseUrl: string;
}

export function generateSDKCode(config: SDKConfig, endpoints: APIEndpoint[]): string {
  if (config.language === 'javascript') {
    return generateJavaScriptSDK(config, endpoints);
  } else if (config.language === 'python') {
    return generatePythonSDK(config, endpoints);
  }
  return '';
}

function generateJavaScriptSDK(config: SDKConfig, endpoints: APIEndpoint[]): string {
  let code = `/**
 * CoinKrazy API SDK - ${config.language}
 * Generated for version ${config.version}
 */

class CoinKrazyAPI {
  constructor(apiKey, baseUrl = '${config.baseUrl}') {
    this.apiKey = apiKey;
    this.baseUrl = baseUrl;
  }

  async request(method, path, data = null) {
    const options = {
      method,
      headers: {
        'Authorization': \`Bearer \${this.apiKey}\`,
        'Content-Type': 'application/json',
      },
    };

    if (data) {
      options.body = JSON.stringify(data);
    }

    const response = await fetch(\`\${this.baseUrl}\${path}\`, options);
    return response.json();
  }
`;

  endpoints.forEach((endpoint) => {
    const methodName = endpoint.summary
      .toLowerCase()
      .replace(/\s+/g, '_')
      .replace(/[^a-z0-9_]/g, '');

    code += `\n  async ${methodName}(${endpoint.parameters ? 'params' : ''}) {
    return this.request('${endpoint.method}', '${endpoint.path}', ${endpoint.requestBody ? 'params' : 'null'});
  }`;
  });

  code += '\n}\n';
  return code;
}

function generatePythonSDK(config: SDKConfig, endpoints: APIEndpoint[]): string {
  let code = `"""
CoinKrazy API SDK - Python
Generated for version ${config.version}
"""

import requests

class CoinKrazyAPI:
    def __init__(self, api_key, base_url='${config.baseUrl}'):
        self.api_key = api_key
        self.base_url = base_url
        self.headers = {
            'Authorization': f'Bearer {api_key}',
            'Content-Type': 'application/json',
        }

    def request(self, method, path, data=None):
        url = f'{self.base_url}{path}'
        response = requests.request(method, url, json=data, headers=self.headers)
        return response.json()
`;

  endpoints.forEach((endpoint) => {
    const methodName = endpoint.summary
      .lower()
      .replace(/\s+/g, '_')
      .replace(/[^a-z0-9_]/g, '');

    code += `\n    def ${methodName}(self, ${endpoint.parameters ? 'params=None' : ''}):
        return self.request('${endpoint.method}', '${endpoint.path}', ${endpoint.requestBody ? 'params' : 'None'})`;
  });

  code += '\n';
  return code;
}
