var shop = use('shop')
var http = use('http')
var miniz = use('miniz')
var io = use('io')
var crypto = use('crypto')
var text = use('text')
var toml = use('toml')
var time = use('time')

var uses = {}

uses.download = function()
{
  var mods = shop.load_config().dependencies
  var cache_dir = '.cell/cache'
  var modules_dir = '.cell/modules'
  var lock_path = '.cell/lock.toml'
  
  // Ensure directories exist
  if (!io.exists(cache_dir))
    io.mkdir(cache_dir)
  if (!io.exists(modules_dir))
    io.mkdir(modules_dir)
  
  // Load or create lock file
  var lock = {}
  if (io.exists(lock_path)) {
    var lock_content = io.slurp(lock_path)
    lock = toml.decode(lock_content)
  }
  if (!lock.modules) lock.modules = {}
  
  for (var mod in mods) {
    var cache_path = cache_dir + '/' + mod + '.zip'
    var module_path = modules_dir + '/' + mod
    
    var zip
    var need_download = false
    var remote_commit = null
    
    // Check remote commit if this is a git repository
    var api_url = shop.get_api_url(mods[mod])
    if (api_url) {
      log.console(`${mod}: checking remote commit...`)
      try {
        var api_response = http.fetch(api_url)
        remote_commit = shop.extract_commit_hash(mods[mod], text(api_response))
        if (remote_commit) {
          log.console(`${mod}: remote commit = ${remote_commit}`)
        }
      } catch (e) {
        log.console(`${mod}: failed to check remote commit`)
      }
    }
    
    // Check if module exists in lock file
    if (!lock.modules[mod] || !lock.modules[mod].hash) {
      log.console(`${mod}: not in lock file, will download`)
      need_download = true
    } else if (!io.exists(cache_path)) {
      log.console(`${mod}: cache missing, will download`)
      need_download = true
    } else if (remote_commit && (!lock.modules[mod].commit || lock.modules[mod].commit != remote_commit)) {
      log.console(`${mod}: remote has new commit`)
      log.console(`  local:  ${lock.modules[mod].commit || 'unknown'}`)
      log.console(`  remote: ${remote_commit}`)
      need_download = true
    }
    
    if (!need_download) {
      // Verify cached file hash
      log.console(`${mod}: verifying cached version`)
      zip = io.slurpbytes(cache_path)
      var hash = crypto.hash(zip)
      var hash_b32 = text(hash, "t")
      
      if (hash_b32 != lock.modules[mod].hash) {
        log.console(`${mod}: hash mismatch, will redownload`)
        log.console(`  expected: ${lock.modules[mod].hash}`)
        log.console(`  actual:   ${hash_b32}`)
        need_download = true
      } else {
        log.console(`${mod}: hash verified`)
      }
    }
    
    if (need_download) {
      // Download the module
      log.console(`downloading ${mod} at ${mods[mod]}`)
      log.console(shop.get_download_url(mods[mod]))
      zip = http.fetch(shop.get_download_url(mods[mod]))
      io.slurpwrite(cache_path, zip)
      log.console(`${mod}: downloaded ${zip.length} bytes`)
      
      // Calculate and store hash
      var hash = crypto.hash(zip)
      var hash_b32 = text(hash, "t")
      lock.modules[mod] = {
        hash: hash_b32,
        url: mods[mod],
        downloaded: time.text()
      }
      
      // Store commit hash if available
      if (remote_commit) {
        lock.modules[mod].commit = remote_commit
      }
      
      log.console(`${mod}: hash = ${hash_b32}`)
      
      // Save updated lock file
      io.slurpwrite(lock_path, toml.encode(lock))
    }
    
    // Extract the module
    var reader = miniz.read(zip)
    var count = reader.count()
    
    log.console(`extracting ${mod} (${count} files)...`)
    
    // Remove existing module directory if it exists (for clean updates)
    if (io.exists(module_path)) {
      log.console(`${mod}: removing old version...`)
      io.rmdir(module_path)
    }
    
    // Create module directory
    io.mkdir(module_path)
    
    // Extract each file
    for (var i = 0; i < count; i++) {
      if (reader.is_directory(i))
        continue
        
      var filename = reader.get_filename(i)
      
      // Strip the module name prefix if present
      var prefix = mod + '/'
      if (filename.indexOf(prefix) == 0)
        filename = filename.substring(prefix.length)
      
      // Skip if filename is empty after stripping
      if (!filename)
        continue
        
      var filepath = module_path + '/' + filename
      
      // Create subdirectories if needed
      var parts = filename.split('/')
      if (parts.length > 1) {
        var dir = module_path
        for (var j = 0; j < parts.length - 1; j++) {
          dir = dir + '/' + parts[j]
          if (!io.exists(dir))
            io.mkdir(dir)
        }
      }
      
      // Extract and write file
      var data = reader.slurp(reader.get_filename(i))
      io.slurpwrite(filepath, data)
    }
    
    log.console(`${mod}: extracted to ${module_path}`)
  }
}

if (uses[arg[0]])
  uses[arg[0]]()
else
  console.log(`Command ${arg[0]} not understood.`)