/**
 * MinIO Storage Adapter
 * Implements the InfrastructureAdapter interface for MinIO (S3-compatible API)
 */

import { InfrastructureAdapter, NormalizedResource, ResourceSpec, NormalizedMetrics, TimeRange, HealthStatus, NormalizedRelationship } from '../types.js'
import { ResourceProvider } from '../../types/resource.js'
import { logger } from '../../lib/logger'

export class MinIOAdapter implements InfrastructureAdapter {
  readonly provider: ResourceProvider = 'MINIO'
  
  private config: {
    endpoint: string
    accessKey: string
    secretKey: string
    adminEndpoint?: string
  }

  constructor(config: { endpoint: string; accessKey: string; secretKey: string; adminEndpoint?: string }) {
    this.config = {
      ...config,
      adminEndpoint: config.adminEndpoint || config.endpoint.replace('/api', '/minio/admin'),
    }
  }

  private async makeS3Request(method: string, path: string, body?: string, headers: Record<string, string> = {}): Promise<Response> {
    const url = `${this.config.endpoint}${path}`
    const auth = Buffer.from(`${this.config.accessKey}:${this.config.secretKey}`).toString('base64')
    
    return fetch(url, {
      method,
      headers: {
        'Authorization': `Basic ${auth}`,
        'Content-Type': 'application/json',
        ...headers,
      },
      body,
    })
  }

  private async makeAdminRequest(method: string, path: string, body?: any): Promise<Response> {
    const url = `${this.config.adminEndpoint}${path}`
    const auth = Buffer.from(`${this.config.accessKey}:${this.config.secretKey}`).toString('base64')
    
    return fetch(url, {
      method,
      headers: {
        'Authorization': `Basic ${auth}`,
        'Content-Type': 'application/json',
      },
      body: body ? JSON.stringify(body) : undefined,
    })
  }

  async discoverResources(): Promise<NormalizedResource[]> {
    const resources: NormalizedResource[] = []
    
    try {
      // Use S3 API to list buckets
      const response = await this.makeS3Request('GET', '/')
      
      if (response.ok) {
        const xmlText = await response.text()
        // Parse XML response (simplified - in production use proper XML parser)
        const bucketMatches = xmlText.match(/<Name>([^<]+)<\/Name>/g) || []
        
        for (const match of bucketMatches) {
          const bucketName = match.replace(/<\/?Name>/g, '')
          const bucketResource = await this.getResource(bucketName)
          if (bucketResource) {
            resources.push(bucketResource)
          }
        }
      }

      // Also try MinIO Admin API for more detailed discovery
      try {
        const adminResponse = await this.makeAdminRequest('GET', '/v3/info')
        if (adminResponse.ok) {
          const adminData = await adminResponse.json()
          // Admin API provides additional information about the MinIO instance
        }
      } catch (error) {
        // Admin API not available, continue with S3 API
      }
    } catch (error) {
      logger.error('Error discovering MinIO resources', { error })
      throw error
    }

    return resources
  }

  async getResource(providerId: string): Promise<NormalizedResource | null> {
    try {
      // Get bucket metadata using S3 API
      const response = await this.makeS3Request('HEAD', `/${providerId}`)
      
      if (response.ok) {
        const metadata: Record<string, any> = {
          bucketName: providerId,
          creationDate: response.headers.get('x-amz-date') || new Date().toISOString(),
        }

        // Try to get additional info from MinIO Admin API
        try {
          const adminResponse = await this.makeAdminRequest('GET', `/v3/info/bucket?bucket=${providerId}`)
          if (adminResponse.ok) {
            const adminData = await adminResponse.json()
            metadata.size = adminData.size
            metadata.objectCount = adminData.objects
          }
        } catch (error) {
          // Admin API not available
        }

        return {
          id: `minio-bucket-${providerId}`,
          name: providerId,
          type: 'bucket',
          provider: 'MINIO',
          providerId: providerId,
          providerResourceId: `minio://buckets/${providerId}`,
          status: 'active',
          metadata,
          tags: [],
          createdAt: new Date(metadata.creationDate),
          updatedAt: new Date(),
        }
      }

      return null
    } catch (error) {
      logger.error(`Error getting MinIO resource ${providerId}`, { error, providerId })
      return null
    }
  }

