// Server Action Types
//
// Server Actions are secure server-side functions that can be called from the client.
// They automatically include CSRF protection and type-safe serialization.

// ============================================================================
// Action Handler Types
// ============================================================================

///|
/// Action handler context - provides request info and utilities
pub struct ActionContext {
  /// Mars Context
  ctx : @mars.Context
  /// Raw request body (JSON string)
  body : String
}

///|
/// Create ActionContext from Mars context
pub fn ActionContext::from_ctx(
  ctx : @mars.Context,
  body : String,
) -> ActionContext {
  { ctx, body }
}

///|
/// Get header value (case-insensitive)
pub fn ActionContext::get_header(
  self : ActionContext,
  name : String,
) -> String? {
  self.ctx.header(name)
}

///|
/// Get query parameter
pub fn ActionContext::get_query(self : ActionContext, name : String) -> String? {
  self.ctx.query(name)
}

///|
/// Get state value
pub fn ActionContext::get_state(self : ActionContext, key : String) -> String? {
  self.ctx.get(key)
}

///|
/// Set state value
pub fn ActionContext::set_state(
  self : ActionContext,
  key : String,
  value : String,
) -> Unit {
  self.ctx.set(key, value)
}

// ============================================================================
// Action Result Types
// ============================================================================

///|
/// Action result - either success with data or error
pub enum ActionResult {
  /// Success with JSON data
  Success(@js.Any)
  /// Success with redirect (returns JSON with redirect instruction for JavaScript clients)
  Redirect(String)
  /// HTTP redirect (returns 302 status with Location header, for non-JS form submissions)
  HttpRedirect(String)
  /// Client error (4xx)
  ClientError(Int, String)
  /// Server error (5xx)
  ServerError(String)
}

///|
/// Create success result
pub fn ActionResult::ok(data : @js.Any) -> ActionResult {
  Success(data)
}

///|
/// Create redirect result (returns JSON for JavaScript clients)
pub fn ActionResult::redirect(url : String) -> ActionResult {
  Redirect(url)
}

///|
/// Create HTTP redirect result (returns 302 with Location header)
/// Use this for non-JavaScript form submissions (progressive enhancement)
pub fn ActionResult::http_redirect(url : String) -> ActionResult {
  HttpRedirect(url)
}

///|
/// Create bad request error
pub fn ActionResult::bad_request(message : String) -> ActionResult {
  ClientError(400, message)
}

///|
/// Create validation error
pub fn ActionResult::validation_error(message : String) -> ActionResult {
  ClientError(422, message)
}

///|
/// Create unauthorized error
pub fn ActionResult::unauthorized(message? : String) -> ActionResult {
  ClientError(401, message.unwrap_or("Unauthorized"))
}

///|
/// Create forbidden error
pub fn ActionResult::forbidden(message? : String) -> ActionResult {
  ClientError(403, message.unwrap_or("Forbidden"))
}

///|
/// Create not found error
pub fn ActionResult::not_found(message? : String) -> ActionResult {
  ClientError(404, message.unwrap_or("Not Found"))
}

///|
/// Create server error
pub fn ActionResult::server_error(message? : String) -> ActionResult {
  ServerError(message.unwrap_or("Internal Server Error"))
}

// ============================================================================
// Action Handler
// ============================================================================

///|
/// Action handler type - async function that receives ActionContext and returns ActionResult
pub(all) struct ActionHandler(async (ActionContext) -> ActionResult raise Error)

// ============================================================================
// ActionRef (type-safe reference, recommended)
// ============================================================================

///|
/// Type-safe action reference.
/// Factory functions can be auto-generated by `sol generate` from action definitions.
///
/// Example:
/// ```moonbit nocheck
/// // Define action ref (can be auto-generated)
/// pub fn create_user() -> ActionRef {
///   { id: "create-user", base_path: "/_action" }
/// }
///
/// // Register with handler
/// registry.register_ref(@actions.create_user(), handler=handler)
///
/// // Client-side invocation
/// @action.invoke_action(@actions.create_user().url(), payload, callback)
/// ```
pub(all) struct ActionRef {
  /// Action identifier
  id : String
  /// Base path for action endpoints (default: "/_action")
  base_path : String
}

