import { SolType, Ref, LiteralBinding, Resource, MaterialiserInput, InputSpec } from '@lifi/compose-spec';

/**
 * A string that represents a non-negative integer (e.g. "1000000000").
 * Uses a template literal type so that only numeric-looking strings
 * are assignable at compile time.
 */
type IntegerString = `${bigint}`;
/**
 * Accepted input for integer-string config fields.
 * Callers may pass either a numeric string ("1000000000") or a native bigint
 * (1000000000n). Bigint values are stringified automatically during JSON
 * serialization via the SDK's `bigintReplacer`.
 */
type IntegerStringInput = IntegerString | bigint;
/**
 * A `0x`-prefixed hex string representing an Ethereum address.
 * Template literal type catches obviously wrong values at compile time.
 */
type Address = `0x${string}`;
/** The universe of output port kinds: Solidity scalar types plus `'resource'`. */
type OutputKind = SolType | 'resource';
/**
 * Maps an ABI parameter type string (from abitype's `ParseAbiItem`) to the
 * closest `OutputKind`. When the ABI type is one of the supported `SolType`
 * scalars, the mapping is exact; for all other Solidity types (e.g. tuples,
 * fixed-point decimals) it falls back to `SolType` — accepting any scalar
 * handle but not `'resource'` handles.
 *
 * The fallback excludes `'resource'` because every Solidity type is a scalar;
 * resource handles carry token-amount semantics that should only flow into
 * slots explicitly declared as resources.
 *
 * Extending `SolType` with new members automatically widens the exact-match
 * branch — no changes needed here.
 */
type AbiTypeToOutputKind<T extends string> = T extends SolType ? T : SolType;
/**
 * Phantom brand key for {@link TypedRef}.
 *
 * Declared, never assigned: it exists only so the type checker can tell a
 * typed ref from an arbitrary `{ $ref }` object. Keying it on a `unique
 * symbol` means no caller can produce one by writing an object literal.
 */
declare const typedRefKind: unique symbol;
/**
 * A typed `$ref` pointer carrying a phantom type parameter.
 *
 * On the wire, and at run time, this is exactly `{ $ref: string }` — the
 * brand is erased. The brand is *required* in the type so a plain `Ref` does
 * not satisfy `TypedRef` structurally, which is the contract `RefBindable`
 * advertises: an untyped `{ $ref }` never enters a typed bind slot. Build one
 * with `raw.ref<T>()`.
 */
interface TypedRef<T extends OutputKind = OutputKind> extends Ref {
    readonly [typedRefKind]: T;
}
/**
 * A literal bind value carrying a phantom type parameter.
 *
 * The wire format accepts a constant in a bind slot as
 * `{ kind: <solType>, value: <string> }`, alongside `$ref` pointers. Use it
 * for a value that must be baked into the flow document rather than supplied
 * at run time — the destination of a flow the backend stores and re-runs
 * later (a `continuation.settle` node's `continuationFlow`) has no run to
 * take an input from.
 *
 * @typeParam T - The Solidity type of the literal (e.g. `'address'`).
 */
interface TypedLiteral<T extends SolType = SolType> extends LiteralBinding {
    readonly kind: T;
    readonly __outputKind?: T;
}
/**
 * An input declaration in a flow schema.
 * `Resource` values become resource inputs (token amounts);
 * `SolType` strings become handle inputs (scalars like addresses).
 */
type InputDecl = Resource | SolType;
/**
 * A record mapping input names to their declarations.
 * Used as the type parameter for generic flow builders and requests.
 */
type InputSchema = Record<string, InputDecl>;
/**
 * Maps an input declaration to the allowed runtime value type.
 * Resource inputs accept materialisers or literal amounts.
 * Handle inputs accept the scalar type matching their SolType.
 */
type InputSpecOf<D> = D extends Resource ? MaterialiserInput | IntegerStringInput : D extends 'address' ? Address : D extends 'bool' ? boolean : D extends `${'u' | ''}int${string}` ? IntegerStringInput : D extends `bytes${string}` ? `0x${string}` : D extends 'string' ? string : InputSpec;
/**
 * A guard applied to an operation, with the `port` field constrained
 * to a specific set of output port names.
 */
interface TypedGuard<Port extends string = string> {
    readonly kind: string;
    readonly port: Port;
    readonly [key: string]: unknown;
}

export { type AbiTypeToOutputKind, type Address, type InputDecl, type InputSchema, type InputSpecOf, type IntegerString, type IntegerStringInput, type OutputKind, type TypedGuard, type TypedLiteral, type TypedRef, typedRefKind };
