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

import "@openzeppelin/contracts/access/Ownable.sol";
import "@openzeppelin/contracts/security/ReentrancyGuard.sol";

/**
 * @title AERE Consensus Contract
 * @dev Manages consensus mechanism and validator operations
 */
contract AereConsensus is Ownable, ReentrancyGuard {
    
    struct Validator {
        address validatorAddress;
        uint256 stake;
        uint256 votingPower;
        bool isActive;
        uint256 joinedAt;
        uint256 rewardsEarned;
    }
    
    struct ConsensusRound {
        uint256 roundNumber;
        address proposer;
        uint256 blockHeight;
        uint256 timestamp;
        bool finalized;
    }
    
    // State variables
    mapping(address => Validator) public validators;
    mapping(uint256 => ConsensusRound) public consensusRounds;
    address[] public validatorList;
    
    uint256 public constant MINIMUM_STAKE = 100000 * 10**18; // 100K AERE
    uint256 public constant BLOCK_TIME = 12; // 12 seconds
    uint256 public currentRound = 0;
    uint256 public totalStaked = 0;
    
    // Events
    event ValidatorJoined(address indexed validator, uint256 stake);
    event ValidatorLeft(address indexed validator);
    event ValidatorSlashed(address indexed validator, uint256 amount, string reason);
    event ConsensusReached(uint256 indexed round, address indexed proposer);
    event BlockProposed(uint256 indexed blockHeight, address indexed proposer);
    
    constructor() {}
    
    /**
     * @dev Join as validator with minimum stake
     */
    function joinValidator() external payable nonReentrant {
        require(msg.value >= MINIMUM_STAKE, "Insufficient stake");
        require(!validators[msg.sender].isActive, "Already a validator");
        
        validators[msg.sender] = Validator({
            validatorAddress: msg.sender,
            stake: msg.value,
            votingPower: calculateVotingPower(msg.value),
            isActive: true,
            joinedAt: block.timestamp,
            rewardsEarned: 0
        });
        
        validatorList.push(msg.sender);
        totalStaked += msg.value;
        
        emit ValidatorJoined(msg.sender, msg.value);
    }
    
    /**
     * @dev Leave validator set and withdraw stake
     */
    function leaveValidator() external nonReentrant {
        require(validators[msg.sender].isActive, "Not an active validator");
        
        Validator storage validator = validators[msg.sender];
        uint256 stakeToReturn = validator.stake;
        
        validator.isActive = false;
        totalStaked -= stakeToReturn;
        
        // Remove from validator list
        for (uint i = 0; i < validatorList.length; i++) {
            if (validatorList[i] == msg.sender) {
                validatorList[i] = validatorList[validatorList.length - 1];
                validatorList.pop();
                break;
            }
        }
        
        payable(msg.sender).transfer(stakeToReturn);
        emit ValidatorLeft(msg.sender);
    }
    
    /**
     * @dev Propose new block (only active validators)
     */
    function proposeBlock(uint256 blockHeight) external {
        require(validators[msg.sender].isActive, "Not an active validator");
        require(isValidProposer(msg.sender), "Not selected as proposer");
        
        currentRound++;
        consensusRounds[currentRound] = ConsensusRound({
            roundNumber: currentRound,
            proposer: msg.sender,
            blockHeight: blockHeight,
            timestamp: block.timestamp,
            finalized: false
        });
        
        emit BlockProposed(blockHeight, msg.sender);
    }
    
    /**
     * @dev Finalize consensus round
     */
    function finalizeRound(uint256 roundNumber) external onlyOwner {
        require(consensusRounds[roundNumber].proposer != address(0), "Round does not exist");
        require(!consensusRounds[roundNumber].finalized, "Round already finalized");
        
        consensusRounds[roundNumber].finalized = true;
        
        // Distribute rewards to proposer
        address proposer = consensusRounds[roundNumber].proposer;
        validators[proposer].rewardsEarned += calculateBlockReward();
        
        emit ConsensusReached(roundNumber, proposer);
    }
    
    /**
     * @dev Slash validator for malicious behavior
     */
    function slashValidator(address validatorAddress, uint256 slashAmount, string memory reason) external onlyOwner {
        require(validators[validatorAddress].isActive, "Validator not active");
        require(slashAmount <= validators[validatorAddress].stake, "Slash amount too high");
        
        validators[validatorAddress].stake -= slashAmount;
        totalStaked -= slashAmount;
        
        // If stake falls below minimum, remove validator
        if (validators[validatorAddress].stake < MINIMUM_STAKE) {
            validators[validatorAddress].isActive = false;
        }
        
        emit ValidatorSlashed(validatorAddress, slashAmount, reason);
    }
    
    /**
     * @dev Calculate voting power based on stake
     */
    function calculateVotingPower(uint256 stake) public pure returns (uint256) {
        return stake / (10**18); // 1 voting power per AERE
    }
    
    /**
     * @dev Calculate block reward
     */
    function calculateBlockReward() public view returns (uint256) {
        return 16 * 10**18; // 16 AERE per block
    }
    
    /**
     * @dev Check if address is valid proposer for current round
     */
    function isValidProposer(address proposer) public view returns (bool) {
        if (!validators[proposer].isActive) return false;
        
        // Simple round-robin selection based on block number
        uint256 proposerIndex = block.number % validatorList.length;
        return validatorList[proposerIndex] == proposer;
    }
    
    /**
     * @dev Get validator info
     */
    function getValidator(address validatorAddress) external view returns (Validator memory) {
        return validators[validatorAddress];
    }
    
    /**
     * @dev Get active validator count
     */
    function getActiveValidatorCount() external view returns (uint256) {
        uint256 count = 0;
        for (uint i = 0; i < validatorList.length; i++) {
            if (validators[validatorList[i]].isActive) {
                count++;
            }
        }
        return count;
    }
    
    /**
     * @dev Get consensus stats
     */
    function getConsensusStats() external view returns (
        uint256 _totalValidators,
        uint256 _totalStaked,
        uint256 _currentRound,
        uint256 _blockTime
    ) {
        return (
            validatorList.length,
            totalStaked,
            currentRound,
            BLOCK_TIME
        );
    }
}
