// analyze.cm — Static analysis over index data.
//
// All functions take an index object (from index.cm) and return structured results.
// Does not depend on streamline — operates purely on source-semantic data.

var analyze = {}

// Find all references to a name, with optional scope filter.
// scope: "top" (enclosing == null), "fn" (enclosing != null), null (all)
analyze.find_refs = function(idx, name, scope) {
  var hits = []
  var i = 0
  var ref = null
  while (i < length(idx.references)) {
    ref = idx.references[i]
    if (ref.name == name) {
      if (scope == null) {
        hits[] = ref
      } else if (scope == "top" && ref.enclosing == null) {
        hits[] = ref
      } else if (scope == "fn" && ref.enclosing != null) {
        hits[] = ref
      }
    }
    i = i + 1
  }
  return hits
}

// Find all <name>.<property> usage patterns (channel analysis).
// Only counts unshadowed uses (name not declared as local var in scope).
analyze.channels = function(idx, name) {
  var channels = {}
  var summary = {}
  var i = 0
  var cs = null
  var callee = null
  var prop = null
  var prefix_dot = name + "."
  while (i < length(idx.call_sites)) {
    cs = idx.call_sites[i]
    callee = cs.callee
    if (callee != null && starts_with(callee, prefix_dot)) {
      prop = text(callee, length(prefix_dot), length(callee))
      if (channels[prop] == null) {
        channels[prop] = []
      }
      channels[prop][] = {span: cs.span}
      if (summary[prop] == null) {
        summary[prop] = 0
      }
      summary[prop] = summary[prop] + 1
    }
    i = i + 1
  }
  return {channels: channels, summary: summary}
}

// Find declarations by name, with optional kind filter.
// kind: "var", "def", "fn", "param", or null (any)
analyze.find_decls = function(idx, name, kind) {
  var hits = []
  var i = 0
  var sym = null
  while (i < length(idx.symbols)) {
    sym = idx.symbols[i]
    if (sym.name == name) {
      if (kind == null || sym.kind == kind) {
        hits[] = sym
      }
    }
    i = i + 1
  }
  return hits
}

// Find intrinsic usage by name.
analyze.find_intrinsic = function(idx, name) {
  var hits = []
  var i = 0
  var ref = null
  if (idx.intrinsic_refs == null) return hits
  while (i < length(idx.intrinsic_refs)) {
    ref = idx.intrinsic_refs[i]
    if (ref.name == name) {
      hits[] = ref
    }
    i = i + 1
  }
  return hits
}

// Call sites with >4 args — always a compile error (max arity is 4).
analyze.excess_args = function(idx) {
  var hits = []
  var i = 0
  var cs = null
  while (i < length(idx.call_sites)) {
    cs = idx.call_sites[i]
    if (cs.args_count > 4) {
      hits[] = {span: cs.span, callee: cs.callee, args_count: cs.args_count}
    }
    i = i + 1
  }
  return hits
}

// Extract module export shape from index data (for cross-module analysis).
analyze.module_summary = function(idx) {
  var exports = {}
  var i = 0
  var j = 0
  var exp = null
  var sym = null
  var found = false
  if (idx.exports == null) return {exports: exports}
  while (i < length(idx.exports)) {
    exp = idx.exports[i]
    found = false
    if (exp.symbol_id != null) {
      j = 0
      while (j < length(idx.symbols)) {
        sym = idx.symbols[j]
        if (sym.symbol_id == exp.symbol_id) {
          if (sym.kind == "fn" && sym.params != null) {
            exports[exp.name] = {type: "function", arity: length(sym.params)}
          } else {
            exports[exp.name] = {type: sym.kind}
          }
          found = true
          break
        }
        j = j + 1
      }
    }
    if (!found) {
      exports[exp.name] = {type: "unknown"}
    }
    i = i + 1
  }
  return {exports: exports}
}

return analyze
