export type Availability = "now" | "future";

export interface OutputAmount {
  readonly expected: bigint;
  readonly min?: bigint;
}

export type Resource =
  | { readonly kind: "native"; readonly chainId: number }
  | {
      readonly kind: "erc20";
      readonly token: string;
      readonly chainId: number;
    };

export type SimulatedAmounts = {
  readonly amountOut: bigint;
  readonly amountOutMin: bigint;
};

export type ProducedResource = Resource & {
  readonly availability: Availability;
  readonly simulated?: SimulatedAmounts;
  readonly owner: string;
};

export const erc20Resource = (token: string, chainId: number): Resource => ({
  kind: "erc20",
  token,
  chainId,
});

export const nativeResource = (chainId: number): Resource => ({
  kind: "native",
  chainId,
});

export const isERC20Resource = (
  r: Resource,
): r is Extract<Resource, { kind: "erc20" }> => r.kind === "erc20";

export const isNativeResource = (
  r: Resource,
): r is Extract<Resource, { kind: "native" }> => r.kind === "native";

export const resourcesEqual = (a: Resource, b: Resource): boolean => {
  if (isNativeResource(a) && isNativeResource(b)) {
    return a.chainId === b.chainId;
  }
  if (isERC20Resource(a) && isERC20Resource(b)) {
    return (
      a.chainId === b.chainId && a.token.toLowerCase() === b.token.toLowerCase()
    );
  }
  return false;
};

export const foldResource = <R>(
  r: Resource,
  cases: {
    readonly native: (chainId: number) => R;
    readonly erc20: (token: string, chainId: number) => R;
  },
): R =>
  r.kind === "native"
    ? cases.native(r.chainId)
    : cases.erc20(r.token, r.chainId);

export const resourceKey = (r: Resource): string =>
  foldResource(r, {
    native: (chainId) => `native:${chainId}`,
    erc20: (token, chainId) => `erc20:${token.toLowerCase()}:${chainId}`,
  });

export const erc20Token = (r: Resource): string => {
  if (r.kind !== "erc20")
    throw new Error(`Expected erc20 resource, got ${r.kind}`);
  return r.token;
};
