/**
 * API Key Management Service
 *
 * Handles secure storage, retrieval, and rotation of user API keys
 * with AES-256-GCM encryption.
 */

import { prisma } from '@/lib/db';
import { encrypt, decrypt } from '@/lib/encryption';
import { logger } from '@/lib/logger';

export interface CreateAPIKeyRequest {
  service: string;
  keyName: string;
  apiKey: string;
  expiresAt?: Date;
}

export interface APIKeyResponse {
  id: string;
  service: string;
  keyName: string;
  isActive: boolean;
  expiresAt?: Date;
  createdAt: Date;
  lastUsedAt?: Date;
}

export class APIKeyManagementService {
  /**
   * Create and securely store an API key
   * Returns the plaintext key ONCE for the user to save
   */
  async createAPIKey(
    userId: string,
    data: CreateAPIKeyRequest
  ): Promise<{ key: APIKeyResponse; plaintextKey: string }> {
    try {
      // Encrypt the API key before storage
      const encryptedKey = encrypt(data.apiKey);

      // Save to Prisma database
      const apiKey = await prisma.userAPIKey.create({
        data: {
          userId,
          service: data.service,
          keyName: data.keyName,
          encryptedKey,
          expiresAt: data.expiresAt,
          isActive: true,
        },
      });

      return {
        key: this.toAPIKeyResponse(apiKey),
        plaintextKey: data.apiKey, // Return plaintext ONCE
      };
    } catch (error: any) {
      // Handle unique constraint violation
      if (error.code === 'P2002') {
        throw new Error(
          `An API key with the same name already exists for this service`
        );
      }
      throw error;
    }
  }

  /**
   * Get all API keys for a user
   * Does NOT include encrypted keys in response
   */
  async getUserAPIKeys(userId: string): Promise<APIKeyResponse[]> {
    const apiKeys = await prisma.userAPIKey.findMany({
      where: { userId },
      orderBy: { createdAt: 'desc' },
    });

    return apiKeys.map(key => this.toAPIKeyResponse(key));
  }

  /**
   * Get decrypted API key for use in workflows
   * Updates lastUsedAt timestamp
   * Returns null for expired or inactive keys
   */
  async getDecryptedAPIKey(
    userId: string,
    service: string
  ): Promise<string | null> {
    const apiKey = await prisma.userAPIKey.findFirst({
      where: {
        userId,
        service,
        isActive: true,
      },
      orderBy: { createdAt: 'desc' }, // Get most recent if multiple
    });

    if (!apiKey) {
      return null;
    }

    // Check if key is expired
    if (this.isKeyExpired(apiKey)) {
      return null;
    }

    // Update lastUsedAt timestamp (non-blocking)
    prisma.userAPIKey.update({
      where: { id: apiKey.id },
      data: { lastUsedAt: new Date() },
    }).catch(error => {
      logger.error('Failed to update lastUsedAt for API key', {
        error,
        keyId: apiKey.id,
      });
    });

    // Decrypt and return the key
    try {
      return decrypt(apiKey.encryptedKey);
    } catch (error) {
      logger.error('Failed to decrypt API key', {
        error,
        keyId: apiKey.id,
      });
      throw new Error('Failed to decrypt API key');
    }
  }

  /**
   * Rotate an API key (update with new encrypted value)
   */
  async rotateAPIKey(
    keyId: string,
    userId: string,
    newApiKey: string
  ): Promise<APIKeyResponse> {
    // Verify ownership
    const existingKey = await prisma.userAPIKey.findUnique({
      where: { id: keyId },
    });

    if (!existingKey || existingKey.userId !== userId) {
      throw new Error('API key not found or access denied');
    }

    // Encrypt new key
    const encryptedKey = encrypt(newApiKey);

    // Update in Prisma
    const updatedKey = await prisma.userAPIKey.update({
      where: { id: keyId },
      data: { encryptedKey },
    });

    return this.toAPIKeyResponse(updatedKey);
  }

  /**
   * Deactivate an API key (soft delete)
   */
  async deactivateAPIKey(keyId: string, userId: string): Promise<void> {
    // Verify ownership
    const existingKey = await prisma.userAPIKey.findUnique({
      where: { id: keyId },
    });

    if (!existingKey || existingKey.userId !== userId) {
      throw new Error('API key not found or access denied');
    }

    // Deactivate in Prisma
    await prisma.userAPIKey.update({
      where: { id: keyId },
      data: { isActive: false },
    });

  }

  /**
   * Delete an API key permanently
   */
  async deleteAPIKey(keyId: string, userId: string): Promise<void> {
    // Verify ownership
    const existingKey = await prisma.userAPIKey.findUnique({
      where: { id: keyId },
    });

    if (!existingKey || existingKey.userId !== userId) {
      throw new Error('API key not found or access denied');
    }

    // Delete from Prisma
    await prisma.userAPIKey.delete({
      where: { id: keyId },
    });

  }

  /**
   * Check if a key is expired
   */
  isKeyExpired(key: { expiresAt?: Date | null }): boolean {
    if (!key.expiresAt) {
      return false; // No expiration date = never expires
    }
    return new Date() > key.expiresAt;
  }

  /**
   * Convert database model to safe response
   * Does NOT include encrypted key
   */
  private toAPIKeyResponse(key: any): APIKeyResponse {
    return {
      id: key.id,
      service: key.service,
      keyName: key.keyName,
      isActive: key.isActive,
      expiresAt: key.expiresAt || undefined,
      createdAt: key.createdAt,
      lastUsedAt: key.lastUsedAt || undefined,
    };
  }
}

// Export singleton instance
export const apiKeyManagementService = new APIKeyManagementService();
