"use strict";
var __importDefault = (this && this.__importDefault) || function (mod) {
    return (mod && mod.__esModule) ? mod : { "default": mod };
};
Object.defineProperty(exports, "__esModule", { value: true });
exports.determineDepositLeverageFlashBorrowType = determineDepositLeverageFlashBorrowType;
exports.determineWithdrawLeverageFlashBorrowType = determineWithdrawLeverageFlashBorrowType;
exports.determineAdjustLeverageFlashBorrowType = determineAdjustLeverageFlashBorrowType;
exports.determineRepayWithCollFlashBorrowType = determineRepayWithCollFlashBorrowType;
const decimal_js_1 = __importDefault(require("decimal.js"));
const classes_1 = require("../classes");
const repay_with_collateral_calcs_1 = require("../lending_operations/repay_with_collateral_calcs");
const utils_1 = require("../utils");
const calcs_1 = require("./calcs");
const utils_2 = require("./utils");
/**
 * Intent-level helpers that pick a viable `FlashBorrowType` for each leverage operation.
 *
 * Callers provide only the intent (amounts, target leverage, price); the SDK computes the
 * required-lamport size for each candidate flash side — AND the collateral redeemed in the same tx
 * for ops that have a redeem leg — then runs the `determineFlashBorrowType` viability check (flash
 * loans enabled + the reserve covers flash-borrow + redeem). Coll-flash is preferred when it is
 * genuinely viable; otherwise it falls back to debt-flash.
 *
 * Each helper throws with a diagnostic message if neither side is viable.
 */
/**
 * Picks a viable `FlashBorrowType` for a leveraged DEPOSIT (open / scale-into position).
 *
 *  - debt-flash flash-borrows the loan-side; coll-flash flash-borrows the collateral side.
 *  - Sizes are derived from `depositLeverageCalcs` (coll-flash) and `depositLeverageCalcsDebtFlash`
 *    (debt-flash), both at the requested target leverage and the provided price.
 *  - `selectedTokenMint` follows the operation's "I'm paying X with this token" convention.
 *
 * @throws if neither side is viable.
 */
function determineDepositLeverageFlashBorrowType(props) {
    const { kaminoMarket, collReserveAddress, debtReserveAddress, depositAmount, selectedTokenMint, targetLeverage, slippagePct, } = props;
    const collReserve = kaminoMarket.getExistingReserveByAddress(collReserveAddress);
    const debtReserve = kaminoMarket.getExistingReserveByAddress(debtReserveAddress);
    const collTokenMint = collReserve.getLiquidityMint();
    const [solTokenReserve] = kaminoMarket.getReservesByMint(utils_1.WRAPPED_SOL_MINT);
    const depositTokenIsCollToken = selectedTokenMint === collTokenMint;
    const depositTokenIsSol = solTokenReserve ? selectedTokenMint === solTokenReserve.getLiquidityMint() : false;
    // Coll-flash sizing.
    const collFlashCalcs = (0, calcs_1.depositLeverageCalcs)({
        depositAmount,
        depositTokenIsCollToken,
        depositTokenIsSol,
        priceDebtToColl: props.priceDebtToColl,
        targetLeverage,
        slippagePct,
        flashLoanFee: collReserve.getFlashLoanFee(),
    });
    const requiredCollLamports = collFlashCalcs.flashBorrowInCollToken.mul(collReserve.getMintFactor()).ceil();
    // Debt-flash sizing.
    const debtFlashCalcs = (0, calcs_1.depositLeverageCalcsDebtFlash)({
        depositAmount,
        depositTokenIsCollToken,
        depositTokenIsSol,
        priceDebtToColl: props.priceDebtToColl,
        targetLeverage,
        slippagePct,
        flashLoanFee: debtReserve.getFlashLoanFee(),
    });
    const requiredDebtLamports = debtFlashCalcs.flashBorrowInDebtToken.mul(debtReserve.getMintFactor()).ceil();
    // A leveraged deposit/open deposits collateral; it never redeems from the collateral reserve.
    return (0, utils_2.determineFlashBorrowType)(collReserve, debtReserve, requiredCollLamports, requiredDebtLamports, new decimal_js_1.default(0));
}
/**
 * Picks a viable `FlashBorrowType` for a leveraged WITHDRAW (partial unwind or close-position).
 *
 *  - debt-flash flash-borrows the repay amount in debt lamports.
 *  - coll-flash flash-borrows the swap-in amount in coll lamports AND redeems collateral from the
 *    same reserve in the same tx, so coll-flash viability accounts for both legs (flash + redeem).
 *  - Sizes come from `withdrawLeverageCalcs` and `withdrawLeverageCalcsCollFlash`.
 *
 * @throws if neither side is viable.
 */
