// SPDX-License-Identifier: MIT

pragma solidity ^0.8.20;

import {Governor} from "@openzeppelin-contracts/governance/Governor.sol";

abstract contract GovernorGhostCounting is Governor {
    enum VoteType {
        Against,
        For,
        Abstain
    }

    struct ProposalVote {
        uint256 againstVotes;
        uint256 forVotes;
        uint256 abstainVotes;
        mapping(address voter => uint8) votes;
    }

    mapping(uint256 proposalId => ProposalVote) private _proposalVotes;

    // forge-lint: disable-next-item(mixed-case-function)
    // solhint-disable-next-line mixed-case-function
    function COUNTING_MODE() public pure virtual override returns (string memory) {
        return "support=bravo&quorum=for";
    }

    function voteOf(uint256 proposalId, address account) public view virtual returns (uint8) {
        return _proposalVotes[proposalId].votes[account];
    }

    function hasVoted(uint256 proposalId, address account) public view virtual override returns (bool) {
        return _proposalVotes[proposalId].votes[account] > 0;
    }

    function proposalVotes(
        uint256 proposalId
    ) public view virtual returns (uint256 againstVotes, uint256 forVotes, uint256 abstainVotes) {
        ProposalVote storage proposalVote = _proposalVotes[proposalId];
        return (proposalVote.againstVotes, proposalVote.forVotes, proposalVote.abstainVotes);
    }

    function _quorumReached(uint256 proposalId) internal view virtual override returns (bool) {
        ProposalVote storage proposalVote = _proposalVotes[proposalId];
        uint256 totalVotes = proposalVote.forVotes + proposalVote.againstVotes;
        return quorum(proposalSnapshot(proposalId)) <= totalVotes;
    }

    function _voteSucceeded(uint256 proposalId) internal view virtual override returns (bool) {
        ProposalVote storage proposalVote = _proposalVotes[proposalId];
        return proposalVote.forVotes > proposalVote.againstVotes;
    }

    function _countVote(
        uint256 proposalId,
        address account,
        uint8 support,
        uint256 weight,
        bytes memory // params
    ) internal virtual override {
        ProposalVote storage proposalVote = _proposalVotes[proposalId];

        if (proposalVote.votes[account] > 0) {
            revert GovernorAlreadyCastVote(account);
        }
        proposalVote.votes[account] = support + 1;

        if (support == uint8(VoteType.Against)) {
            proposalVote.againstVotes += weight;
        } else if (support == uint8(VoteType.For)) {
            proposalVote.forVotes += weight;
        } else {
            revert GovernorInvalidVoteType();
        }
    }
}
