import { readFileSync, existsSync } from 'fs'
import { homedir } from 'os'
import { join } from 'path'
import yaml from 'js-yaml'

export class FCPClient {
  constructor() {
    // Try environment variables first
    this.apiKey = process.env.FCP_API_KEY
    this.project = process.env.FCP_PROJECT
    
    // If not in env, try reading Flow config
    if (!this.apiKey || !this.project) {
      const config = this._readFlowConfig()
      if (config) {
        this.apiKey = this.apiKey || config.api_key
        this.project = this.project || config.project
      }
    }
    
    // Validate we have required config
    if (!this.apiKey) {
      throw new Error(
        'No API key found. Run \'flow init\' or set FCP_API_KEY\n' +
        'Note: If you\'ve already run \'flow init\', the API key may be in your system keychain.\n' +
        'Set FCP_API_KEY environment variable to use it with mithril.'
      )
    }
    if (!this.project) throw new Error('No project found. Run \'flow init\' or set FCP_PROJECT')
    
    this.baseUrl = process.env.FCP_API_URL || 'https://api.mlfoundry.com/v2'
    this.retries = process.env.NODE_ENV === 'test' ? 0 : 3
  }
  
  _readFlowConfig() {
    // Read Flow SDK configuration if available
    const configPath = join(homedir(), '.flow', 'config.yaml')
    if (!existsSync(configPath)) {
      return null
    }
    
    try {
      const content = readFileSync(configPath, 'utf8')
      return yaml.load(content) || {}
    } catch (error) {
      // If YAML parsing fails, return null
      return null
    }
  }
  
  async _request(method, path, options = {}) {
    const url = `${this.baseUrl}${path}`
    let lastError
    
    for (let attempt = 0; attempt <= this.retries; attempt++) {
      try {
        const response = await fetch(url, {
          method,
          headers: {
            'Authorization': `Bearer ${this.apiKey}`,
            'Content-Type': 'application/json',
            ...options.headers
          },
          body: options.body ? JSON.stringify(options.body) : undefined,
          signal: AbortSignal.timeout(30000)
        })
        
        if (!response.ok) {
          const error = new Error(`HTTP ${response.status}`)
          error.status = response.status
          // Parse error body if JSON
          try {
            if (response.headers.get('content-type')?.includes('application/json')) {
              const body = await response.json()
              error.response = { body }
            }
          } catch (e) {
            // Ignore JSON parse errors
          }
          throw error
        }
        
        return response.headers.get('content-type')?.includes('application/json') 
          ? await response.json()
          : await response.text()
      } catch (error) {
        lastError = error
        if (attempt < this.retries) {
          await new Promise(resolve => setTimeout(resolve, 1000 * (attempt + 1)))
        }
      }
    }
    
    throw lastError
  }
  
  async listAvailable() {
    // Query spot market availability
    const response = await this._request('GET', '/spot/availability')
    const auctions = response.auctions || []
    return auctions.map(a => ({
      type: a.instance_type,
      region: a.region,
      gpu: a.gpu_count || 0,
      price: `$${a.price.toFixed(2)}`,
      available: a.quantity
    }))
  }
  
  async createInstance(type, { region } = {}) {
    // Load SSH public key
    const sshKeyPath = process.env.FCP_SSH_KEY || join(homedir(), '.ssh', 'id_rsa.pub')
    const publicKey = readFileSync(sshKeyPath, 'utf8').trim()
    
    // Register SSH key with project
    const sshKeyId = await this._ensureSSHKey(publicKey)
    
    // Match instance type to available auctions
    const availabilityResponse = await this._request('GET', '/spot/availability')
    const auctions = availabilityResponse.auctions || []
    let candidates = auctions.filter(a => a.instance_type === type)
    
    if (region) {
      candidates = candidates.filter(a => a.region === region)
    }
    
    if (candidates.length === 0) {
      throw new Error(`No availability for ${type}${region ? ` in ${region}` : ''}`)
    }
    
    // Execute spot bid
    const auction = candidates[0]
    const { id } = await this._request('POST', '/spot/bids', {
      body: {
        project: this.project,
        auction: auction.id,
        launch_specification: {
          ssh_keys: [sshKeyId]
        }
      }
    })
    
    return id
  }
  
  async getInstance(id) {
    // Query bid and instance status
    try {
      const bid = await this._request('GET', `/spot/bids/${id}`)
      const instance = bid.instance || {}
      
      return {
        id,
        type: bid.instance_type || 'N/A',
        status: instance.status || bid.status || 'PENDING',
        region: bid.region || 'N/A',
        ip: instance.public_ip || null,
        created: bid.created_at
      }
    } catch (error) {
      if (error.status === 404) {
        return { id, status: 'NOT_FOUND' }
      }
      throw error
    }
  }
  
  async deleteInstance(id) {
    // Cancel bid and terminate instance
    try {
      await this._request('DELETE', `/spot/bids/${id}`)
      return true
    } catch (error) {
      if (error.status === 404) {
        return false
      }
      throw error
    }
  }
  
  async listMyInstances() {
    // List active bids for project
    const response = await this._request('GET', `/spot/bids?project=${this.project}`)
    const bids = response.bids || []
    
    return bids
      .filter(b => ['PENDING', 'RUNNING'].includes(b.status))
      .map(b => ({
        id: b.id,
        type: b.instance_type || 'N/A',
        status: b.status,
        region: b.region || 'N/A'
      }))
  }
  
  async _ensureSSHKey(publicKey) {
    // Query project SSH keys
    const response = await this._request('GET', `/projects/${this.project}/ssh-keys`)
    const ssh_keys = response.ssh_keys || []
    
    // Match by key fingerprint
    const keyFingerprint = publicKey.split(' ')[1]
    const existing = ssh_keys.find(k => k.public_key.includes(keyFingerprint))
    
    if (existing) return existing.id
    
    // Register new SSH key
    const { id } = await this._request('POST', `/projects/${this.project}/ssh-keys`, {
      body: {
        name: 'mithril-key',
        public_key: publicKey
      }
    })
    
    return id
  }
}