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

import { Ok, Err, Some, None, type Result, type Option, type ValidationError, HttpResult, type JsonValue, type BoundaryError, type ServiceBinding, callService, boundaryError } from "../../runtime.js";

import { CurrencyCode as __CommonsCurrencyCode, Money as __CommonsMoney } from "../../commerce/money.js";

import * as commerce_payment from "../commerce-payment/handlers.js";

export type CurrencyCode = __CommonsCurrencyCode & { readonly __ctxBrand: "commerce.orders" };
export const CurrencyCode = {
  of(value: string): Result<CurrencyCode, ValidationError> { return __CommonsCurrencyCode.of(value) as unknown as Result<CurrencyCode, ValidationError>; },
  unsafe(value: string): CurrencyCode { return __CommonsCurrencyCode.unsafe(value) as unknown as CurrencyCode; },
};
export type Money = __CommonsMoney & { readonly __ctxBrand: "commerce.orders" };

/**
 * Orders context with both an internal `on call` service and an external
 * HTTP API. The HTTP handler maps domain errors to HttpResult variants
 * (the Anti-Corruption Layer pattern at the HTTP boundary).
 */

export type OrderError =
    { readonly tag: "PaymentDeclined" }
  | { readonly tag: "PaymentInsufficientFunds" }
  | { readonly tag: "PaymentInfrastructureError" };

export const OrderError = {
  PaymentDeclined: { tag: "PaymentDeclined" } as OrderError,
  PaymentInsufficientFunds: { tag: "PaymentInsufficientFunds" } as OrderError,
  PaymentInfrastructureError: { tag: "PaymentInfrastructureError" } as OrderError,
};

/**
 * Request payload for creating an order via HTTP. Plain record deserialised
 * from the JSON request body.
 */
export interface CreateOrderRequest {
  readonly amountMinor: number;
  readonly currency: CurrencyCode;
}

export const CreateOrderRequest = {
};

/**
 * View returned to HTTP clients.
 */
export interface OrderView {
  readonly status: string;
  readonly totalMinor: number;
}

export const OrderView = {
};

export const placement = {
  /**
   * Internal placement. Returns a domain Result so consumers in other
   * contexts can integrate without coupling to HTTP status semantics.
   */
  async call(total: Money, deps: { env: { COMMERCE_PAYMENT: ServiceBinding } }): Promise<Result<void, OrderError>> {
    const auth = await callService(deps.env.COMMERCE_PAYMENT, "authorise", commerce_payment.serialise_Money(total), commerce_payment.deserialise_Result_AuthId_PaymentError, "commerce.orders");
    switch (auth.tag) {
      case "Ok": {
        return Ok(undefined);
      }
      case "Err": {
        const error = auth.error;
        return ((__d) => {
    switch (__d.tag) {
      case "Declined": {
        return Err(OrderError.PaymentDeclined);
      }
      case "InsufficientFunds": {
        return Err(OrderError.PaymentInsufficientFunds);
      }
      case "GatewayDown": {
        return Err(OrderError.PaymentInfrastructureError);
      }
    }
    throw new Error("non-exhaustive match");
  })(error);
      }
    }
    throw new Error("non-exhaustive match");
  },
};

export const orders = {
  /**
   * HTTP entry point. Parses the request body, authorises payment, then
   * maps the domain outcome to an HTTP result.
   */
  async http_POST_orders(body: CreateOrderRequest, deps: { env: { COMMERCE_PAYMENT: ServiceBinding } }): Promise<HttpResult<OrderView>> {
    const total = { minorUnits: body.amountMinor, currency: body.currency };
    const auth = await callService(deps.env.COMMERCE_PAYMENT, "authorise", commerce_payment.serialise_Money(total), commerce_payment.deserialise_Result_AuthId_PaymentError, "commerce.orders");
    switch (auth.tag) {
      case "Ok": {
        return HttpResult.Created({ status: "placed", totalMinor: body.amountMinor });
      }
      case "Err": {
        const error = auth.error;
        return ((__d) => {
    switch (__d.tag) {
      case "Declined": {
        return HttpResult.UnprocessableEntity("payment declined");
      }
      case "InsufficientFunds": {
        return HttpResult.UnprocessableEntity("insufficient funds");
      }
      case "GatewayDown": {
        return HttpResult.ServerError("payment system unavailable");
      }
    }
    throw new Error("non-exhaustive match");
  })(error);
      }
    }
    throw new Error("non-exhaustive match");
  },
};

export function serialise_CreateOrderRequest(value: CreateOrderRequest): JsonValue {
  return {
    amountMinor: value.amountMinor as JsonValue,
    currency: serialise_CurrencyCode(value.currency),
  };
}

