// cell verify [<scope>] - Verify integrity and consistency
//
// Usage:
//   cell verify             Verify current directory package
//   cell verify .           Verify current directory package
//   cell verify <locator>   Verify specific package
//   cell verify shop        Verify entire shop
//   cell verify world       Verify all world roots
//
// Options:
//   --deep                  Traverse full dependency closure
//   --target <triple>       Verify builds for specific target

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

var scope = null
var deep = false
var target_triple = null
var i = 0
var resolved = null

for (i = 0; i < length(args); i++) {
  if (args[i] == '--deep') {
    deep = true
  } else 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] == '--help' || args[i] == '-h') {
    log.console("Usage: cell verify [<scope>] [options]")
    log.console("")
    log.console("Verify integrity and consistency.")
    log.console("")
    log.console("Scopes:")
    log.console("  <locator>  Verify specific package")
    log.console("  shop       Verify entire shop")
    log.console("  world      Verify all world roots")
    log.console("")
    log.console("Options:")
    log.console("  --deep             Traverse full dependency closure")
    log.console("  --target <triple>  Verify builds for specific target")
    $stop()
  } else if (!starts_with(args[i], '-')) {
    scope = args[i]
  }
}

// Default to current directory
if (!scope) {
  scope = '.'
}

// Detect target if not specified
if (!target_triple) {
  target_triple = build.detect_host_target()
}

var errors = []
var warnings = []
var checked = 0

function add_error(msg) {
  push(errors, msg)
}

function add_warning(msg) {
  push(warnings, msg)
}

// Verify a single package
function verify_package(locator) {
  var lock = null
  var lock_entry = null
  var links = null
  var link_target = null
  var pkg_dir = null
  var dir_exists = false
  var current_target = null
  var expected_target = null
  var target_dir = null
  var lib_dir = null
  var lib_name = null
  var dylib_ext = null
  var lib_path = null
  var c_files = null

  checked++

  lock = shop.load_lock()
  lock_entry = lock[locator]
  links = link.load()
  link_target = links[locator]

  // Check lock entry exists
  if (!lock_entry) {
    add_error(locator + ": not in lock")
  }

  // Check package directory exists
  pkg_dir = shop.get_package_dir(locator)
  dir_exists = fd.is_dir(pkg_dir) || fd.is_link(pkg_dir)

  if (!dir_exists) {
    add_error(locator + ": package directory missing at " + pkg_dir)
    return
  }

  // Check cell.toml exists
  if (!fd.is_file(pkg_dir + '/cell.toml')) {
    add_error(locator + ": missing cell.toml")
    return
  }

  // For linked packages, verify link target
  if (link_target) {
    if (starts_with(link_target, '/')) {
      // Local path target
      if (!fd.is_dir(link_target)) {
        add_error(locator + ": link target does not exist: " + link_target)
      } else if (!fd.is_file(link_target + '/cell.toml')) {
        add_error(locator + ": link target is not a valid package: " + link_target)
      }
    } else {
      // Package target
      target_dir = shop.get_package_dir(link_target)
      if (!fd.is_dir(target_dir) && !fd.is_link(target_dir)) {
        add_error(locator + ": link target package not found: " + link_target)
      }
    }

    // Check symlink is correct
    if (fd.is_link(pkg_dir)) {
      current_target = fd.readlink(pkg_dir)
      expected_target = starts_with(link_target, '/') ? link_target : shop.get_package_dir(link_target)
      if (current_target != expected_target) {
        add_warning(locator + ": symlink target mismatch (expected " + expected_target + ", got " + current_target + ")")
      }
    } else {
      add_warning(locator + ": linked but directory is not a symlink")
    }
  }

  // Check build output exists
  lib_dir = shop.get_lib_dir()
  lib_name = shop.lib_name_for_package(locator)
  dylib_ext = '.dylib' // TODO: detect from target
  lib_path = lib_dir + '/' + lib_name + dylib_ext

  // Only check for builds if package has C files
  var _check_build = function() {
    c_files = pkg.get_c_files(locator, target_triple, true)
    if (c_files && length(c_files) > 0) {
      if (!fd.is_file(lib_path)) {
        add_warning(locator + ": library not built at " + lib_path)
      }
    }
  } disruption {
    // Skip build check if can't determine C files
  }
  _check_build()
}

// Check for link cycles
function check_link_cycles() {
  var links = link.load()

  function follow_chain(origin, visited) {
    var target = null
    if (visited[origin]) {
      return origin // cycle detected
    }
    visited[origin] = true

    target = links[origin]
    if (target && links[target]) {
      return follow_chain(target, visited)
    }
    return null
  }

  arrfor(array(links), function(origin) {
    var cycle_start = follow_chain(origin, {})
    if (cycle_start) {
      add_error("Link cycle detected starting from: " + origin)
    }
  })
}

// Check for dangling links
function check_dangling_links() {
  var links = link.load()

  arrfor(array(links), function(origin) {
    var target = links[origin]
    if (starts_with(target, '/')) {
      if (!fd.is_dir(target)) {
        add_error("Dangling link: " + origin + " -> " + target + " (target does not exist)")
      }
    }
  })
}

// Gather packages to verify
var packages_to_verify = []
var locator = null
var all_deps = null

if (scope == 'shop') {
  packages_to_verify = shop.list_packages()
} else if (scope == 'world') {
  // For now, world is the same as shop
  packages_to_verify = shop.list_packages()
} else {
  // Single package
  locator = scope

  // Resolve local paths
  if (locator == '.' || starts_with(locator, './') || starts_with(locator, '../') || fd.is_dir(locator)) {
    resolved = fd.realpath(locator)
    if (resolved) {
      locator = resolved
    }
  }

  if (deep) {
    // Gather all dependencies
    all_deps = pkg.gather_dependencies(locator)
    push(packages_to_verify, locator)
    arrfor(all_deps, function(dep) {
      push(packages_to_verify, dep)
    })
  } else {
    push(packages_to_verify, locator)
  }
}

log.console("Verifying " + text(length(packages_to_verify)) + " package(s)...")
log.console("")

// Run verification
check_link_cycles()
check_dangling_links()

arrfor(packages_to_verify, function(p) {
  if (p == 'core') return
  verify_package(p)
})

// Print results
if (length(warnings) > 0) {
  log.console("Warnings:")
  arrfor(warnings, function(w) {
    log.console("  " + w)
  })
  log.console("")
}

if (length(errors) > 0) {
  log.console("Errors:")
  arrfor(errors, function(e) {
    log.console("  " + e)
  })
  log.console("")
  log.console("Verification FAILED: " + text(length(errors)) + " error(s), " + text(length(warnings)) + " warning(s)")
} else {
  log.console("Verification PASSED: " + text(checked) + " package(s) checked, " + text(length(warnings)) + " warning(s)")
}

$stop()
