(*
   --------------------------------------------------------------------------
   title: E-commerce Order State Machine
   description: Complex order state transitions with multiple conditions
   order: 6
   --------------------------------------------------------------------------

   Real-world TRICKY example: E-commerce Order State Machine

   THE TRICKY PROBLEM:
   Order states can transition based on multiple conditions:
   - Current state (Pending, Confirmed, Shipped, Delivered, Cancelled, Refunded)
   - Payment status (Paid, Unpaid, Failed, Refunded)
   - Inventory status (InStock, OutOfStock, Reserved)
   - Time elapsed (in hours)
   - Customer action (None, CancelRequest, ReturnRequest)

   TRICKY QUESTIONS:
   - From which states can we transition to Cancelled?
   - What are ALL the ways to reach Delivered state?
   - Are there any impossible state combinations?
   - What happens if payment fails after shipping?
   - Can we refund before delivery?

   WITHOUT decomposition: State diagram is complex, easy to miss edge cases
   WITH decomposition: See ALL valid transitions, find dead states or impossible paths
*)

type order_state = Pending | Confirmed | Shipped | Delivered | Cancelled | Refunded
type payment_status = Paid | Unpaid | Failed | PaymentRefunded
type inventory_status = InStock | OutOfStock | Reserved
type customer_action = NoAction | CancelRequest | ReturnRequest

(* THE TRICKY FUNCTION: Determine next state based on current state and conditions *)
let next_order_state
    current_state
    payment inventory
    hours_elapsed
    customer_action =

  match current_state with
  | Pending ->
      if customer_action = CancelRequest then
        Cancelled
      else if payment = Failed then
        Cancelled
      else if payment = Paid && inventory = Reserved then
        Confirmed
      else if payment = Paid && inventory = OutOfStock then
        (* Edge case: paid but no stock - what to do? *)
        Pending  (* Keep pending until stock available *)
      else if hours_elapsed > 48 && payment = Unpaid then
        (* Timeout: cancel unpaid orders after 48 hours *)
        Cancelled
      else
        Pending

  | Confirmed ->
      if customer_action = CancelRequest && hours_elapsed < 24 then
        (* Allow cancellation within 24 hours of confirmation *)
        Cancelled
      else if payment = PaymentRefunded then
        (* Payment was refunded - cancel order *)
        Cancelled
      else if inventory = OutOfStock then
        (* Stock became unavailable after confirmation *)
        Pending  (* Back to pending *)
      else if hours_elapsed >= 24 then
        (* Ship after 24 hours *)
        Shipped
      else
        Confirmed

  | Shipped ->
      if customer_action = CancelRequest then
        (* Too late to cancel, but can request return after delivery *)
        Shipped
      else if hours_elapsed >= 72 then
        (* Delivered after 72 hours of shipping *)
        Delivered
      else
        Shipped

  | Delivered ->
      if customer_action = ReturnRequest && hours_elapsed < 168 then
        (* Allow returns within 7 days (168 hours) of delivery *)
        Refunded
      else
        Delivered

  | Cancelled ->
      if payment = Paid then
        (* Refund paid orders that were cancelled *)
        Refunded
      else
        Cancelled

  | Refunded ->
      (* Terminal state - no transitions *)
      Refunded

[@@decomp top ()]

(* VARIATION 1: Using basis to keep state abstract
   This shows the control flow without expanding state details
*)
let next_order_state_abstract
    current_state
    payment inventory
    hours_elapsed
    customer_action =

  match current_state with
  | Pending ->
      if customer_action = CancelRequest then Cancelled
      else if payment = Failed then Cancelled
      else if payment = Paid && inventory = Reserved then Confirmed
      else if payment = Paid && inventory = OutOfStock then Pending
      else if hours_elapsed > 48 && payment = Unpaid then Cancelled
      else Pending

  | Confirmed ->
      if customer_action = CancelRequest && hours_elapsed < 24 then Cancelled
      else if payment = PaymentRefunded then Cancelled
      else if inventory = OutOfStock then Pending
      else if hours_elapsed >= 24 then Shipped
      else Confirmed

  | Shipped ->
      if customer_action = CancelRequest then Shipped
      else if hours_elapsed >= 72 then Delivered
      else Shipped

  | Delivered ->
      if customer_action = ReturnRequest && hours_elapsed < 168 then Refunded
      else Delivered

  | Cancelled ->
      if payment = Paid then Refunded
      else Cancelled

  | Refunded ->
      Refunded

[@@decomp top ~ctx_simp:true ()]

(* VARIATION 2: With pruning to remove impossible transitions
   Question: Are there any unreachable transitions?
*)
let transitions_from_confirmed
    current_state
    payment inventory
    hours_elapsed
    customer_action =

  match current_state with
  | Confirmed ->
      if customer_action = CancelRequest && hours_elapsed < 24 then Cancelled
      else if payment = PaymentRefunded then Cancelled
      else if inventory = OutOfStock then Pending
      else if hours_elapsed >= 24 then Shipped
      else Confirmed
  | _ -> current_state  (* Other states unchanged *)

[@@decomp top ~prune:true ()]
