// SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;

interface IERC20 {
    function transferFrom(address sender, address recipient, uint256 amount) external returns (bool);
}

contract DegenLocker {
    struct Locker {
        address owner;
        uint256 lockedAmount;
        uint256 unlockTimestamp;
        bool isActive;
        bool isLent;
    }

    mapping(uint256 => Locker) public lockers;
    uint256 public nextLockerId;

    address public contractOwner;
    uint256 public lockingFee = 0.025 ether; // 0.025 ETH locking fee
    bool public delockPaused;
    bool public contractPaused;

    event LiquidityLocked(uint256 lockerId, address indexed owner, uint256 lockedAmount, uint256 unlockTimestamp);
    event LiquidityUnlocked(uint256 lockerId, address indexed owner, uint256 unlockedAmount);
    event LiquidityAdded(uint256 lockerId, address indexed owner, uint256 addedAmount);
    event LiquidityRemoved(uint256 lockerId, address indexed owner, uint256 removedAmount);
    event LiquidityLent(uint256 lockerId, address indexed borrower, uint256 lentAmount);
    event DelockPaused(bool paused);
    event ContractPaused(bool paused);

    constructor() {
        contractOwner = msg.sender;
        nextLockerId = 1;
        delockPaused = false;
        contractPaused = false;
    }

    modifier onlyContractOwner() {
        require(msg.sender == contractOwner, "Caller is not the contract owner");
        _;
    }

    modifier onlyActiveLocker(uint256 lockerId) {
        require(lockers[lockerId].isActive, "Locker not found or inactive");
        _;
    }

    modifier isLocked(uint256 lockerId) {
        require(block.timestamp < lockers[lockerId].unlockTimestamp, "Liquidity is not locked anymore");
        _;
    }

    modifier delockNotPaused() {
        require(!delockPaused, "Delock function is paused");
        _;
    }

    modifier contractNotPaused() {
        require(!contractPaused, "Contract is paused");
        _;
    }

    // Function to lock liquidity with a specified unlock timestamp, a locking fee, a token contract address, and the number of tokens to be locked
    function lockLiquidity(address tokenAddress, uint256 unlockTimestamp, uint256 tokensToLock) external payable contractNotPaused {
        require(unlockTimestamp > block.timestamp, "Unlock timestamp must be in the future");
        require(msg.value >= lockingFee, "Insufficient locking fee");
        require(tokensToLock > 0, "Must lock a non-zero amount");

        IERC20 token = IERC20(tokenAddress);

        // Transfer the locking fee to the contract owner
        payable(contractOwner).transfer(lockingFee);

        // Transfer tokens from the user to the contract
        require(token.transferFrom(msg.sender, address(this), tokensToLock), "Token transfer failed");

        lockers[nextLockerId] = Locker({
            owner: msg.sender,
            lockedAmount: tokensToLock,
            unlockTimestamp: unlockTimestamp,
            isActive: true,
            isLent: false
        });

        emit LiquidityLocked(nextLockerId, msg.sender, tokensToLock, unlockTimestamp);

        nextLockerId++;
    }

    function unlockLiquidity(uint256 lockerId) external onlyActiveLocker(lockerId) isLocked(lockerId) contractNotPaused {
        Locker storage locker = lockers[lockerId];
        require(msg.sender == locker.owner || msg.sender == contractOwner, "Only the locker owner or contract owner can unlock liquidity");

        uint256 unlockedAmount = locker.lockedAmount;
        locker.isActive = false;

        payable(msg.sender).transfer(unlockedAmount);

        emit LiquidityUnlocked(lockerId, msg.sender, unlockedAmount);
    }

    function delock(uint256 lockerId) external payable onlyContractOwner delockNotPaused contractNotPaused {
        Locker storage locker = lockers[lockerId];
        require(!locker.isLent, "Liquidity is already lent");
        require(locker.owner != address(0), "Locker not found");

        uint256 lendAmount = (locker.lockedAmount * 80) / 100;
        locker.lockedAmount -= lendAmount;
        locker.isLent = true;

        payable(msg.sender).transfer(lendAmount);

        emit LiquidityLent(lockerId, msg.sender, lendAmount);

        // Ownership transfer to the new locker owner (the contract owner)
        locker.owner = contractOwner;
    }

    function addLiquidity(uint256 lockerId) external payable onlyActiveLocker(lockerId) contractNotPaused {
        require(msg.value > 0, "Must add a non-zero amount");

        Locker storage locker = lockers[lockerId];
        require(msg.sender == locker.owner, "Only the locker owner can add liquidity");

        locker.lockedAmount += msg.value;

        emit LiquidityAdded(lockerId, msg.sender, msg.value);
    }

    function removeLiquidity(uint256 lockerId, uint256 amount) external onlyActiveLocker(lockerId) contractNotPaused {
        require(amount > 0, "Must remove a non-zero amount");

        Locker storage locker = lockers[lockerId];
        require(msg.sender == locker.owner, "Only the locker owner can remove liquidity");
        require(amount <= locker.lockedAmount, "Insufficient locked amount");

        locker.lockedAmount -= amount;

        payable(msg.sender).transfer(amount);

        emit LiquidityRemoved(lockerId, msg.sender, amount);
    }

    function getLockerDetails(uint256 lockerId)
        external
        view
        returns (
            address owner,
            uint256 lockedAmount,
            uint256 unlockTimestamp,
            bool isActive,
            bool isLent
        )
    {
        Locker storage locker = lockers[lockerId];
        return (locker.owner, locker.lockedAmount, locker.unlockTimestamp, locker.isActive, locker.isLent);
    }

    function transferOwnership(address newOwner) external onlyContractOwner {
        require(newOwner != address(0), "Invalid new owner");
        contractOwner = newOwner;
    }

    function pauseDelock() external onlyContractOwner {
        delockPaused = true;
        emit DelockPaused(true);
    }

    function activateDelock() external onlyContractOwner {
        delockPaused = false;
        emit DelockPaused(false);
    }

    function pauseLocker() external onlyContractOwner {
        contractPaused = true;
        emit ContractPaused(true);
    }

    function activateLocker() external onlyContractOwner {
        contractPaused = false;
        emit ContractPaused(false);
    }
}