// Resource - Signal-based async value container
//
// Uses @ui.AsyncState for state representation

///|
/// Resource - Async value container with Signal integration
/// Environment-agnostic: uses callbacks instead of Promise/async directly
pub struct Resource[T] {
  /// The reactive state
  state : Signal[@ui.AsyncState[T]]
  /// Trigger refetch
  priv refetch_ : () -> Unit
}

///|
/// Get current async state (reactive)
pub fn[T] Resource::get(self : Resource[T]) -> @ui.AsyncState[T] {
  self.state.get()
}

///|
/// Peek current state without tracking
pub fn[T] Resource::peek(self : Resource[T]) -> @ui.AsyncState[T] {
  self.state.peek()
}

///|
/// Check if currently pending
pub fn[T] Resource::is_pending(self : Resource[T]) -> Bool {
  self.state.peek().is_pending()
}

///|
/// Check if successfully loaded
pub fn[T] Resource::is_success(self : Resource[T]) -> Bool {
  self.state.peek().is_success()
}

///|
/// Check if failed
pub fn[T] Resource::is_failure(self : Resource[T]) -> Bool {
  self.state.peek().is_failure()
}

///|
/// Get value if success
pub fn[T] Resource::value(self : Resource[T]) -> T? {
  self.state.peek().value()
}

///|
/// Get error if failure
pub fn[T] Resource::error(self : Resource[T]) -> String? {
  self.state.peek().error()
}

///|
/// Returns a reactive getter that tracks whether this resource is pending.
/// Unlike is_pending() which uses peek(), this uses get() for dependency tracking.
pub fn[T] Resource::pending(self : Resource[T]) -> () -> Bool {
  fn() { self.state.get().is_pending() }
}

///|
/// Trigger refetch
pub fn[T] Resource::refetch(self : Resource[T]) -> Unit {
  (self.refetch_)()
}

///|
/// Create a Resource from a callback-based fetcher
/// The fetcher receives (resolve, reject) callbacks
/// This is environment-agnostic - the actual async execution
/// is handled by the caller
pub fn[T] resource(
  fetcher : ((T) -> Unit, (String) -> Unit) -> Unit,
) -> Resource[T] {
  let state : Signal[@ui.AsyncState[T]] = signal(@ui.Pending)
  let resolve_ = fn(value : T) { state.set(@ui.Success(value)) }
  let reject_ = fn(error : String) { state.set(@ui.Failure(error)) }
  fn do_fetch() {
    state.set(@ui.Pending)
    fetcher(resolve_, reject_)
  }

  // Initial fetch
  do_fetch()
  { state, refetch_: do_fetch }
}

///|
/// Create a Resource with initial value (already resolved)
pub fn[T] resource_resolved(value : T) -> Resource[T] {
  let state : Signal[@ui.AsyncState[T]] = signal(@ui.Success(value))
  { state, refetch_: fn() { () } } // No-op for pre-resolved
}

///|
/// Create a Resource with initial error (already rejected)
pub fn[T] resource_rejected(error : String) -> Resource[T] {
  let state : Signal[@ui.AsyncState[T]] = signal(@ui.Failure(error))
  { state, refetch_: fn() { () } } // No-op for pre-rejected
}

///|
/// Create a deferred Resource (starts pending, resolve/reject manually)
pub fn[T] deferred() -> (Resource[T], (T) -> Unit, (String) -> Unit) {
  let state : Signal[@ui.AsyncState[T]] = signal(@ui.Pending)
  let resolve = fn(value : T) { state.set(@ui.Success(value)) }
  let reject = fn(error : String) { state.set(@ui.Failure(error)) }
  let res : Resource[T] = { state, refetch_: fn() { state.set(@ui.Pending) } }
  (res, resolve, reject)
}