function determineWithdrawLeverageFlashBorrowType(props) {
    const { kaminoMarket, obligation, collReserveAddress, debtReserveAddress, withdrawAmount, selectedTokenMint, isClosingPosition, priceCollToDebt, currentSlot, slippagePct, } = props;
    const collReserve = kaminoMarket.getExistingReserveByAddress(collReserveAddress);
    const debtReserve = kaminoMarket.getExistingReserveByAddress(debtReserveAddress);
    const deposited = props.depositedLamports !== undefined
        ? (0, classes_1.lamportsToNumberDecimal)(props.depositedLamports, collReserve.stats.decimals)
        : (0, classes_1.lamportsToNumberDecimal)(obligation.getDepositByReserve(collReserveAddress)?.amount ?? new decimal_js_1.default(0), collReserve.stats.decimals);
    const borrowed = props.borrowedLamports !== undefined
        ? (0, classes_1.lamportsToNumberDecimal)(props.borrowedLamports, debtReserve.stats.decimals)
        : (0, classes_1.lamportsToNumberDecimal)(obligation.getBorrowByReserve(debtReserveAddress)?.amount ?? new decimal_js_1.default(0), debtReserve.stats.decimals);
    const selectedTokenIsCollToken = selectedTokenMint === collReserve.getLiquidityMint();
    // Debt-flash: flash borrow = repayAmount.
    const debtFlashCalcs = (0, calcs_1.withdrawLeverageCalcs)(kaminoMarket, collReserve, debtReserve, priceCollToDebt, withdrawAmount, deposited, borrowed, currentSlot, isClosingPosition, selectedTokenIsCollToken, selectedTokenMint, obligation, debtReserve.getFlashLoanFee(), slippagePct);
    const requiredDebtLamports = debtFlashCalcs.repayAmount.mul(debtReserve.getMintFactor()).ceil();
    // Coll-flash: flash borrow = flashBorrowInCollToken.
    const collFlashCalcs = (0, calcs_1.withdrawLeverageCalcsCollFlash)(kaminoMarket, collReserve, debtReserve, priceCollToDebt, withdrawAmount, deposited, borrowed, currentSlot, isClosingPosition, selectedTokenIsCollToken, selectedTokenMint, obligation, collReserve.getFlashLoanFee(), slippagePct);
    const requiredCollLamports = collFlashCalcs.flashBorrowInCollToken.mul(collReserve.getMintFactor()).ceil();
    // Coll-flash also redeems collateral from the SAME reserve in the same tx
    // (WithdrawObligationCollateralAndRedeemReserveCollateral), before the flash loan is repaid. On a
    // close that's the entire deposit; on a partial it's `depositTokenWithdrawAmount` (matches the
    // amount the build flow passes to the redeem ix). Pass it so a thin collateral reserve that can't
    // cover flash-borrow + redeem falls back to debt-flash instead of self-colliding (klend 6008).
    const redeemCollLamports = (isClosingPosition ? deposited : collFlashCalcs.depositTokenWithdrawAmount)
        .mul(collReserve.getMintFactor())
        .ceil();
    return (0, utils_2.determineFlashBorrowType)(collReserve, debtReserve, requiredCollLamports, requiredDebtLamports, redeemCollLamports);
}
/**
 * Picks a viable `FlashBorrowType` for a leveraged ADJUST (increase or decrease).
 *
 * Auto-detects increase vs decrease from the sign of the position deltas returned by
 * `calcAdjustAmounts`:
 *  - increase (current < target): coll-flash borrows `adjustDepositPosition` coll lamports,
 *    debt-flash borrows `flashBorrowInDebtToken` debt lamports; deposits collateral, so no redeem;
 *  - decrease (current > target): debt-flash borrows |`adjustBorrowPosition`| debt lamports,
 *    coll-flash borrows `flashBorrowInCollToken` coll lamports AND withdraws
 *    `depositTokenWithdrawAmount` coll from the same reserve to repay the flash loan — a redeem leg
 *    the coll-flash viability check accounts for (thin reserve: flash + redeem can self-collide).
 *
 * @throws if neither side is viable, or if `targetLeverage` equals the current leverage exactly
 *   (nothing to adjust — caller should skip the op).
 */
