import type { SimulationRevert } from "./compile.js";
import type { FlowInput, ResourceInput, SolType } from "./flowSchema.js";
import type { Availability, Resource } from "./resource.js";

export type {
  HandlePort,
  OpInputPort,
  OpOutputPort,
  ResourceOutputPort,
  ResourcePort,
} from "./zodSchemas.js";

import type {
  HandlePort,
  OpInputPort,
  OpOutputPort,
  ResourceOutputPort,
  ResourcePort,
} from "./zodSchemas.js";

export interface Port {
  readonly name: string;
  readonly type: SolType;
  readonly mode: "linear" | "copy";
  readonly availability?: Availability;
  readonly resource?: Resource;
}

export const isResourceInput = (input: FlowInput): input is ResourceInput =>
  "resource" in input;

export const flowInputType = (input: FlowInput): SolType =>
  isResourceInput(input) ? "uint256" : input.type;

export const flowInputMode = (input: FlowInput): "linear" | "copy" =>
  isResourceInput(input) ? "linear" : "copy";

export const flowInputResource = (input: FlowInput): Resource | undefined =>
  isResourceInput(input) ? input.resource : undefined;

export const isResourcePort = (port: OpInputPort): port is ResourcePort =>
  port.kind === "resource";

export const isHandlePort = (
  port: OpInputPort | OpOutputPort
): port is HandlePort => port.kind === "handle";

export const isResourceOutputPort = (
  port: OpInputPort | OpOutputPort
): port is ResourceOutputPort => port.kind === "resource_output";

export type ComposeErrorKind =
  | "decode_error"
  | "validation_error"
  | "linearity_error"
  | "resolve_error"
  | "preparation_error"
  | "provider_error"
  | "no_route_error"
  | "lowering_error"
  | "simulation_error"
  | "simulation_setup_error"
  | "simulation_revert"
  | "subgraph_simulation_revert"
  | "guard_error"
  | "continuation_error"
  | "compilation_error"
  | "verification_error"
  | "price_impact_exceeded"
  | "fee_resolution_error"
  | "fee_resolution_unavailable"
  | "flashloan_unauthorized";

export type GenericComposeErrorKind = Exclude<
  ComposeErrorKind,
  "preparation_error" | "simulation_revert"
>;

export interface FailedPreparedOp {
  readonly callId: string;
  readonly op: string;
  readonly kind: GenericComposeErrorKind;
  readonly message: string;
  readonly path?: string;
}

interface ComposeErrorBase {
  readonly message: string;
  readonly path?: string;
}

export interface PreparationComposeError extends ComposeErrorBase {
  readonly kind: "preparation_error";
  readonly failedOps: readonly FailedPreparedOp[];
  readonly succeededOps: readonly string[];
}

export interface SimulationRevertComposeError extends ComposeErrorBase {
  readonly kind: "simulation_revert";
  readonly details?: SimulationRevert;
}

export interface GenericComposeError extends ComposeErrorBase {
  readonly kind: GenericComposeErrorKind;
  readonly [key: string]: unknown;
}

export type ComposeError =
  | PreparationComposeError
  | SimulationRevertComposeError
  | GenericComposeError;

export interface MaterialiserInputBase {
  readonly kind: string;
}

export type MaterialiserInput = MaterialiserInputBase & Record<string, unknown>;

export type MaterialiserInputOf<C extends object> = MaterialiserInputBase & C;

export type MaterialiserConfigOf<C extends object> = C;

export const isMaterialiserInput = (s: InputSpec): s is MaterialiserInput =>
  typeof s === "object" && s !== null && "kind" in s;

export type InputSpec = bigint | string | MaterialiserInput;

export interface PreconditionBase {
  readonly type: string;
}

export type Precondition = PreconditionBase & Record<string, unknown>;

export type PreconditionOf<C extends object> = PreconditionBase & C;

export type PreconditionConfigOf<C extends object> = Omit<
  PreconditionBase,
  "type"
> &
  C;
