import { EVM_NATIVE_SENTINEL } from './chainCapabilities.js';

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);

// Canonical address representation of a resource for consumers whose wire
// contract uses the EVM-native sentinel rather than the Resource union.
export const resourceToken = (r: Resource): string =>
  foldResource(r, {
    native: () => EVM_NATIVE_SENTINEL,
    erc20: (token) => token,
  });

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;
};
