import { describe, expect, it, vi } from "vitest";
import {
  reconcileV2Position,
  type ExitCapability,
  type FinalizedV2Reader,
  type V2PositionRequest,
} from "./reconciliation";

const request: V2PositionRequest = {
  chainId: 8453,
  contract: "0x1111111111111111111111111111111111111111",
  positionId: 7n,
  owner: "0x2222222222222222222222222222222222222222",
  contractStatus: "legacy",
};

function reader(overrides: Partial<Awaited<ReturnType<FinalizedV2Reader["readPosition"]>>> = {}): FinalizedV2Reader {
  return {
    finalizedBlock: vi.fn(async () => ({
      number: 123n,
      hash: `0x${"ab".repeat(32)}` as const,
      observedAt: "2026-08-20T00:00:00.000Z",
      finality: "finalized" as const,
    })),
    readPosition: vi.fn(async () => ({
      recordedPrincipal: 100n,
      pendingPayout: 10n,
      contractAssetBalance: 110n,
      disputed: false,
      terminal: false,
      ...overrides,
    })),
  };
}

const noExit: ExitCapability = {
  kind: "no-verified-exit",
  contractName: "AIPartnershipBondsV2",
  reason: "no user-controlled exit selector is present in the frozen V2 ABI",
};

describe("reconcileV2Position", () => {
  it("honestly blocks positions with no verified V2 exit", async () => {
    const result = await reconcileV2Position(reader(), request, noExit);
    expect(result.reconciled).toBe(true);
    expect(result.exit).toEqual({ available: false, reason: noExit.reason });
    expect(result.v3CreationAllowed).toBe(false);
  });

  it("never proposes migration for an insolvent reconciliation", async () => {
    const result = await reconcileV2Position(
      reader({ contractAssetBalance: 109n }),
      request,
      noExit,
    );
    expect(result.reconciled).toBe(false);
    expect(result.discrepancy).toBe(-1n);
    expect(result.v3CreationAllowed).toBe(false);
  });

  it("blocks disputed V2 positions", async () => {
    const result = await reconcileV2Position(
      reader({ disputed: true }),
      request,
      noExit,
    );
    expect(result.exit.available).toBe(false);
    expect(result.v3CreationAllowed).toBe(false);
  });

  it("returns only an unsigned transaction for a verified exit", async () => {
    const capability: ExitCapability = {
      kind: "verified-exit",
      contractName: "ExampleV2",
      selector: "0x12345678",
      sourceAbiSha256: "f".repeat(64),
      encode: ({ positionId }) =>
        `0x12345678${positionId.toString(16).padStart(64, "0")}`,
    };
    const result = await reconcileV2Position(reader(), request, capability);
    expect(result.exit.available).toBe(true);
    if (result.exit.available) {
      expect(result.exit.transaction).toEqual({
        chainId: 8453,
        from: request.owner,
        to: request.contract,
        value: "0",
        data: `0x12345678${"7".padStart(64, "0")}`,
      });
    }
    expect(result.v3CreationAllowed).toBe(true);
  });

  it("fails closed on RPC errors without plausible zero state", async () => {
    const failing: FinalizedV2Reader = {
      finalizedBlock: vi.fn(async () => { throw new Error("rpc unavailable"); }),
      readPosition: vi.fn(),
    };
    const result = await reconcileV2Position(failing, request, noExit);
    expect(result.reconciled).toBe(false);
    expect(result.v3CreationAllowed).toBe(false);
    expect(result.dataSourceErrors).toEqual(["rpc unavailable"]);
  });
});
