/**
 * Cloudflare KV Storage Utility Class
 * 
 * This utility class provides methods to interact with Cloudflare KV storage
 * for storing and retrieving key-value pairs.
 */

interface CloudflareKVConfig {
  apiKey: string
  baseUrl: string
  accountId: string
  kvNamespaceId: string
}

interface CloudflareKVResponse<T = any> {
  success: boolean
  errors: Array<{ code: number; message: string }>
  messages: string[]
  result: T
}

export class CloudflareKVClient {
  private config: CloudflareKVConfig

  constructor(config?: Partial<CloudflareKVConfig>) {
    this.config = {
      apiKey: config?.apiKey || "dlGquMNiAX-S7SV9pXne7YGfdH_fEgq3TfIGgNcQ",
      baseUrl: config?.baseUrl || "https://api.cloudflare.com/client/v4",
      accountId: config?.accountId || "67720b647ff2b55cf37ba3ef9e677083",
      kvNamespaceId: config?.kvNamespaceId || "995d7eff088547d7a7d8fea53e56115b"
    }
  }

  /**
   * Get the base URL for KV operations
   */
  private getKVBaseUrl(): string {
    return `${this.config.baseUrl}/accounts/${this.config.accountId}/storage/kv/namespaces/${this.config.kvNamespaceId}`
  }

  /**
   * Get common headers for API requests
   */
  private getHeaders(): HeadersInit {
    return {
      'Authorization': `Bearer ${this.config.apiKey}`,
      'Content-Type': 'application/json'
    }
  }

  /**
   * Store a value in Cloudflare KV
   * 
   * @param key - The key to store the value under
   * @param value - The value to store (will be JSON stringified if not a string)
   * @param metadata - Optional metadata to associate with the key-value pair
   * @returns Promise resolving to the API response
   */
  async put(key: string, value: any, metadata?: Record<string, any>): Promise<CloudflareKVResponse> {
    try {
      const url = `${this.getKVBaseUrl()}/values/${encodeURIComponent(key)}`
      console.log(`[CloudflareKV] PUT request to: ${url}`)

      const body: any = {
        value: typeof value === 'string' ? value : JSON.stringify(value)
      }

      if (metadata) {
        body.metadata = JSON.stringify(metadata)
      }

      console.log(`[CloudflareKV] PUT body:`, body)

      const response = await fetch(url, {
        method: 'PUT',
        headers: {
          'Authorization': `Bearer ${this.config.apiKey}`,
          'Content-Type': 'application/json'
        },
        body: JSON.stringify(body)
      })

      console.log(`[CloudflareKV] PUT response status: ${response.status}`)

      const result = await response.json()
      console.log(`[CloudflareKV] PUT response:`, result)

      if (!response.ok) {
        throw new Error(`HTTP ${response.status}: ${result.errors?.[0]?.message || 'Unknown error'}`)
      }

      return result
    } catch (error) {
      console.error('Error putting value to Cloudflare KV:', error)
      throw error
    }
  }

  /**
   * Retrieve a value from Cloudflare KV
   * 
   * @param key - The key to retrieve
   * @param parseJSON - Whether to attempt JSON parsing of the retrieved value
   * @returns Promise resolving to the stored value
   */
  async get<T = any>(key: string, parseJSON: boolean = true): Promise<T | string | null> {
    try {
      const url = `${this.getKVBaseUrl()}/values/${encodeURIComponent(key)}`
      
      const response = await fetch(url, {
        method: 'GET',
        headers: {
          'Authorization': `Bearer ${this.config.apiKey}`
        }
      })

      if (response.status === 404) {
        return null
      }

      if (!response.ok) {
        const errorData = await response.json()
        throw new Error(`HTTP ${response.status}: ${errorData.errors?.[0]?.message || 'Unknown error'}`)
      }

      const textValue = await response.text()

      if (parseJSON) {
        try {
          return JSON.parse(textValue) as T
        } catch {
          // If JSON parsing fails, return as string
          return textValue as any
        }
      }

      return textValue as any
    } catch (error) {
      console.error('Error getting value from Cloudflare KV:', error)
      throw error
    }
  }

  /**
   * Delete a key-value pair from Cloudflare KV
   * 
   * @param key - The key to delete
   * @returns Promise resolving to the API response
   */
  async delete(key: string): Promise<CloudflareKVResponse> {
    try {
      const url = `${this.getKVBaseUrl()}/values/${encodeURIComponent(key)}`
      
      const response = await fetch(url, {
        method: 'DELETE',
        headers: this.getHeaders()
      })

      const result = await response.json()
      
      if (!response.ok) {
        throw new Error(`HTTP ${response.status}: ${result.errors?.[0]?.message || 'Unknown error'}`)
      }

      return result
    } catch (error) {
      console.error('Error deleting value from Cloudflare KV:', error)
      throw error
    }
  }

  /**
   * List keys in the KV namespace
   * 
   * @param prefix - Optional prefix to filter keys
   * @param limit - Maximum number of keys to return (default: 1000)
   * @param cursor - Cursor for pagination
   * @returns Promise resolving to the list of keys
   */
  async listKeys(prefix?: string, limit: number = 1000, cursor?: string): Promise<CloudflareKVResponse<{
    keys: Array<{
      name: string
      expiration?: number
      metadata?: Record<string, any>
    }>
    list_complete: boolean
    cursor?: string
  }>> {
    try {
      const params = new URLSearchParams()
      if (prefix) params.append('prefix', prefix)
      if (limit) params.append('limit', limit.toString())
      if (cursor) params.append('cursor', cursor)

      const url = `${this.getKVBaseUrl()}/keys?${params.toString()}`
      
      const response = await fetch(url, {
        method: 'GET',
        headers: this.getHeaders()
      })

      const result = await response.json()
      
      if (!response.ok) {
        throw new Error(`HTTP ${response.status}: ${result.errors?.[0]?.message || 'Unknown error'}`)
      }

      return result
    } catch (error) {
      console.error('Error listing keys from Cloudflare KV:', error)
      throw error
    }
  }

  /**
   * Check if a key exists in the KV store
   * 
   * @param key - The key to check
   * @returns Promise resolving to boolean indicating if key exists
   */
  async exists(key: string): Promise<boolean> {
    try {
      const value = await this.get(key, false)
      return value !== null
    } catch (error) {
      console.error('Error checking key existence in Cloudflare KV:', error)
      return false
    }
  }

  /**
   * Store multiple key-value pairs in batch
   * 
   * @param entries - Array of key-value pairs to store
   * @returns Promise resolving to array of results
   */
  async putBatch(entries: Array<{ key: string; value: any; metadata?: Record<string, any> }>): Promise<CloudflareKVResponse[]> {
    const results = await Promise.allSettled(
      entries.map(entry => this.put(entry.key, entry.value, entry.metadata))
    )

    return results.map((result, index) => {
      if (result.status === 'fulfilled') {
        return result.value
      } else {
        console.error(`Failed to put key ${entries[index].key}:`, result.reason)
        return {
          success: false,
          errors: [{ code: -1, message: result.reason.message || 'Unknown error' }],
          messages: [],
          result: null
        }
      }
    })
  }
}

// Export a default instance with the default configuration
export const cloudflareKV = new CloudflareKVClient()

// Export types for external use
export type { CloudflareKVConfig, CloudflareKVResponse }
