// cell install <locator> - Install a package to the shop
//
// Usage:
//   cell install <locator>   Install a package and its dependencies
//   cell install .           Install current directory package
//
// Options:
//   --target <triple>   Build for target platform
//   --refresh           Refresh floating refs before locking
//   --dry-run           Show what would be installed
//   -r                  Recursively find and install all packages in directory

var shop = use('internal/shop')
var build = use('build')
var pkg = use('package')
var fd = use('fd')

if (length(args) < 1) {
  log.console("Usage: cell install <locator> [options]")
  log.console("")
  log.console("Options:")
  log.console("  --target <triple>  Build for target platform")
  log.console("  --refresh          Refresh floating refs before locking")
  log.console("  --dry-run          Show what would be installed")
  log.console("  -r                 Recursively find and install all packages in directory")
  $stop()
}

var locator = null
var target_triple = null
var refresh = false
var dry_run = false
var recursive = false
var i = 0
var resolved = null
var locators = null

for (i = 0; i < length(args); i++) {
  if (args[i] == '--target' || args[i] == '-t') {
    if (i + 1 < length(args)) {
      target_triple = args[++i]
    } else {
      log.error('--target requires a triple')
      $stop()
    }
  } else if (args[i] == '--refresh') {
    refresh = true
  } else if (args[i] == '--dry-run') {
    dry_run = true
  } else if (args[i] == '-r') {
    recursive = true
  } else if (args[i] == '--help' || args[i] == '-h') {
    log.console("Usage: cell install <locator> [options]")
    log.console("")
    log.console("Install a package and its dependencies to the shop.")
    log.console("")
    log.console("Options:")
    log.console("  --target <triple>  Build for target platform")
    log.console("  --refresh          Refresh floating refs before locking")
    log.console("  --dry-run          Show what would be installed")
    log.console("  -r                 Recursively find and install all packages in directory")
    $stop()
  } else if (!starts_with(args[i], '-')) {
    locator = args[i]
  }
}

if (!locator && !recursive) {
  log.console("Usage: cell install <locator>")
  $stop()
}

// Resolve relative paths to absolute paths
// Local paths like '.' or '../foo' need to be converted to absolute paths
if (locator && (locator == '.' || starts_with(locator, './') || starts_with(locator, '../') || fd.is_dir(locator))) {
  resolved = fd.realpath(locator)
  if (resolved) {
    locator = resolved
  }
}

// Recursively find all cell packages in a directory
function find_packages(dir) {
  var found = []
  var list = fd.readdir(dir)
  if (!list) return found
  if (fd.is_file(dir + '/cell.toml')) {
    push(found, dir)
  }
  arrfor(list, function(item) {
    if (item == '.' || item == '..' || item == '.cell' || item == '.git') return
    var full = dir + '/' + item
    var st = fd.stat(full)
    var sub = null
    if (st && st.isDirectory) {
      sub = find_packages(full)
      arrfor(sub, function(p) {
        push(found, p)
      })
    }
  })
  return found
}

// If -r flag, find all packages recursively and install each
if (recursive) {
  if (!locator) {
    locator = '.'
  }
  resolved = fd.realpath(locator)
  if (!resolved || !fd.is_dir(resolved)) {
    log.error(`${locator} is not a directory`)
    $stop()
  }
  locators = find_packages(resolved)
  if (length(locators) == 0) {
    log.console("No packages found in " + resolved)
    $stop()
  }
  log.console(`Found ${text(length(locators))} package(s) in ${resolved}`)
}

// Default target
if (!target_triple) {
  target_triple = build.detect_host_target()
}

// Gather all packages that will be installed
var packages_to_install = []
var skipped_packages = []
var summary = null
var visited = {}

// Recursive mode: install all found packages and exit
if (recursive) {
  arrfor(locators, function(loc) {
    log.console("  Installing " + loc + "...")
    var _inst = function() {
      shop.update(loc)
      shop.extract(loc)
      shop.build_package_scripts(loc)
      var _build_c = function() {
        build.build_dynamic(loc, target_triple, 'release')
      } disruption {
        // Not all packages have C code
      }
      _build_c()
      push(packages_to_install, loc)
    } disruption {
      push(skipped_packages, loc)
      log.console(`  Warning: Failed to install ${loc}`)
    }
    _inst()
  })

  summary = "Installed " + text(length(packages_to_install)) + " package(s)."
  if (length(skipped_packages) > 0) {
    summary += " Failed: " + text(length(skipped_packages)) + "."
  }
  log.console(summary)
  $stop()
}

log.console("Installing " + locator + "...")

function gather_packages(pkg_locator) {
  var lock = null
  var update_result = null
  var deps = null
  if (visited[pkg_locator]) return
  visited[pkg_locator] = true

  // Check if this is a local path that doesn't exist
  if (starts_with(pkg_locator, '/') && !fd.is_dir(pkg_locator)) {
    push(skipped_packages, pkg_locator)
    log.console("  Skipping missing local package: " + pkg_locator)
    return
  }

  push(packages_to_install, pkg_locator)

  // Try to read dependencies
  var _gather = function() {
    // For packages not yet extracted, we need to update and extract first to read deps
    lock = shop.load_lock()
    if (!lock[pkg_locator]) {
      if (!dry_run) {
        update_result = shop.update(pkg_locator)
        if (update_result) {
          shop.extract(pkg_locator)
        } else {
          // Update failed - package might not be fetchable
          log.console("Warning: Could not fetch " + pkg_locator)
          return
        }
      }
    } else {
      // Package is in lock, ensure it's extracted
      if (!dry_run) {
        shop.extract(pkg_locator)
      }
    }

    deps = pkg.dependencies(pkg_locator)
    if (deps) {
      arrfor(array(deps), function(alias) {
        var dep_locator = deps[alias]
        gather_packages(dep_locator)
      })
    }
  } disruption {
    // Package might not have dependencies or cell.toml issue
    if (!dry_run) {
      log.console(`Warning: Could not read dependencies for ${pkg_locator}`)
    }
  }
  _gather()
}

// Gather all packages
gather_packages(locator)

if (dry_run) {
  log.console("Would install:")
  arrfor(packages_to_install, function(p) {
    var lock = shop.load_lock()
    var exists = lock[p] != null
    log.console("  " + p + (exists ? " (already installed)" : ""))
  })
  if (length(skipped_packages) > 0) {
    log.console("")
    log.console("Would skip (missing local paths):")
    arrfor(skipped_packages, function(p) {
      log.console("  " + p)
    })
  }
  $stop()
}

// Install each package
function install_package(pkg_locator) {
  // Update lock entry
  shop.update(pkg_locator)

  // Extract/symlink the package
  shop.extract(pkg_locator)

  // Build scripts
  shop.build_package_scripts(pkg_locator)

  // Build C code
  var _build_c = function() {
    build.build_dynamic(pkg_locator, target_triple, 'release')
  } disruption {
    // Not all packages have C code
  }
  _build_c()
}

arrfor(packages_to_install, function(p) {
  log.console("  Installing " + p + "...")
  install_package(p)
})

summary = "Installed " + text(length(packages_to_install)) + " package(s)."
if (length(skipped_packages) > 0) {
  summary += " Skipped " + text(length(skipped_packages)) + " missing local path(s)."
}
log.console(summary)

$stop()
