import app/web
import gleam/erlang/process
import gleam/otp/actor

pub const call_timeout = 1000

pub type Manager(resp_type, resp_error, msg_type) {
  Manager(subject: process.Subject(Request(resp_type, resp_error, msg_type)))
}

pub type State(state_type) {
  State(ctx: web.Context, state: state_type)
}

pub type Response(resp_type, resp_error) =
  Result(resp_type, resp_error)

pub type Request(resp_type, resp_error, msg_type) {
  Request(
    reply_to: process.Subject(Response(resp_type, resp_error)),
    msg: msg_type,
  )
}

pub fn new(
  ctx: web.Context,
  init_state: fn() -> state_type,
  handle: fn(Request(resp_type, resp_error, msg_type), State(state_type)) ->
    actor.Next(Request(resp_type, resp_error, msg_type), State(state_type)),
) -> Manager(resp_type, resp_error, msg_type) {
  let assert Ok(subject) = actor.start(State(ctx, init_state()), handle)

  Manager(subject)
}

pub fn call(
  manager: Manager(resp_type, resp_error, msg_type),
  msg: msg_type,
) -> Result(resp_type, resp_error) {
  actor.call(manager.subject, Request(_, msg), call_timeout)
}
