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

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

/**
 * @title AERE Governance Contract
 * @dev Manages decentralized governance and proposals
 */
contract AereGovernance is Ownable, ReentrancyGuard {
    
    AereToken public aereToken;
    
    enum ProposalStatus { Active, Passed, Failed, Executed, Cancelled }
    enum VoteType { Yes, No, Abstain }
    
    struct Proposal {
        uint256 id;
        string title;
        string description;
        address proposer;
        uint256 startTime;
        uint256 endTime;
        uint256 yesVotes;
        uint256 noVotes;
        uint256 abstainVotes;
        ProposalStatus status;
        uint256 executionTime;
        bytes executionData;
    }
    
    struct Vote {
        VoteType voteType;
        uint256 votingPower;
        bool hasVoted;
    }
    
    // State variables
    mapping(uint256 => Proposal) public proposals;
    mapping(uint256 => mapping(address => Vote)) public votes;
    mapping(address => uint256) public votingPower;
    
    uint256 public proposalCount = 0;
    uint256 public constant VOTING_PERIOD = 7 days;
    uint256 public constant EXECUTION_DELAY = 2 days;
    uint256 public constant PROPOSAL_THRESHOLD = 100000 * 10**18; // 100K AERE
    uint256 public constant QUORUM_PERCENTAGE = 20; // 20%
    
    // Events
    event ProposalCreated(uint256 indexed proposalId, address indexed proposer, string title);
    event VoteCast(uint256 indexed proposalId, address indexed voter, VoteType voteType, uint256 votingPower);
    event ProposalExecuted(uint256 indexed proposalId);
    event ProposalCancelled(uint256 indexed proposalId);
    
    constructor(address _aereToken) {
        aereToken = AereToken(_aereToken);
    }
    
    /**
     * @dev Create new governance proposal
     */
    function createProposal(
        string memory title,
        string memory description,
        bytes memory executionData
    ) external returns (uint256) {
        require(aereToken.balanceOf(msg.sender) >= PROPOSAL_THRESHOLD, "Insufficient tokens to propose");
        
        proposalCount++;
        uint256 proposalId = proposalCount;
        
        proposals[proposalId] = Proposal({
            id: proposalId,
            title: title,
            description: description,
            proposer: msg.sender,
            startTime: block.timestamp,
            endTime: block.timestamp + VOTING_PERIOD,
            yesVotes: 0,
            noVotes: 0,
            abstainVotes: 0,
            status: ProposalStatus.Active,
            executionTime: 0,
            executionData: executionData
        });
        
        emit ProposalCreated(proposalId, msg.sender, title);
        return proposalId;
    }
    
    /**
     * @dev Cast vote on proposal
     */
    function castVote(uint256 proposalId, VoteType voteType) external {
        Proposal storage proposal = proposals[proposalId];
        require(proposal.status == ProposalStatus.Active, "Proposal not active");
        require(block.timestamp <= proposal.endTime, "Voting period ended");
        require(!votes[proposalId][msg.sender].hasVoted, "Already voted");
        
        uint256 voterPower = aereToken.balanceOf(msg.sender);
        require(voterPower > 0, "No voting power");
        
        votes[proposalId][msg.sender] = Vote({
            voteType: voteType,
            votingPower: voterPower,
            hasVoted: true
        });
        
        if (voteType == VoteType.Yes) {
            proposal.yesVotes += voterPower;
        } else if (voteType == VoteType.No) {
            proposal.noVotes += voterPower;
        } else {
            proposal.abstainVotes += voterPower;
        }
        
        emit VoteCast(proposalId, msg.sender, voteType, voterPower);
    }
    
    /**
     * @dev Finalize proposal after voting period
     */
    function finalizeProposal(uint256 proposalId) external {
        Proposal storage proposal = proposals[proposalId];
        require(proposal.status == ProposalStatus.Active, "Proposal not active");
        require(block.timestamp > proposal.endTime, "Voting still active");
        
        uint256 totalVotes = proposal.yesVotes + proposal.noVotes + proposal.abstainVotes;
        uint256 quorumRequired = (aereToken.getCirculatingSupply() * QUORUM_PERCENTAGE) / 100;
        
        if (totalVotes >= quorumRequired && proposal.yesVotes > proposal.noVotes) {
            proposal.status = ProposalStatus.Passed;
            proposal.executionTime = block.timestamp + EXECUTION_DELAY;
        } else {
            proposal.status = ProposalStatus.Failed;
        }
    }
    
    /**
     * @dev Execute passed proposal
     */
    function executeProposal(uint256 proposalId) external {
        Proposal storage proposal = proposals[proposalId];
        require(proposal.status == ProposalStatus.Passed, "Proposal not passed");
        require(block.timestamp >= proposal.executionTime, "Execution delay not met");
        
        proposal.status = ProposalStatus.Executed;
        
        // Execute proposal logic here
        if (proposal.executionData.length > 0) {
            (bool success,) = address(this).call(proposal.executionData);
            require(success, "Execution failed");
        }
        
        emit ProposalExecuted(proposalId);
    }
    
    /**
     * @dev Cancel proposal (only proposer or owner)
     */
    function cancelProposal(uint256 proposalId) external {
        Proposal storage proposal = proposals[proposalId];
        require(
            msg.sender == proposal.proposer || msg.sender == owner(),
            "Not authorized to cancel"
        );
        require(proposal.status == ProposalStatus.Active, "Proposal not active");
        
        proposal.status = ProposalStatus.Cancelled;
        emit ProposalCancelled(proposalId);
    }
    
    /**
     * @dev Get proposal details
     */
    function getProposal(uint256 proposalId) external view returns (Proposal memory) {
        return proposals[proposalId];
    }
    
    /**
     * @dev Get vote details
     */
    function getVote(uint256 proposalId, address voter) external view returns (Vote memory) {
        return votes[proposalId][voter];
    }
    
    /**
     * @dev Check if proposal has quorum
     */
    function hasQuorum(uint256 proposalId) external view returns (bool) {
        Proposal memory proposal = proposals[proposalId];
        uint256 totalVotes = proposal.yesVotes + proposal.noVotes + proposal.abstainVotes;
        uint256 quorumRequired = (aereToken.getCirculatingSupply() * QUORUM_PERCENTAGE) / 100;
        return totalVotes >= quorumRequired;
    }
    
    /**
     * @dev Get governance statistics
     */
    function getGovernanceStats() external view returns (
        uint256 _totalProposals,
        uint256 _activeProposals,
        uint256 _totalVoters,
        uint256 _quorumPercentage
    ) {
        uint256 activeCount = 0;
        for (uint256 i = 1; i <= proposalCount; i++) {
            if (proposals[i].status == ProposalStatus.Active) {
                activeCount++;
            }
        }
        
        return (
            proposalCount,
            activeCount,
            aereToken.getCirculatingSupply() / (10**18), // Convert to readable number
            QUORUM_PERCENTAGE
        );
    }
}