function determineAdjustLeverageFlashBorrowType(props) {
    const { kaminoMarket, obligation, collReserveAddress, debtReserveAddress, targetLeverage, priceCollToDebt, slippagePct, } = props;
    const collReserve = kaminoMarket.getExistingReserveByAddress(collReserveAddress);
    const debtReserve = kaminoMarket.getExistingReserveByAddress(debtReserveAddress);
    const deposited = (0, classes_1.lamportsToNumberDecimal)(obligation.getDepositByReserve(collReserveAddress)?.amount ?? new decimal_js_1.default(0), collReserve.stats.decimals);
    const borrowed = (0, classes_1.lamportsToNumberDecimal)(obligation.getBorrowByReserve(debtReserveAddress)?.amount ?? new decimal_js_1.default(0), debtReserve.stats.decimals);
    // Direction is determined by the sign of the position deltas — the flash-loan-fee on this call
    // is a tiny perturbation that won't flip the sign; using the coll fee here is incidental.
    const { adjustDepositPosition, adjustBorrowPosition } = (0, calcs_1.calcAdjustAmounts)({
        currentDepositPosition: deposited,
        currentBorrowPosition: borrowed,
        targetLeverage,
        priceCollToDebt,
        flashLoanFee: new decimal_js_1.default(collReserve.getFlashLoanFee()),
    });
    const isIncrease = adjustDepositPosition.gte(0) && adjustBorrowPosition.gte(0);
    const isDecrease = adjustDepositPosition.lte(0) && adjustBorrowPosition.lte(0);
    if (!isIncrease && !isDecrease) {
        throw new Error('determineAdjustLeverageFlashBorrowType: ambiguous direction (deposit/borrow deltas have opposite signs)');
    }
    if (adjustDepositPosition.isZero() && adjustBorrowPosition.isZero()) {
        throw new Error('determineAdjustLeverageFlashBorrowType: nothing to adjust — current leverage matches target');
    }
    let requiredCollLamports;
    let requiredDebtLamports;
    // Increase (leverage-up) deposits collateral — no redeem. Decrease (deleverage) coll-flash
    // withdraws collateral from the same reserve to repay the flash loan, so it has a redeem leg.
    let redeemCollLamports = new decimal_js_1.default(0);
    if (isIncrease) {
        // Coll-flash borrows enough coll to deposit; debt-flash borrows the debt-side calc.
        requiredCollLamports = adjustDepositPosition.mul(collReserve.getMintFactor()).ceil();
        const debtFlashCalcs = (0, calcs_1.adjustDepositLeverageCalcsDebtFlash)(debtReserve, adjustDepositPosition, adjustBorrowPosition, props.priceDebtToColl, debtReserve.getFlashLoanFee(), slippagePct);
        requiredDebtLamports = debtFlashCalcs.flashBorrowInDebtToken.mul(debtReserve.getMintFactor()).ceil();
    }
    else {
        // Debt-flash borrows the absolute borrow delta; coll-flash uses the coll-side calc.
        requiredDebtLamports = decimal_js_1.default.abs(adjustBorrowPosition).mul(debtReserve.getMintFactor()).ceil();
        const collFlashCalcs = (0, calcs_1.adjustWithdrawLeverageCalcsCollFlash)(adjustDepositPosition, adjustBorrowPosition, priceCollToDebt, collReserve.getFlashLoanFee(), slippagePct);
        requiredCollLamports = collFlashCalcs.flashBorrowInCollToken.mul(collReserve.getMintFactor()).ceil();
        redeemCollLamports = collFlashCalcs.depositTokenWithdrawAmount.mul(collReserve.getMintFactor()).ceil();
    }
    return (0, utils_2.determineFlashBorrowType)(collReserve, debtReserve, requiredCollLamports, requiredDebtLamports, redeemCollLamports);
}
/**
 * Picks a viable `FlashBorrowType` for a repay-with-collateral op given only the client-level intent
 * (which obligation, which reserves, how much debt to repay, the swap price). The SDK computes
 * the per-side required lamports internally — the caller does NOT need to size the flash borrow.
 *
 * Semantics (matches the build flows):
 *  - debt-flash needs to flash borrow the IR-adjusted debt repay amount in debt lamports;
 *  - coll-flash needs to flash borrow enough coll to swap into that same IR-adjusted repay amount,
 *    then withdraws `collWithdrawForFlashRepayLamports` coll from the reserve to repay the flash loan.
 *
 * Both sides are then validated against `isFlashLoanEnabled(reserve)` and against the reserve's
 * available liquidity by `determineFlashBorrowType` — coll-flash must cover flash-borrow + that
 * redeem leg. Coll-flash is preferred when genuinely viable; otherwise it falls back to debt-flash.
 *
 * @throws if neither reserve supports flash borrowing the required amount.
 */