  async createResource(spec: ResourceSpec): Promise<NormalizedResource> {
    try {
      if (spec.type !== 'bucket') {
        throw new Error(`Unsupported resource type: ${spec.type}. Only 'bucket' is supported.`)
      }

      const bucketName = spec.name
      
      // Create bucket using S3 API
      const response = await this.makeS3Request('PUT', `/${bucketName}`)
      
      if (!response.ok) {
        const errorText = await response.text()
        throw new Error(`Failed to create bucket: ${errorText}`)
      }

      // Apply configuration if provided
      if (spec.config.versioning) {
        await this.makeS3Request('PUT', `/${bucketName}?versioning`, 
          '<VersioningConfiguration xmlns="http://s3.amazonaws.com/doc/2006-03-01/"><Status>Enabled</Status></VersioningConfiguration>',
          { 'Content-Type': 'application/xml' }
        )
      }

      return await this.getResource(bucketName) || {
        id: `minio-bucket-${bucketName}`,
        name: bucketName,
        type: 'bucket',
        provider: 'MINIO',
        providerId: bucketName,
        providerResourceId: `minio://buckets/${bucketName}`,
        status: 'active',
        metadata: {},
        tags: spec.tags || [],
        createdAt: new Date(),
        updatedAt: new Date(),
      }
    } catch (error) {
      logger.error('Error creating MinIO resource', { error })
      throw error
    }
  }

  async updateResource(providerId: string, spec: Partial<ResourceSpec>): Promise<NormalizedResource> {
    try {
      const existing = await this.getResource(providerId)
      if (!existing) {
        throw new Error(`Resource ${providerId} not found`)
      }

      // Update versioning if specified
      if (spec.config?.versioning !== undefined) {
        const versioningStatus = spec.config.versioning ? 'Enabled' : 'Suspended'
        await this.makeS3Request('PUT', `/${providerId}?versioning`,
          `<VersioningConfiguration xmlns="http://s3.amazonaws.com/doc/2006-03-01/"><Status>${versioningStatus}</Status></VersioningConfiguration>`,
          { 'Content-Type': 'application/xml' }
        )
      }

      return await this.getResource(providerId) || existing
    } catch (error) {
      logger.error(`Error updating MinIO resource ${providerId}`, { error, providerId })
      throw error
    }
  }

  async deleteResource(providerId: string): Promise<boolean> {
    try {
      // Delete bucket (must be empty)
      const response = await this.makeS3Request('DELETE', `/${providerId}`)
      return response.ok || response.status === 204
    } catch (error) {
      logger.error(`Error deleting MinIO resource ${providerId}`, { error, providerId })
      return false
    }
  }

  async getMetrics(providerId: string, timeRange: TimeRange): Promise<NormalizedMetrics[]> {
    const metrics: NormalizedMetrics[] = []
    
    try {
      // Try MinIO Admin API for metrics
      try {
        const adminResponse = await this.makeAdminRequest('GET', `/v3/info/bucket?bucket=${providerId}`)
        if (adminResponse.ok) {
          const adminData = await adminResponse.json()
          
          if (adminData.size) {
            metrics.push({
              resourceId: providerId,
              metricType: 'STORAGE_IOPS',
              value: adminData.size,
              timestamp: new Date(),
              labels: { type: 'bucket_size' },
            })
          }

          if (adminData.objects) {
            metrics.push({
              resourceId: providerId,
              metricType: 'REQUEST_RATE',
              value: adminData.objects,
              timestamp: new Date(),
              labels: { type: 'object_count' },
            })
          }
        }
      } catch (error) {
        // Admin API not available, try S3 API
        const response = await this.makeS3Request('GET', `/${providerId}?stats`)
        if (response.ok) {
          const size = parseInt(response.headers.get('x-amz-bucket-size') || '0')
          const objectCount = parseInt(response.headers.get('x-amz-bucket-object-count') || '0')

          if (size > 0) {
            metrics.push({
              resourceId: providerId,
              metricType: 'STORAGE_IOPS',
              value: size,
              timestamp: new Date(),
              labels: { type: 'bucket_size' },
            })
          }

          if (objectCount > 0) {
            metrics.push({
              resourceId: providerId,
              metricType: 'REQUEST_RATE',
              value: objectCount,
              timestamp: new Date(),
              labels: { type: 'object_count' },
            })
          }
        }
      }
    } catch (error) {
      logger.error(`Error getting MinIO metrics for ${providerId}`, { error, providerId })
    }

    return metrics
  }

  async getRelationships(providerId: string): Promise<NormalizedRelationship[]> {
    const relationships: NormalizedRelationship[] = []
    
    try {
      // List objects in bucket
      const response = await this.makeS3Request('GET', `/${providerId}`)
      
      if (response.ok) {
        const xmlText = await response.text()
        // In a real implementation, you might track relationships between buckets
        // or replication relationships
      }
    } catch (error) {
      logger.error(`Error getting MinIO relationships for ${providerId}`, { error, providerId })
    }

    return relationships
  }

  async healthCheck(): Promise<HealthStatus> {
    try {
      // Check if we can list buckets (health check)
      const response = await this.makeS3Request('GET', '/')
      
      if (response.ok) {
        return {
          status: 'healthy',
          lastChecked: new Date(),
        }
      }

      return {
        status: 'unhealthy',
        message: `API returned status ${response.status}`,
        lastChecked: new Date(),
      }
    } catch (error) {
      return {
        status: 'unhealthy',
        message: error instanceof Error ? error.message : 'Unknown error',
        lastChecked: new Date(),
      }
    }
  }
}

