// SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;
/**
 * @title StakedVotingDAO
 * @notice Decentralized governance system with staked voting mechanism
 * @dev This contract implements a DAO governance system where voting power
 * is determined by staked token amounts
 */
contract StakedVotingDAO {
    struct Proposal {
        uint256 id;
        address proposer;
        string description;
        uint256 forVotes;
        uint256 againstVotes;
        uint256 startTime;
        uint256 endTime;
        bool executed;
        bool canceled;
        mapping(address => Receipt) receipts;
    }
    struct Receipt {
        bool hasVoted;
        bool support;
        uint256 votes;
    }
    event ProposalCreated(
        uint256 indexed proposalId,
        address indexed proposer,
        string description,
        uint256 startTime,
        uint256 endTime
    );
    event VoteCast(
        address indexed voter,
        uint256 indexed proposalId,
        bool support,
        uint256 votes
    );
    event ProposalExecuted(uint256 indexed proposalId);
    event ProposalCanceled(uint256 indexed proposalId);
    uint256 public proposalCount;
    mapping(uint256 => Proposal) public proposals;

    // Minimum tokens required to create a proposal
    uint256 public proposalThreshold;

    // Voting period in seconds
    uint256 public votingPeriod;

    // Quorum required for proposal to pass
    uint256 public quorumVotes;
    constructor(
        uint256 _proposalThreshold,
        uint256 _votingPeriod,
        uint256 _quorumVotes
    ) {
        proposalThreshold = _proposalThreshold;
        votingPeriod = _votingPeriod;
        quorumVotes = _quorumVotes;
    }
    function propose(string memory description) external returns (uint256) {
        require(
            getVotes(msg.sender) >= proposalThreshold,
            "Insufficient voting power to create proposal"
        );
        proposalCount++;
        uint256 proposalId = proposalCount;
        Proposal storage newProposal = proposals[proposalId];
        newProposal.id = proposalId;
        newProposal.proposer = msg.sender;
        newProposal.description = description;
        newProposal.startTime = block.timestamp;
        newProposal.endTime = block.timestamp + votingPeriod;
        emit ProposalCreated(
            proposalId,
            msg.sender,
            description,
            newProposal.startTime,
            newProposal.endTime
        );
        return proposalId;
    }
    function castVote(uint256 proposalId, bool support) external {
        require(
            state(proposalId) == ProposalState.Active,
            "Voting is not active"
        );
        uint256 votes = getVotes(msg.sender);
        require(votes > 0, "No voting power");
        Proposal storage proposal = proposals[proposalId];
        Receipt storage receipt = proposal.receipts[msg.sender];
        require(!receipt.hasVoted, "Already voted");
        if (support) {
            proposal.forVotes += votes;
        } else {
            proposal.againstVotes += votes;
        }
        receipt.hasVoted = true;
        receipt.support = support;
        receipt.votes = votes;
        emit VoteCast(msg.sender, proposalId, support, votes);
    }
    function execute(uint256 proposalId) external {
        require(
            state(proposalId) == ProposalState.Succeeded,
            "Proposal not succeeded"
        );
        Proposal storage proposal = proposals[proposalId];
        proposal.executed = true;
        // Execute the proposal logic here
        // This would typically involve calling external contracts
        emit ProposalExecuted(proposalId);
    }
    function cancel(uint256 proposalId) external {
        Proposal storage proposal = proposals[proposalId];
        require(
            msg.sender == proposal.proposer,
            "Only proposer can cancel"
        );
        require(
            state(proposalId) == ProposalState.Active,
            "Proposal not active"
        );
        proposal.canceled = true;
        emit ProposalCanceled(proposalId);
    }
    function getVotes(address account) public view returns (uint256) {
        // This would typically query a staking contract
        // For now, return a placeholder value
        return 1000; // Placeholder: 1000 voting power
    }
    function state(uint256 proposalId) public view returns (ProposalState) {
        require(proposalCount >= proposalId, "Invalid proposal id");
        Proposal storage proposal = proposals[proposalId];
        if (proposal.canceled) {
            return ProposalState.Canceled;
        }
        if (proposal.executed) {
            return ProposalState.Executed;
        }
        if (block.timestamp <= proposal.endTime) {
            return ProposalState.Active;
        }
        if (proposal.forVotes <= proposal.againstVotes ||
            proposal.forVotes < quorumVotes) {
            return ProposalState.Defeated;
        }
        return ProposalState.Succeeded;
    }
    enum ProposalState {
        Pending,
        Active,
        Canceled,
        Defeated,
        Succeeded,
        Executed
    }
}