// SPDX-License-Identifier: MIT
pragma solidity ^0.8.19;

import "@openzeppelin/contracts/token/ERC20/IERC20.sol";
import "@openzeppelin/contracts/access/Ownable.sol";
import "@openzeppelin/contracts/security/ReentrancyGuard.sol";
import "@openzeppelin/contracts/security/Pausable.sol";
import "./AIPowerRental.sol";

/**
 * @title AITBC Payment Processor
 * @dev Advanced payment processing contract with escrow, automated releases, and dispute resolution
 * @notice Handles AITBC token payments for AI power rental services
 */
contract AITBCPaymentProcessor is Ownable, ReentrancyGuard, Pausable {
    
    // State variables
    IERC20 public aitbcToken;
    AIPowerRental public aiPowerRental;
    
    uint256 public paymentCounter;
    uint256 public platformFeePercentage = 250; // 2.5% in basis points
    uint256 public disputeResolutionFee = 100; // 1% in basis points
    uint256 public minPaymentAmount = 1e15; // 0.001 AITBC minimum
    uint256 public maxPaymentAmount = 1e22; // 10,000 AITBC maximum
    
    // Structs
    struct Payment {
        uint256 paymentId;
        address from;
        address to;
        uint256 amount;
        uint256 platformFee;
        uint256 disputeFee;
        PaymentStatus status;
        uint256 releaseTime;
        uint256 createdTime;
        uint256 confirmedTime;
        bytes32 agreementId;
        string paymentPurpose;
        ReleaseCondition releaseCondition;
        bytes32 conditionHash;
    }
    
    struct EscrowAccount {
        uint256 escrowId;
        address depositor;
        address beneficiary;
        uint256 amount;
        uint256 releaseTime;
        bool isReleased;
        bool isRefunded;
        bytes32 releaseCondition;
        uint256 createdTime;
        EscrowType escrowType;
    }
    
    struct ScheduledPayment {
        uint256 scheduleId;
        uint256 paymentId;
        uint256 nextReleaseTime;
        uint256 releaseInterval;
        uint256 totalReleases;
        uint256 releasedCount;
        bool isActive;
    }
    
    // Enums
    enum PaymentStatus {
        Created,
        Confirmed,
        HeldInEscrow,
        Released,
        Refunded,
        Disputed,
        Cancelled
    }
    
    enum EscrowType {
        Standard,
        PerformanceBased,
        TimeBased,
        Conditional
    }
    
    enum ReleaseCondition {
        Immediate,
        Manual,
        Performance,
        TimeBased,
        DisputeResolution
    }
    
    // Mappings
    mapping(uint256 => Payment) public payments;
    mapping(uint256 => EscrowAccount) public escrowAccounts;
    mapping(uint256 => ScheduledPayment) public scheduledPayments;
    mapping(address => uint256[]) public senderPayments;
    mapping(address => uint256[]) public recipientPayments;
    mapping(bytes32 => uint256) public agreementPayments;
    mapping(address => uint256) public userEscrowBalance;
    mapping(address => bool) public authorizedPayees;
    mapping(address => bool) public authorizedPayers;
    
    // Events
    event PaymentCreated(
        uint256 indexed paymentId,
        address indexed from,
        address indexed to,
        uint256 amount,
        bytes32 agreementId,
        string paymentPurpose
    );
    
    event PaymentConfirmed(
        uint256 indexed paymentId,
        uint256 confirmedTime,
        bytes32 transactionHash
    );
    
    event PaymentReleased(
        uint256 indexed paymentId,
        address indexed to,
        uint256 amount,
        uint256 platformFee
    );
    
    event PaymentRefunded(
        uint256 indexed paymentId,
        address indexed to,
        uint256 amount,
        string reason
    );
    
    event EscrowCreated(
        uint256 indexed escrowId,
        address indexed depositor,
        address indexed beneficiary,
        uint256 amount,
        EscrowType escrowType
    );
    
    event EscrowReleased(
        uint256 indexed escrowId,
        uint256 amount,
        bytes32 conditionHash
    );
    
    event EscrowRefunded(
        uint256 indexed escrowId,
        address indexed depositor,
        uint256 amount,
        string reason
    );
    
    event ScheduledPaymentCreated(
        uint256 indexed scheduleId,
        uint256 indexed paymentId,
        uint256 nextReleaseTime,
        uint256 releaseInterval
    );
    
    event ScheduledPaymentReleased(
        uint256 indexed scheduleId,
        uint256 indexed paymentId,
        uint256 releaseCount
    );
    
    event DisputeInitiated(
        uint256 indexed paymentId,
        address indexed initiator,
        string reason
    );
    
    event DisputeResolved(
        uint256 indexed paymentId,
        uint256 resolutionAmount,
        bool resolvedInFavorOfPayer
    );
    
    event PlatformFeeCollected(
        uint256 indexed paymentId,
        uint256 feeAmount,
        address indexed collector
    );
    
    // Modifiers
    modifier onlyAuthorizedPayer() {
        require(authorizedPayers[msg.sender], "Not authorized payer");
        _;
    }
    
    modifier onlyAuthorizedPayee() {
        require(authorizedPayees[msg.sender], "Not authorized payee");
        _;
    }
    
    modifier paymentExists(uint256 _paymentId) {
        require(_paymentId < paymentCounter, "Payment does not exist");
        _;
    }
    
    modifier validStatus(uint256 _paymentId, PaymentStatus _requiredStatus) {
        require(payments[_paymentId].status == _requiredStatus, "Invalid payment status");
        _;
    }
    
    modifier sufficientBalance(address _user, uint256 _amount) {
        require(aitbcToken.balanceOf(_user) >= _amount, "Insufficient balance");
        _;
    }
    
    modifier sufficientAllowance(address _user, uint256 _amount) {
        require(aitbcToken.allowance(_user, address(this)) >= _amount, "Insufficient allowance");
        _;
    }
    
    // Constructor
    constructor(address _aitbcToken, address _aiPowerRental) {
        aitbcToken = IERC20(_aitbcToken);
        aiPowerRental = AIPowerRental(_aiPowerRental);
        paymentCounter = 0;
    }
    
    /**
     * @dev Creates a new payment
     * @param _to Recipient address
     * @param _amount Payment amount
     * @param _agreementId Associated agreement ID
     * @param _paymentPurpose Purpose of the payment
     * @param _releaseCondition Release condition
     */
    function createPayment(
        address _to,
        uint256 _amount,
        bytes32 _agreementId,
        string memory _paymentPurpose,
        ReleaseCondition _releaseCondition
    ) external onlyAuthorizedPayer sufficientBalance(msg.sender, _amount) sufficientAllowance(msg.sender, _amount) nonReentrant whenNotPaused returns (uint256) {
        require(_amount >= minPaymentAmount, "Amount below minimum");
        require(_amount <= maxPaymentAmount, "Amount above maximum");
        require(_to != address(0), "Invalid recipient");
        require(authorizedPayees[_to], "Recipient not authorized");
        
        uint256 paymentId = paymentCounter++;
        uint256 platformFee = (_amount * platformFeePercentage) / 10000;
        uint256 disputeFee = (_amount * disputeResolutionFee) / 10000;
        uint256 totalAmount = _amount + platformFee + disputeFee;
        
        payments[paymentId] = Payment({
            paymentId: paymentId,
            from: msg.sender,
            to: _to,
            amount: _amount,
            platformFee: platformFee,
            disputeFee: disputeFee,
            status: PaymentStatus.Created,
            releaseTime: 0,
            createdTime: block.timestamp,
            confirmedTime: 0,
            agreementId: _agreementId,
            paymentPurpose: _paymentPurpose,
            releaseCondition: _releaseCondition,
            conditionHash: bytes32(0)
        });
        
        senderPayments[msg.sender].push(paymentId);
        recipientPayments[_to].push(paymentId);
        
        if (_agreementId != bytes32(0)) {
            agreementPayments[_agreementId] = paymentId;
        }
        
        // Transfer tokens to contract
        require(
            aitbcToken.transferFrom(msg.sender, address(this), totalAmount),
            "Payment transfer failed"
        );
        
        emit PaymentCreated(paymentId, msg.sender, _to, _amount, _agreementId, _paymentPurpose);
        
        return paymentId;
    }
    
    /**
     * @dev Confirms a payment with transaction hash
     * @param _paymentId ID of the payment
     * @param _transactionHash Blockchain transaction hash
     */
    function confirmPayment(uint256 _paymentId, bytes32 _transactionHash) 
        external 
        paymentExists(_paymentId)
        validStatus(_paymentId, PaymentStatus.Created)
        nonReentrant 
    {
        Payment storage payment = payments[_paymentId];
        
        require(msg.sender == payment.from, "Only payer can confirm");
        
        payment.status = PaymentStatus.Confirmed;
        payment.confirmedTime = block.timestamp;
        payment.conditionHash = _transactionHash;
        
        // Handle immediate release
        if (payment.releaseCondition == ReleaseCondition.Immediate) {
            _releasePayment(_paymentId);
        } else if (payment.releaseCondition == ReleaseCondition.TimeBased) {
            payment.status = PaymentStatus.HeldInEscrow;
            payment.releaseTime = block.timestamp + 1 hours; // Default 1 hour hold
        } else {
            payment.status = PaymentStatus.HeldInEscrow;
        }
        
        emit PaymentConfirmed(_paymentId, block.timestamp, _transactionHash);
    }
    
    /**
     * @dev Releases a payment to the recipient
     * @param _paymentId ID of the payment
     */
    function releasePayment(uint256 _paymentId) 
        external 
        paymentExists(_paymentId)
        nonReentrant 
    {
        Payment storage payment = payments[_paymentId];
        
        require(
            payment.status == PaymentStatus.Confirmed || 
            payment.status == PaymentStatus.HeldInEscrow,
            "Payment not ready for release"
        );
        
        if (payment.releaseCondition == ReleaseCondition.Manual) {
            require(msg.sender == payment.from, "Only payer can release manually");
        } else if (payment.releaseCondition == ReleaseCondition.TimeBased) {
            require(block.timestamp >= payment.releaseTime, "Release time not reached");
        }
        
        _releasePayment(_paymentId);
    }
    
    /**
     * @dev Creates an escrow account
     * @param _beneficiary Beneficiary address
     * @param _amount Amount to lock in escrow
     * @param _releaseTime Release time (0 for no time limit)
     * @param _escrowType Type of escrow
     * @param _releaseCondition Release condition hash
     */
    function createEscrow(
        address _beneficiary,
        uint256 _amount,
        uint256 _releaseTime,
        EscrowType _escrowType,
        bytes32 _releaseCondition
    ) external onlyAuthorizedPayer sufficientBalance(msg.sender, _amount) sufficientAllowance(msg.sender, _amount) nonReentrant whenNotPaused returns (uint256) {
        require(_beneficiary != address(0), "Invalid beneficiary");
        require(_amount >= minPaymentAmount, "Amount below minimum");
        
        uint256 escrowId = paymentCounter++;
        
        escrowAccounts[escrowId] = EscrowAccount({
            escrowId: escrowId,
            depositor: msg.sender,
            beneficiary: _beneficiary,
            amount: _amount,
            releaseTime: _releaseTime,
            isReleased: false,
            isRefunded: false,
            releaseCondition: _releaseCondition,
            createdTime: block.timestamp,
            escrowType: _escrowType
        });
        
        // Transfer tokens to contract
        require(
            aitbcToken.transferFrom(msg.sender, address(this), _amount),
            "Escrow transfer failed"
        );
        
        userEscrowBalance[msg.sender] += _amount;
        
        emit EscrowCreated(escrowId, msg.sender, _beneficiary, _amount, _escrowType);
        
        return escrowId;
    }
    
    /**
     * @dev Releases escrow to beneficiary
     * @param _escrowId ID of the escrow account
     */
    function releaseEscrow(uint256 _escrowId) 
        external 
        nonReentrant 
    {
        EscrowAccount storage escrow = escrowAccounts[_escrowId];
        
        require(!escrow.isReleased, "Escrow already released");
        require(!escrow.isRefunded, "Escrow already refunded");
        require(
            escrow.releaseTime == 0 || block.timestamp >= escrow.releaseTime,
            "Release time not reached"
        );
        
        escrow.isReleased = true;
        userEscrowBalance[escrow.depositor] -= escrow.amount;
        
        require(
            aitbcToken.transfer(escrow.beneficiary, escrow.amount),
            "Escrow release failed"
        );
        
        emit EscrowReleased(_escrowId, escrow.amount, escrow.releaseCondition);
    }
    
    /**
     * @dev Refunds escrow to depositor
     * @param _escrowId ID of the escrow account
     * @param _reason Reason for refund
     */
    function refundEscrow(uint256 _escrowId, string memory _reason) 
        external 
        nonReentrant 
    {
        EscrowAccount storage escrow = escrowAccounts[_escrowId];
        
        require(!escrow.isReleased, "Escrow already released");
        require(!escrow.isRefunded, "Escrow already refunded");
        require(
            msg.sender == escrow.depositor || msg.sender == owner(),
            "Only depositor or owner can refund"
        );
        
        escrow.isRefunded = true;
        userEscrowBalance[escrow.depositor] -= escrow.amount;
        
        require(
            aitbcToken.transfer(escrow.depositor, escrow.amount),
            "Escrow refund failed"
        );
        
        emit EscrowRefunded(_escrowId, escrow.depositor, escrow.amount, _reason);
    }
    
    /**
     * @dev Initiates a dispute for a payment
     * @param _paymentId ID of the payment
     * @param _reason Reason for dispute
     */
    function initiateDispute(uint256 _paymentId, string memory _reason) 
        external 
        paymentExists(_paymentId)
        nonReentrant 
    {
        Payment storage payment = payments[_paymentId];
        
        require(
            payment.status == PaymentStatus.Confirmed || 
            payment.status == PaymentStatus.HeldInEscrow,
            "Cannot dispute this payment"
        );
        
        require(
            msg.sender == payment.from || msg.sender == payment.to,
            "Only payment participants can dispute"
        );
        
        payment.status = PaymentStatus.Disputed;
        
        emit DisputeInitiated(_paymentId, msg.sender, _reason);
    }
    
    /**
     * @dev Resolves a dispute
     * @param _paymentId ID of the disputed payment
     * @param _resolutionAmount Amount to award to the winner
     * @param _resolveInFavorOfPayer True if resolving in favor of payer
     */
    function resolveDispute(
        uint256 _paymentId,
        uint256 _resolutionAmount,
        bool _resolveInFavorOfPayer
    ) external onlyOwner paymentExists(_paymentId) nonReentrant {
        Payment storage payment = payments[_paymentId];
        
        require(payment.status == PaymentStatus.Disputed, "Payment not disputed");
        require(_resolutionAmount <= payment.amount, "Resolution amount too high");
        
        address winner = _resolveInFavorOfPayer ? payment.from : payment.to;
        address loser = _resolveInFavorOfPayer ? payment.to : payment.from;
        
        // Calculate refund for loser
        uint256 refundAmount = payment.amount - _resolutionAmount;
        
        // Transfer resolution amount to winner
        if (_resolutionAmount > 0) {
            require(
                aitbcToken.transfer(winner, _resolutionAmount),
                "Resolution payment failed"
            );
        }
        
        // Refund remaining amount to loser
        if (refundAmount > 0) {
            require(
                aitbcToken.transfer(loser, refundAmount),
                "Refund payment failed"
            );
        }
        
        payment.status = PaymentStatus.Released;
        
        emit DisputeResolved(_paymentId, _resolutionAmount, _resolveInFavorOfPayer);
    }
    
    /**
     * @dev Claims platform fees
     * @param _paymentId ID of the payment
     */
    function claimPlatformFee(uint256 _paymentId) 
        external 
        onlyOwner 
        paymentExists(_paymentId)
        nonReentrant 
    {
        Payment storage payment = payments[_paymentId];
        
        require(payment.status == PaymentStatus.Released, "Payment not released");
        require(payment.platformFee > 0, "No platform fee to claim");
        
        uint256 feeAmount = payment.platformFee;
        payment.platformFee = 0;
        
        require(
            aitbcToken.transfer(owner(), feeAmount),
            "Platform fee transfer failed"
        );
        
        emit PlatformFeeCollected(_paymentId, feeAmount, owner());
    }
    
    /**
     * @dev Authorizes a payee
     * @param _payee Address to authorize
     */
    function authorizePayee(address _payee) external onlyOwner {
        authorizedPayees[_payee] = true;
    }
    
    /**
     * @dev Revokes payee authorization
     * @param _payee Address to revoke
     */
    function revokePayee(address _payee) external onlyOwner {
        authorizedPayees[_payee] = false;
    }
    
    /**
     * @dev Authorizes a payer
     * @param _payer Address to authorize
     */
    function authorizePayer(address _payer) external onlyOwner {
        authorizedPayers[_payer] = true;
    }
    
    /**
     * @dev Revokes payer authorization
     * @param _payer Address to revoke
     */
    function revokePayer(address _payer) external onlyOwner {
        authorizedPayers[_payer] = false;
    }
    
    /**
     * @dev Updates platform fee percentage
     * @param _newFee New fee percentage in basis points
     */
    function updatePlatformFee(uint256 _newFee) external onlyOwner {
        require(_newFee <= 1000, "Fee too high"); // Max 10%
        platformFeePercentage = _newFee;
    }
    
    /**
     * @dev Emergency pause function
     */
    function pause() external onlyOwner {
        _pause();
    }
    
    /**
     * @dev Unpause function
     */
    function unpause() external onlyOwner {
        _unpause();
    }
    
    // Internal functions
    
    function _releasePayment(uint256 _paymentId) internal {
        Payment storage payment = payments[_paymentId];
        
        payment.status = PaymentStatus.Released;
        
        // Transfer amount to recipient
        require(
            aitbcToken.transfer(payment.to, payment.amount),
            "Payment transfer failed"
        );
        
        // Transfer platform fee to owner
        if (payment.platformFee > 0) {
            require(
                aitbcToken.transfer(owner(), payment.platformFee),
                "Platform fee transfer failed"
            );
        }
        
        emit PaymentReleased(_paymentId, payment.to, payment.amount, payment.platformFee);
    }
    
    // View functions
    
    /**
     * @dev Gets payment details
     * @param _paymentId ID of the payment
     */
    function getPayment(uint256 _paymentId) 
        external 
        view 
        paymentExists(_paymentId) 
        returns (Payment memory) 
    {
        return payments[_paymentId];
    }
    
    /**
     * @dev Gets escrow account details
     * @param _escrowId ID of the escrow account
     */
    function getEscrowAccount(uint256 _escrowId) 
        external 
        view 
        returns (EscrowAccount memory) 
    {
        return escrowAccounts[_escrowId];
    }
    
    /**
     * @dev Gets all payments for a sender
     * @param _sender Address of the sender
     */
    function getSenderPayments(address _sender) 
        external 
        view 
        returns (uint256[] memory) 
    {
        return senderPayments[_sender];
    }
    
    /**
     * @dev Gets all payments for a recipient
     * @param _recipient Address of the recipient
     */
    function getRecipientPayments(address _recipient) 
        external 
        view 
        returns (uint256[] memory) 
    {
        return recipientPayments[_recipient];
    }
    
    /**
     * @dev Gets payment associated with an agreement
     * @param _agreementId ID of the agreement
     */
    function getAgreementPayment(bytes32 _agreementId) 
        external 
        view 
        returns (uint256) 
    {
        return agreementPayments[_agreementId];
    }
    
    /**
     * @dev Gets user's escrow balance
     * @param _user Address of the user
     */
    function getUserEscrowBalance(address _user) 
        external 
        view 
        returns (uint256) 
    {
        return userEscrowBalance[_user];
    }
}
