// Computed / Memo - derived reactive values
// Compatible with signal module API

///|
/// Create a memoized computed value (internal)
fn[T] create_computed(getter : () -> T) -> Computed[T] {
  { node: ReactiveNode::new(ReactiveFlags::None.to_int()), value: None, getter }
}

///|
/// Get the computed value
/// Automatically tracks dependency if called inside an effect/computed
pub fn[T] Computed::get(self : Computed[T]) -> T {
  let flags = self.node.flags.get_value()
  let dirty_flag = ReactiveFlags::Dirty.to_int()
  let pending_flag = ReactiveFlags::Pending.to_int()
  let mutable_flag = ReactiveFlags::Mutable.to_int()
  let recursed_check_flag = ReactiveFlags::RecursedCheck.to_int()

  // Check if dirty or pending (need to recompute)
  let is_dirty = (flags & dirty_flag) != 0
  let is_pending = (flags & pending_flag) != 0

  // Check if dependencies have changed
  let deps_changed = if is_pending {
    check_deps_dirty(self.node)
  } else {
    false
  }
  if is_dirty || deps_changed {
    // Recompute
    if update_computed(self) && self.node.subs is Some(subs) {
      shallow_propagate(subs)
    }
  } else if is_pending {
    // Clear pending flag if deps not dirty
    self.node.flags.set_value(flags & (pending_flag ^ -1))
  } else if flags == ReactiveFlags::None.to_int() {
    // First computation
    self.node.flags.set_value(mutable_flag | recursed_check_flag)
    let prev_sub = set_active_sub(Some(self.node))
    self.value = Some((self.getter)())
    let _ = set_active_sub(prev_sub)
    let current_flags = self.node.flags.get_value()
    self.node.flags.set_value(current_flags & (recursed_check_flag ^ -1))
  }

  // Track dependency
  if get_active_sub() is Some(sub) {
    link(self.node, sub, get_cycle())
  }
  match self.value {
    Some(v) => v
    None => panic()
  }
}

///|
/// Update computed value, returns true if changed
fn[T] update_computed(c : Computed[T]) -> Bool {
  inc_cycle()
  c.node.deps_tail = None
  c.node.flags.set_value(
    ReactiveFlags::Mutable.to_int() | ReactiveFlags::RecursedCheck.to_int(),
  )
  let prev_sub = set_active_sub(Some(c.node))
  let old_value = c.value
  let new_value = (c.getter)()
  c.value = Some(new_value)
  let _ = set_active_sub(prev_sub)
  let current_flags = c.node.flags.get_value()
  c.node.flags.set_value(
    current_flags & (ReactiveFlags::RecursedCheck.to_int() ^ -1),
  )
  purge_deps(c.node)
  match old_value {
    Some(old) => not(physical_equal(old, new_value))
    None => true
  }
}

///|
/// Check if any dependency has changed since last computation
/// Uses last_modified_cycle to handle diamond dependencies correctly
fn check_deps_dirty(node : ReactiveNode) -> Bool {
  let dirty_flag = ReactiveFlags::Dirty.to_int()
  let pending_flag = ReactiveFlags::Pending.to_int()
  let mutable_flag = ReactiveFlags::Mutable.to_int()
  let mut current_link = node.deps
  while current_link is Some(lnk) {
    let dep = lnk.dep
    let dep_flags = dep.flags.get_value()
    if (dep_flags & mutable_flag) != 0 {
      // Check if dep was modified after this link was created
      // This handles diamond dependencies where dirty flag may be cleared
      if dep.last_modified_cycle > lnk.version {
        return true
      }
      // Also check traditional dirty/pending flags
      if (dep_flags & (dirty_flag | pending_flag)) != 0 {
        return true
      }
    }
    current_link = lnk.next_dep
  }
  false
}

///|
/// Peek at current value without tracking
pub fn[T] Computed::peek(self : Computed[T]) -> T? {
  self.value
}