// Async Resource - Environment-agnostic async state management
//
// This module provides a callback-based async primitive that can be
// integrated with any async runtime (JS Promise, native async, etc.)
//

///|
/// Async state representation - environment independent
pub enum AsyncState[T] {
  Pending
  Success(T)
  Failure(String)
}

///|
/// Check if state is pending
pub fn[T] AsyncState::is_pending(self : AsyncState[T]) -> Bool {
  match self {
    Pending => true
    _ => false
  }
}

///|
/// Check if state is success
pub fn[T] AsyncState::is_success(self : AsyncState[T]) -> Bool {
  match self {
    Success(_) => true
    _ => false
  }
}

///|
/// Check if state is failure
pub fn[T] AsyncState::is_failure(self : AsyncState[T]) -> Bool {
  match self {
    Failure(_) => true
    _ => false
  }
}

///|
/// Get value if success, None otherwise
pub fn[T] AsyncState::value(self : AsyncState[T]) -> T? {
  match self {
    Success(v) => Some(v)
    _ => None
  }
}

///|
/// Get error if failure, None otherwise
pub fn[T] AsyncState::error(self : AsyncState[T]) -> String? {
  match self {
    Failure(e) => Some(e)
    _ => None
  }
}

///|
/// 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[AsyncState[T]]
  /// Trigger refetch
  priv refetch_ : () -> Unit
}

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

///|
/// Peek current state without tracking
pub fn[T] Resource::peek(self : Resource[T]) -> 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()
}

///|
/// 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[AsyncState[T]] = signal(Pending)
  let resolve_ = fn(value : T) { state.set(Success(value)) }
  let reject_ = fn(error : String) { state.set(Failure(error)) }
  fn do_fetch() {
    state.set(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[AsyncState[T]] = signal(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[AsyncState[T]] = signal(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[AsyncState[T]] = signal(Pending)
  let resolve = fn(value : T) { state.set(Success(value)) }
  let reject = fn(error : String) { state.set(Failure(error)) }
  let res : Resource[T] = { state, refetch_: fn() { state.set(Pending) } }
  (res, resolve, reject)
}