export function deserialise_CreateOrderRequest(json: JsonValue, path: string = "$"): Result<CreateOrderRequest, BoundaryError> {
  if (typeof json !== "object" || json === null || Array.isArray(json)) {
    return Err({ kind: "StructuralMismatch", path, expected: "object", actual: typeof json });
  }
  const obj = json as { [k: string]: JsonValue };
  if (typeof obj["amountMinor"] !== "number") {
    return Err({ kind: "StructuralMismatch", path: `${path}.amountMinor`, expected: "number", actual: typeof obj["amountMinor"] });
  }
  if (!Number.isInteger(obj["amountMinor"])) {
    return Err({ kind: "StructuralMismatch", path: `${path}.amountMinor`, expected: "integer", actual: String(obj["amountMinor"]) });
  }
  const __amountMinor = obj["amountMinor"];
  const __r_currency = deserialise_CurrencyCode(obj["currency"], `${path}.currency`);
  if (__r_currency.tag === "Err") return __r_currency;
  const __currency = __r_currency.value;
  return Ok({ amountMinor: __amountMinor, currency: __currency } as CreateOrderRequest);
}

export function serialise_OrderError(value: OrderError): JsonValue {
  switch (value.tag) {
    case "PaymentDeclined":
      return { kind: "PaymentDeclined" };
    case "PaymentInsufficientFunds":
      return { kind: "PaymentInsufficientFunds" };
    case "PaymentInfrastructureError":
      return { kind: "PaymentInfrastructureError" };
  }
}

export function deserialise_OrderError(json: JsonValue, path: string = "$"): Result<OrderError, BoundaryError> {
  if (typeof json !== "object" || json === null || Array.isArray(json)) {
    return Err({ kind: "StructuralMismatch", path, expected: "object", actual: typeof json });
  }
  const obj = json as { [k: string]: JsonValue };
  const kind = obj["kind"];
  switch (kind) {
    case "PaymentDeclined":
      return Ok({ tag: "PaymentDeclined" } as OrderError);
    case "PaymentInsufficientFunds":
      return Ok({ tag: "PaymentInsufficientFunds" } as OrderError);
    case "PaymentInfrastructureError":
      return Ok({ tag: "PaymentInfrastructureError" } as OrderError);
    default:
      return Err({ kind: "StructuralMismatch", path, expected: "sum variant kind", actual: String(kind) });
  }
}

export function serialise_OrderView(value: OrderView): JsonValue {
  return {
    status: value.status as JsonValue,
    totalMinor: value.totalMinor as JsonValue,
  };
}

export function deserialise_OrderView(json: JsonValue, path: string = "$"): Result<OrderView, BoundaryError> {
  if (typeof json !== "object" || json === null || Array.isArray(json)) {
    return Err({ kind: "StructuralMismatch", path, expected: "object", actual: typeof json });
  }
  const obj = json as { [k: string]: JsonValue };
  if (typeof obj["status"] !== "string") {
    return Err({ kind: "StructuralMismatch", path: `${path}.status`, expected: "string", actual: typeof obj["status"] });
  }
  const __status = obj["status"];
  if (typeof obj["totalMinor"] !== "number") {
    return Err({ kind: "StructuralMismatch", path: `${path}.totalMinor`, expected: "number", actual: typeof obj["totalMinor"] });
  }
  if (!Number.isInteger(obj["totalMinor"])) {
    return Err({ kind: "StructuralMismatch", path: `${path}.totalMinor`, expected: "integer", actual: String(obj["totalMinor"]) });
  }
  const __totalMinor = obj["totalMinor"];
  return Ok({ status: __status, totalMinor: __totalMinor } as OrderView);
}


import { serialise_CurrencyCode, deserialise_CurrencyCode, serialise_Money, deserialise_Money } from "../../commerce/money.js";
export { serialise_CurrencyCode, deserialise_CurrencyCode, serialise_Money, deserialise_Money };

export function serialise_Result_Unit_OrderError(value: Result<void, OrderError>): JsonValue {
  if (value.tag === "Ok") return { kind: "Ok", value: null };
  return { kind: "Err", error: serialise_OrderError(value.error) };
}

export function deserialise_Result_Unit_OrderError(json: JsonValue, path: string = "$"): Result<Result<void, OrderError>, BoundaryError> {
  if (typeof json !== "object" || json === null || Array.isArray(json)) {
    return Err({ kind: "StructuralMismatch", path, expected: "object", actual: typeof json });
  }
  const obj = json as { [k: string]: JsonValue };
  if (obj["kind"] === "Ok") {
  const __v = undefined;
    return Ok(Ok(__v) as Result<void, OrderError>);
  } else if (obj["kind"] === "Err") {
  const __r_e = deserialise_OrderError(obj["error"], `${path}.error`);
  if (__r_e.tag === "Err") return __r_e;
  const __e = __r_e.value;
    return Ok(Err(__e) as Result<void, OrderError>);
  }
  return Err({ kind: "StructuralMismatch", path, expected: "Ok | Err", actual: String(obj["kind"]) });
}

