// Generated by bynkc — do not edit by hand.
// context commerce.orders

import { Ok, Err, Some, None, type Result, type Option, type ValidationError, type DurableObjectState, type DurableObjectNamespace, StateRegistry, makeAgent } from "../runtime.js";

import { CustomerId as __CommonsCustomerId, OrderId as __CommonsOrderId } from "./identifiers.js";

export type CustomerId = __CommonsCustomerId & { readonly __ctxBrand: "commerce.orders" };
export const CustomerId = {
  of(value: string): Result<CustomerId, ValidationError> { return __CommonsCustomerId.of(value) as unknown as Result<CustomerId, ValidationError>; },
  unsafe(value: string): CustomerId { return __CommonsCustomerId.unsafe(value) as unknown as CustomerId; },
};
export type OrderId = __CommonsOrderId & { readonly __ctxBrand: "commerce.orders" };
export const OrderId = {
  of(value: string): Result<OrderId, ValidationError> { return __CommonsOrderId.of(value) as unknown as Result<OrderId, ValidationError>; },
  unsafe(value: string): OrderId { return __CommonsOrderId.unsafe(value) as unknown as OrderId; },
};

/**
 * Orders context. Each order is an agent instance keyed by OrderId.
 */

export type OrderError =
    { readonly tag: "EmptyCart" }
  | { readonly tag: "TooManyItems" }
  | { readonly tag: "OutOfStock" };

export const OrderError = {
  EmptyCart: { tag: "EmptyCart" } as OrderError,
  TooManyItems: { tag: "TooManyItems" } as OrderError,
  OutOfStock: { tag: "OutOfStock" } as OrderError,
};

export type OrderStatus =
    { readonly tag: "Pending" }
  | { readonly tag: "Placed" }
  | { readonly tag: "Cancelled" };

export const OrderStatus = {
  Pending: { tag: "Pending" } as OrderStatus,
  Placed: { tag: "Placed" } as OrderStatus,
  Cancelled: { tag: "Cancelled" } as OrderStatus,
};

export interface Inventory {
  check(productId: CustomerId): Promise<Result<number, OrderError>>;
}

export const InventoryToken: unique symbol = Symbol("Inventory");

export class StubInventory implements Inventory {
  async check(productId: CustomerId): Promise<Result<number, OrderError>> {
    return Ok(100);
  }
}

export const StubInventoryProvider = { token: InventoryToken, factory: () => new StubInventory() };

export interface OrderState {
  readonly status: Option<OrderStatus>;
  readonly items: number;
}

const __OrderRegistry = new StateRegistry();
function __zeroOfOrderState(): OrderState { return { status: None, items: 0 }; }

export class Order {
  state: DurableObjectState;
  constructor(state: DurableObjectState) {
    this.state = state;
  }

  private async loadState(): Promise<OrderState> {
    const stored = await this.state.storage.get<OrderState>("state");
    return stored ?? __zeroOfOrderState();
  }

  private async commitState(s: OrderState): Promise<void> {
    await this.state.storage.put("state", s);
  }

  async status(deps: {}): Promise<Option<OrderStatus>> {
    const currentState = await this.loadState();
    return currentState.status;
  }

  async addItem(productId: CustomerId, quantity: number, deps: { Inventory: Inventory }): Promise<Result<void, OrderError>> {
    const currentState = await this.loadState();
    const stockResult = await deps.Inventory.check(productId);
    const __r0 = stockResult;
    if (__r0.tag === "Err") return __r0;
    const stock = __r0.value;
    if (stock < quantity) {
      return Err(OrderError.OutOfStock);
    } else {
      if (currentState.items >= 50) {
        return Err(OrderError.TooManyItems);
      } else {
        await this.commitState({ ...currentState, items: currentState.items + quantity });
        return Ok(undefined);
      }
    }
  }

}

export function __makeOrder(key: OrderId, env?: { ORDER?: DurableObjectNamespace }): Order {
  return makeAgent(__OrderRegistry, env?.ORDER, key, (state) => new Order(state));
}

export function __resetAgents(): void {
  __OrderRegistry.reset();
}