function determineRepayWithCollFlashBorrowType(props) {
    const { kaminoMarket, obligation, debtReserveAddress, collReserveAddress, repayAmount, priceCollToDebt, currentSlot, referrer, slippagePct, } = props;
    const collReserve = kaminoMarket.getExistingReserveByAddress(collReserveAddress);
    const debtReserve = kaminoMarket.getExistingReserveByAddress(debtReserveAddress);
    if (priceCollToDebt.isZero()) {
        throw new Error('determineRepayWithCollFlashBorrowType: priceCollToDebt is zero');
    }
    const { repayAmountLamports } = (0, repay_with_collateral_calcs_1.calcRepayAmountWithSlippage)(kaminoMarket, debtReserve, currentSlot, obligation, repayAmount, referrer);
    // Debt-flash borrows the same IR-adjusted repay amount used by the transaction builder.
    const requiredDebtLamports = repayAmountLamports;
    // Coll-flash repays the debt by swapping flash-borrowed coll, then withdraws collateral from the
    // same reserve to repay the flash loan — `collWithdrawForFlashRepayLamports` is that redeem leg.
    const { flashBorrowInCollLamports: requiredCollLamports, collWithdrawForFlashRepayLamports } = (0, repay_with_collateral_calcs_1.calcRepayWithCollCollFlashSwap)({
        repayAmountLamports,
        swapPriceCollToDebt: priceCollToDebt,
        slippagePct,
        collFlashLoanFee: collReserve.getFlashLoanFee(),
        collMintFactor: collReserve.getMintFactor(),
        debtMintFactor: debtReserve.getMintFactor(),
    });
    return (0, utils_2.determineFlashBorrowType)(collReserve, debtReserve, requiredCollLamports, requiredDebtLamports, collWithdrawForFlashRepayLamports);
}
//# sourceMappingURL=flashBorrowType.js.map