pragma solidity ^0.8.19;
import {Test} from "forge-std/Test.sol";
import {console2} from "forge-std/console2.sol";
import {StabilityPool} from "../../contracts/core/pools/StabilityPool/StabilityPool.sol";
import {crvUSDToken} from "../../contracts/mocks/core/tokens/crvUSDToken.sol";
import {RAACToken} from "../../contracts/core/tokens/RAACToken.sol";
import {RAACHousePrices} from "../../contracts/core/primitives/RAACHousePrices.sol";
import {RAACNFT} from "../../contracts/core/tokens/RAACNFT.sol";
import {RToken} from "../../contracts/core/tokens/RToken.sol";
import {DebtToken} from "../../contracts/core/tokens/DebtToken.sol";
import {DEToken} from "../../contracts/core/tokens/DEToken.sol";
import {LendingPool} from "../../contracts/core/pools/LendingPool/LendingPool.sol";
import {RAACMinter, IRAACMinter} from "../../contracts/core/minters/RAACMinter/RAACMinter.sol";
import {PercentageMath} from "../../contracts/libraries/math/PercentageMath.sol";
import {ILendingPool} from "../../contracts/interfaces/core/pools/LendingPool/ILendingPool.sol";
import {IStabilityPool} from "../../contracts/interfaces/core/pools/StabilityPool/IStabilityPool.sol";
contract FoundryTest is Test {
    using PercentageMath for uint256;
    StabilityPool public stabilityPool;
    LendingPool public lendingPool;
    RAACMinter public raacMinter;
    crvUSDToken public crvusd;
    RToken public rToken;
    DEToken public deToken;
    RAACToken public raacToken;
    RAACNFT public raacNFT;
    DebtToken public debtToken;
    RAACHousePrices public raacHousePrices;
    address public owner;
    address public user1;
    address public user2;
    address public user3;
    address public treasury;
    uint256 public constant INITIAL_BALANCE = 1000e18;
    uint256 public constant INITIAL_PRIME_RATE = 1e27;
    uint256 constant INITIAL_BATCH_SIZE = 3;
    uint256 constant HOUSE_PRICE = 100e18;
    uint256 constant TOKEN_ID = 1;
    function setUp() public {
        
        owner = address(this);
        user1 = makeAddr("user1");
        user2 = makeAddr("user2");
        user3 = makeAddr("user3");
        treasury = makeAddr("treasury");
        
        crvusd = new crvUSDToken(owner);
        crvusd.setMinter(owner);
        raacToken = new RAACToken(owner, 100, 50);
        
        raacHousePrices = new RAACHousePrices(owner);
        raacHousePrices.setOracle(owner);
        
        raacHousePrices.setHousePrice(TOKEN_ID, HOUSE_PRICE);
        
        raacNFT = new RAACNFT(address(crvusd), address(raacHousePrices), owner);
        
        rToken = new RToken("RToken", "RToken", owner, address(crvusd));
        debtToken = new DebtToken("DebtToken", "DT", owner);
        deToken = new DEToken("DEToken", "DEToken", owner, address(rToken));
        
        lendingPool = new LendingPool(
            address(crvusd),
            address(rToken),
            address(debtToken),
            address(raacNFT),
            address(raacHousePrices),
            INITIAL_PRIME_RATE
        );
        stabilityPool = new StabilityPool(owner);
        
        vm.warp(block.timestamp + 2 days);
        
        raacMinter = new RAACMinter(address(raacToken), address(stabilityPool), address(lendingPool), owner);
        
        lendingPool.setStabilityPool(address(stabilityPool));
        rToken.setReservePool(address(lendingPool));
        debtToken.setReservePool(address(lendingPool));
        rToken.transferOwnership(address(lendingPool));
        debtToken.transferOwnership(address(lendingPool));
        deToken.setStabilityPool(address(stabilityPool));
        deToken.transferOwnership(address(stabilityPool));
        
        stabilityPool.initialize(
            address(rToken),
            address(deToken),
            address(raacToken),
            address(raacMinter),
            address(crvusd),
            address(lendingPool)
        );
        
        raacToken.setMinter(address(raacMinter));
        raacToken.manageWhitelist(address(stabilityPool), true);
        
        _setupInitialBalancesAndAllowances();
    }
    function test_WithdrawNFTLeavesUserUnderCollateralized() public {
        
        uint256 housePriceTwo = 18e18;
        raacHousePrices.setHousePrice(TOKEN_ID + 1, housePriceTwo);
        address borrower = makeAddr("borrower");
        
        
        uint256 requiredStartAmount = HOUSE_PRICE + housePriceTwo;
        crvusd.mint(borrower, requiredStartAmount);
        assertEq(crvusd.balanceOf(borrower), requiredStartAmount);
        
        vm.startPrank(borrower);
        crvusd.approve(address(raacNFT), requiredStartAmount);
        raacNFT.mint(TOKEN_ID, HOUSE_PRICE);
        raacNFT.mint(TOKEN_ID + 1, housePriceTwo);
        assertEq(raacNFT.balanceOf(borrower), 2);
        
        assertEq(crvusd.balanceOf(borrower), 0);
        
        raacNFT.approve(address(lendingPool), TOKEN_ID);
        raacNFT.approve(address(lendingPool), TOKEN_ID + 1);
        lendingPool.depositNFT(TOKEN_ID);
        lendingPool.depositNFT(TOKEN_ID + 1);
        assertEq(raacNFT.balanceOf(address(lendingPool)), 2);
        
        uint256 totalCollateralValue = lendingPool.getUserCollateralValue(borrower);
        assertEq(totalCollateralValue, requiredStartAmount);
        
        
        uint256 borrowedAmount = totalCollateralValue.percentMul(lendingPool.liquidationThreshold());
        console2.log("borrowedAmount", borrowedAmount);
        lendingPool.borrow(borrowedAmount);
        uint256 crvusdBalanceAfterBorrow = crvusd.balanceOf(borrower);
        assertEq(crvusdBalanceAfterBorrow, borrowedAmount);
        
        vm.warp(block.timestamp + 100 days);
        
        lendingPool.updateState();
        
        uint256 userDebt = lendingPool.getUserDebt(borrower);
        console2.log("userDebt", userDebt);
        
        
        uint256 minAmountToRepayDebt = userDebt.percentMul(lendingPool.liquidationThreshold());
        console2.log("minAmountToRepayDebt", minAmountToRepayDebt);
        
        crvusd.approve(address(lendingPool), minAmountToRepayDebt);
        lendingPool.repay(minAmountToRepayDebt);
        assertEq(crvusd.balanceOf(borrower), crvusdBalanceAfterBorrow - minAmountToRepayDebt);
        
        uint256 healthFactor = lendingPool.calculateHealthFactor(borrower);
        console2.log("healthFactor after repay", healthFactor);
        
        lendingPool.withdrawNFT(TOKEN_ID);
        assertEq(raacNFT.balanceOf(address(lendingPool)), 1);
        assertEq(raacNFT.balanceOf(borrower), 1);
        vm.stopPrank();
        
        userDebt = lendingPool.getUserDebt(borrower);
        console2.log("userDebt after repay", userDebt);
        
        uint256 collateralValue = lendingPool.getUserCollateralValue(borrower);
        console2.log("collateralValue after repay", collateralValue);
        
        assertLt(collateralValue, userDebt);
        
        healthFactor = lendingPool.calculateHealthFactor(borrower);
        console2.log("healthFactor after withdraw", healthFactor);
    }
    function _setupInitialBalancesAndAllowances() internal {
        
        crvusd.mint(user1, INITIAL_BALANCE);
        crvusd.mint(user2, INITIAL_BALANCE);
        crvusd.mint(user3, INITIAL_BALANCE);
        
        vm.startPrank(user1);
        crvusd.approve(address(lendingPool), type(uint256).max);
        lendingPool.deposit(INITIAL_BALANCE);
        rToken.approve(address(stabilityPool), type(uint256).max);
        vm.stopPrank();
        vm.startPrank(user2);
        crvusd.approve(address(lendingPool), type(uint256).max);
        lendingPool.deposit(INITIAL_BALANCE);
        rToken.approve(address(stabilityPool), type(uint256).max);
        vm.stopPrank();
        vm.startPrank(user3);
        crvusd.approve(address(lendingPool), type(uint256).max);
        lendingPool.deposit(INITIAL_BALANCE);
        rToken.approve(address(stabilityPool), type(uint256).max);
        vm.stopPrank();
    }
}