///|
/// Get the full URL for this action
pub fn ActionRef::url(self : ActionRef) -> String {
  self.base_path + "/" + self.id
}

///|
/// Create an ActionDef from this ref and a handler
pub fn ActionRef::to_def(
  self : ActionRef,
  handler : ActionHandler,
) -> ActionDef {
  { id: self.id, handler, middlewares: [], require_json: true }
}

// ============================================================================
// Action Definition
// ============================================================================

///|
/// Server Action definition
pub struct ActionDef {
  /// Action identifier (should be cryptographically secure in production)
  id : String
  /// Action handler
  handler : ActionHandler
  /// Additional middlewares for this action
  middlewares : Array[@middleware.Middleware]
  /// Whether to require JSON Content-Type
  require_json : Bool
}

///|
/// Create action definition with default settings
pub fn ActionDef::new(id : String, handler : ActionHandler) -> ActionDef {
  { id, handler, middlewares: [], require_json: true }
}

///|
/// Add middleware to action
pub fn ActionDef::with_middleware(
  self : ActionDef,
  mw : @middleware.Middleware,
) -> ActionDef {
  let middlewares = self.middlewares.copy()
  middlewares.push(mw)
  { ..self, middlewares, }
}

///|
/// Add multiple middlewares to action
pub fn ActionDef::with_middlewares(
  self : ActionDef,
  mws : Array[@middleware.Middleware],
) -> ActionDef {
  let middlewares = self.middlewares.copy()
  for mw in mws {
    middlewares.push(mw)
  }
  { ..self, middlewares, }
}

///|
/// Set JSON requirement
pub fn ActionDef::with_require_json(
  self : ActionDef,
  require : Bool,
) -> ActionDef {
  { ..self, require_json: require }
}

// ============================================================================
// Action Registry
// ============================================================================

///|
/// Registry for server actions
pub struct ActionRegistry {
  /// Action definitions by ID
  actions : Map[String, ActionDef]
  /// Base path for action endpoints (default: "/_action")
  base_path : String
  /// Allowed origins for CSRF protection
  allowed_origins : Array[String]
}

///|
/// Create new action registry
pub fn ActionRegistry::new(allowed_origins? : Array[String]) -> ActionRegistry {
  {
    actions: {},
    base_path: "/_action",
    allowed_origins: allowed_origins.unwrap_or([]),
  }
}

///|
/// Set base path for action endpoints
pub fn ActionRegistry::with_base_path(
  self : ActionRegistry,
  base_path : String,
) -> ActionRegistry {
  { ..self, base_path, }
}

///|
/// Register an action
pub fn ActionRegistry::register(
  self : ActionRegistry,
  action : ActionDef,
) -> ActionRegistry {
  let actions = self.actions
  actions[action.id] = action
  { ..self, actions, }
}

///|
/// Register an action from a type-safe ActionRef and handler
pub fn ActionRegistry::register_ref(
  self : ActionRegistry,
  aref : ActionRef,
  handler : ActionHandler,
) -> ActionRegistry {
  self.register(aref.to_def(handler))
}

///|
/// Register multiple actions
pub fn ActionRegistry::register_all(
  self : ActionRegistry,
  defs : Array[ActionDef],
) -> ActionRegistry {
  let actions = self.actions
  for def in defs {
    actions[def.id] = def
  }
  { ..self, actions, }
}

///|
/// Get action by ID
pub fn ActionRegistry::get(self : ActionRegistry, id : String) -> ActionDef? {
  self.actions.get(id)
}

///|
/// Get all action IDs
pub fn ActionRegistry::action_ids(self : ActionRegistry) -> Array[String] {
  self.actions.keys().collect()
}

///|
/// Get action endpoint URL
pub fn ActionRegistry::action_url(self : ActionRegistry, id : String) -> String {
  self.base_path + "/" + id
}