/**
 * API Key management for Synor public API.
 *
 * Keys are stored in Redis with the format:
 * apikey:{key} -> { tier, name, createdAt, lastUsed }
 */

// Use require for ioredis to avoid ESM import issues
import type { Redis } from 'ioredis';
import IORedisModule from 'ioredis';
// eslint-disable-next-line @typescript-eslint/no-explicit-any
const IORedis = (IORedisModule as any).default || IORedisModule;
import { v4 as uuidv4 } from 'uuid';
import type { RateLimitTier } from './rate-limiter.js';

export interface ApiKey {
  key: string;
  tier: RateLimitTier;
  name: string;
  createdAt: number;
  lastUsed: number;
  requestCount: number;
}

export interface CreateApiKeyOptions {
  tier: RateLimitTier;
  name: string;
}

export class ApiKeyManager {
  private redis: Redis;

  constructor(redisUrl: string) {
    this.redis = new IORedis(redisUrl);
  }

  /**
   * Generate a new API key.
   */
  async createKey(options: CreateApiKeyOptions): Promise<ApiKey> {
    const key = `sk_${options.tier}_${uuidv4().replace(/-/g, '')}`;
    const now = Date.now();

    const apiKey: ApiKey = {
      key,
      tier: options.tier,
      name: options.name,
      createdAt: now,
      lastUsed: 0,
      requestCount: 0,
    };

    await this.redis.set(
      `apikey:${key}`,
      JSON.stringify(apiKey),
      'EX',
      365 * 24 * 60 * 60 // 1 year expiry
    );

    return apiKey;
  }

  /**
   * Validate an API key and return its metadata.
   */
  async validateKey(key: string): Promise<ApiKey | null> {
    const data = await this.redis.get(`apikey:${key}`);
    if (!data) return null;

    const apiKey = JSON.parse(data) as ApiKey;

    // Update last used timestamp
    const pipeline = this.redis.pipeline();
    pipeline.hset(`apikey:${key}:stats`, 'lastUsed', Date.now());
    pipeline.hincrby(`apikey:${key}:stats`, 'requestCount', 1);
    await pipeline.exec();

    return apiKey;
  }

  /**
   * Get API key with usage statistics.
   */
  async getKeyWithStats(key: string): Promise<(ApiKey & { stats: {
    requestsToday: number;
    requestsThisMonth: number;
  } }) | null> {
    const data = await this.redis.get(`apikey:${key}`);
    if (!data) return null;

    const apiKey = JSON.parse(data) as ApiKey;
    const stats = await this.redis.hgetall(`apikey:${key}:stats`);

    return {
      ...apiKey,
      lastUsed: parseInt(stats.lastUsed || '0'),
      requestCount: parseInt(stats.requestCount || '0'),
      stats: {
        requestsToday: parseInt(stats.requestsToday || '0'),
        requestsThisMonth: parseInt(stats.requestsThisMonth || '0'),
      },
    };
  }

  /**
   * Revoke an API key.
   */
  async revokeKey(key: string): Promise<boolean> {
    const deleted = await this.redis.del(`apikey:${key}`);
    await this.redis.del(`apikey:${key}:stats`);
    return deleted > 0;
  }

  /**
   * List all API keys for an account (by prefix).
   */
  async listKeys(pattern: string = 'apikey:*'): Promise<string[]> {
    const keys = await this.redis.keys(pattern);
    return keys.map((k: string) => k.replace('apikey:', ''));
  }

  /**
   * Record usage for analytics.
   */
  async recordUsage(
    key: string,
    method: string,
    responseTime: number
  ): Promise<void> {
    const today = new Date().toISOString().split('T')[0];
    const month = today.substring(0, 7);

    const pipeline = this.redis.pipeline();

    // Per-key daily stats
    pipeline.hincrby(`usage:${key}:${today}`, 'requests', 1);
    pipeline.hincrby(`usage:${key}:${today}`, 'responseTime', responseTime);
    pipeline.expire(`usage:${key}:${today}`, 90 * 24 * 60 * 60); // 90 days

    // Per-key monthly stats
    pipeline.hincrby(`usage:${key}:${month}`, 'requests', 1);
    pipeline.expire(`usage:${key}:${month}`, 365 * 24 * 60 * 60); // 1 year

    // Method tracking
    pipeline.hincrby(`methods:${today}`, method, 1);
    pipeline.expire(`methods:${today}`, 90 * 24 * 60 * 60);

    // Global stats
    pipeline.hincrby('stats:global', 'totalRequests', 1);
    pipeline.lpush('stats:latency', responseTime);
    pipeline.ltrim('stats:latency', 0, 999); // Keep last 1000

    await pipeline.exec();
  }

  /**
   * Get usage analytics.
   */
  async getAnalytics(key: string): Promise<{
    today: { requests: number; avgResponseTime: number };
    thisMonth: { requests: number };
    methods: Record<string, number>;
  }> {
    const today = new Date().toISOString().split('T')[0];
    const month = today.substring(0, 7);

    const pipeline = this.redis.pipeline();
    pipeline.hgetall(`usage:${key}:${today}`);
    pipeline.hgetall(`usage:${key}:${month}`);
    pipeline.hgetall(`methods:${today}`);

    const results = await pipeline.exec();

    const dailyStats = (results?.[0]?.[1] as Record<string, string>) || {};
    const monthlyStats = (results?.[1]?.[1] as Record<string, string>) || {};
    const methodStats = (results?.[2]?.[1] as Record<string, string>) || {};

    const requests = parseInt(dailyStats.requests || '0');
    const totalTime = parseInt(dailyStats.responseTime || '0');

    return {
      today: {
        requests,
        avgResponseTime: requests > 0 ? Math.round(totalTime / requests) : 0,
      },
      thisMonth: {
        requests: parseInt(monthlyStats.requests || '0'),
      },
      methods: Object.fromEntries(
        Object.entries(methodStats).map(([k, v]) => [k, parseInt(v)])
      ),
    };
  }

  async close(): Promise<void> {
    await this.redis.quit();
  }
}
