export type Hex = `0x${string}`;
export type ContractStatus =
  | "active"
  | "legacy"
  | "deprecated"
  | "paused"
  | "read-only";

export type FinalizedBlock = {
  number: bigint;
  hash: Hex;
  observedAt: string;
  finality: "finalized";
};

export type V2PositionRequest = {
  chainId: number;
  contract: Hex;
  positionId: bigint;
  owner: Hex;
  contractStatus: ContractStatus;
};

export type PositionState = {
  recordedPrincipal: bigint;
  pendingPayout: bigint;
  contractAssetBalance: bigint;
  disputed: boolean;
  terminal: boolean;
};

export type ExitCapability =
  | {
      kind: "verified-exit";
      contractName: string;
      selector: Hex;
      encode(position: V2PositionRequest): Hex;
      sourceAbiSha256: string;
    }
  | {
      kind: "no-verified-exit";
      contractName: string;
      reason: string;
      sourceAbiSha256?: string;
    };

export type UnsignedTransaction = {
  chainId: number;
  from: Hex;
  to: Hex;
  value: "0";
  data: Hex;
};

export type ReconciliationResult = {
  request: V2PositionRequest;
  block: FinalizedBlock;
  position: PositionState;
  reconciled: boolean;
  discrepancy: bigint;
  exit:
    | { available: true; transaction: UnsignedTransaction; sourceAbiSha256: string }
    | { available: false; reason: string };
  v3CreationAllowed: boolean;
  dataSourceErrors: string[];
  schemaVersion: "1.0.0";
};

export interface FinalizedV2Reader {
  finalizedBlock(chainId: number): Promise<FinalizedBlock>;
  readPosition(
    request: V2PositionRequest,
    block: FinalizedBlock,
  ): Promise<PositionState>;
}

/**
 * Reads canonical V2 state at one finalized block and produces, at most, an
 * unsigned user-controlled exit transaction. It never signs, broadcasts,
 * transfers, or invents an exit method.
 */
export async function reconcileV2Position(
  reader: FinalizedV2Reader,
  request: V2PositionRequest,
  capability: ExitCapability,
): Promise<ReconciliationResult> {
  try {
    const block = await reader.finalizedBlock(request.chainId);
    const position = await reader.readPosition(request, block);
    const obligations = position.recordedPrincipal + position.pendingPayout;
    const discrepancy = position.contractAssetBalance - obligations;
    const reconciled = discrepancy >= 0n;

    if (!reconciled) {
      return {
        request,
        block,
        position,
        reconciled,
        discrepancy,
        exit: {
          available: false,
          reason: "contract assets are below recorded principal plus pending payouts",
        },
        v3CreationAllowed: false,
        dataSourceErrors: [],
        schemaVersion: "1.0.0",
      };
    }

    if (position.disputed) {
      return {
        request,
        block,
        position,
        reconciled,
        discrepancy,
        exit: { available: false, reason: "position is disputed; no reputation or funds are copied" },
        v3CreationAllowed: false,
        dataSourceErrors: [],
        schemaVersion: "1.0.0",
      };
    }

    if (capability.kind === "no-verified-exit") {
      return {
        request,
        block,
        position,
        reconciled,
        discrepancy,
        exit: { available: false, reason: capability.reason },
        v3CreationAllowed: false,
        dataSourceErrors: [],
        schemaVersion: "1.0.0",
      };
    }

    return {
      request,
      block,
      position,
      reconciled,
      discrepancy,
      exit: {
        available: true,
        transaction: {
          chainId: request.chainId,
          from: request.owner,
          to: request.contract,
          value: "0",
          data: capability.encode(request),
        },
        sourceAbiSha256: capability.sourceAbiSha256,
      },
      // A fresh V3 agreement is offered only after a normal V2 exit is known.
      // Execution still requires separate user signatures and transactions.
      v3CreationAllowed: true,
      dataSourceErrors: [],
      schemaVersion: "1.0.0",
    };
  } catch (error) {
    return {
      request,
      block: {
        number: 0n,
        hash: "0x",
        observedAt: new Date(0).toISOString(),
        finality: "finalized",
      },
      position: {
        recordedPrincipal: 0n,
        pendingPayout: 0n,
        contractAssetBalance: 0n,
        disputed: false,
        terminal: false,
      },
      reconciled: false,
      discrepancy: 0n,
      exit: { available: false, reason: "direct-chain reconciliation failed" },
      v3CreationAllowed: false,
      dataSourceErrors: [error instanceof Error ? error.message : String(error)],
      schemaVersion: "1.0.0",
    };
  }
}
