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

/**
 * @title Oracle Aggregator
 * @notice Chainlink-compatible oracle aggregator for price feeds
 * @dev Implements round-based oracle updates with access control
 */
contract Aggregator {
    struct Round {
        uint256 answer;
        uint256 startedAt;
        uint256 updatedAt;
        uint256 answeredInRound;
        address transmitter;
    }

    uint8 public constant decimals = 8;
    string public description;
    
    uint256 public version = 1;
    uint256 public latestRound;
    
    mapping(uint256 => Round) public rounds;
    
    // Access control
    address public admin;
    address[] public transmitters;
    mapping(address => bool) public isTransmitter;
    
    // Round parameters
    uint256 public heartbeat;
    uint256 public deviationThreshold; // in basis points (e.g., 50 = 0.5%)
    bool public paused;
    
    event AnswerUpdated(
        int256 indexed current,
        uint256 indexed roundId,
        uint256 updatedAt
    );
    event NewRound(
        uint256 indexed roundId,
        address indexed startedBy,
        uint256 startedAt
    );
    event TransmitterAdded(address indexed transmitter);
    event TransmitterRemoved(address indexed transmitter);
    event AdminChanged(address indexed oldAdmin, address indexed newAdmin);
    event HeartbeatUpdated(uint256 oldHeartbeat, uint256 newHeartbeat);
    event DeviationThresholdUpdated(uint256 oldThreshold, uint256 newThreshold);
    event Paused(address account);
    event Unpaused(address account);
    
    modifier onlyAdmin() {
        require(msg.sender == admin, "Aggregator: only admin");
        _;
    }
    
    modifier onlyTransmitter() {
        require(isTransmitter[msg.sender], "Aggregator: only transmitter");
        _;
    }
    
    modifier whenNotPaused() {
        require(!paused, "Aggregator: paused");
        _;
    }
    
    constructor(
        string memory _description,
        address _admin,
        uint256 _heartbeat,
        uint256 _deviationThreshold
    ) {
        description = _description;
        admin = _admin;
        heartbeat = _heartbeat;
        deviationThreshold = _deviationThreshold;
    }
    
    /**
     * @notice Update the answer for the current round
     * @param answer New answer value
     */
    function updateAnswer(uint256 answer) external virtual onlyTransmitter whenNotPaused {
        uint256 currentRound = latestRound;
        Round storage round = rounds[currentRound];
        
        // Check if we need to start a new round
        if (round.updatedAt == 0 || 
            block.timestamp >= round.startedAt + heartbeat ||
            shouldUpdate(answer, round.answer)) {
            currentRound = latestRound + 1;
            latestRound = currentRound;
            
            rounds[currentRound] = Round({
                answer: answer,
                startedAt: block.timestamp,
                updatedAt: block.timestamp,
                answeredInRound: currentRound,
                transmitter: msg.sender
            });
            
            emit NewRound(currentRound, msg.sender, block.timestamp);
        } else {
            // Update existing round (median or weighted average logic can be added)
            round.updatedAt = block.timestamp;
            round.transmitter = msg.sender;
        }
        
        emit AnswerUpdated(int256(answer), currentRound, block.timestamp);
    }
    
    /**
     * @notice Check if answer should be updated based on deviation threshold
     */
    function shouldUpdate(uint256 newAnswer, uint256 oldAnswer) internal view returns (bool) {
        if (oldAnswer == 0) return true;
        
        uint256 deviation = newAnswer > oldAnswer 
            ? ((newAnswer - oldAnswer) * 10000) / oldAnswer
            : ((oldAnswer - newAnswer) * 10000) / oldAnswer;
            
        return deviation >= deviationThreshold;
    }
    
    /**
     * @notice Get the latest answer
     */
    function latestAnswer() external view returns (int256) {
        return int256(rounds[latestRound].answer);
    }
    
    /**
     * @notice Get the latest round data
     */
    function latestRoundData()
        external
        view
        returns (
            uint80 roundId,
            int256 answer,
            uint256 startedAt,
            uint256 updatedAt,
            uint80 answeredInRound
        )
    {
        Round storage round = rounds[latestRound];
        return (
            uint80(latestRound),
            int256(round.answer),
            round.startedAt,
            round.updatedAt,
            uint80(round.answeredInRound)
        );
    }
    
    /**
     * @notice Get round data for a specific round
     */
    function getRoundData(uint80 _roundId)
        external
        view
        returns (
            uint80 roundId,
            int256 answer,
            uint256 startedAt,
            uint256 updatedAt,
            uint80 answeredInRound
        )
    {
        Round storage round = rounds[_roundId];
        require(round.updatedAt > 0, "Aggregator: round not found");
        return (
            _roundId,
            int256(round.answer),
            round.startedAt,
            round.updatedAt,
            uint80(round.answeredInRound)
        );
    }
    
    /**
     * @notice Add a transmitter
     */
    function addTransmitter(address transmitter) external onlyAdmin {
        require(!isTransmitter[transmitter], "Aggregator: already transmitter");
        isTransmitter[transmitter] = true;
        transmitters.push(transmitter);
        emit TransmitterAdded(transmitter);
    }
    
    /**
     * @notice Remove a transmitter
     */
    function removeTransmitter(address transmitter) external onlyAdmin {
        require(isTransmitter[transmitter], "Aggregator: not transmitter");
        isTransmitter[transmitter] = false;
        
        // Remove from array
        for (uint256 i = 0; i < transmitters.length; i++) {
            if (transmitters[i] == transmitter) {
                transmitters[i] = transmitters[transmitters.length - 1];
                transmitters.pop();
                break;
            }
        }
        
        emit TransmitterRemoved(transmitter);
    }
    
    /**
     * @notice Change admin
     */
    function changeAdmin(address newAdmin) external onlyAdmin {
        require(newAdmin != address(0), "Aggregator: zero address");
        address oldAdmin = admin;
        admin = newAdmin;
        emit AdminChanged(oldAdmin, newAdmin);
    }
    
    /**
     * @notice Update heartbeat
     */
    function updateHeartbeat(uint256 newHeartbeat) external onlyAdmin {
        uint256 oldHeartbeat = heartbeat;
        heartbeat = newHeartbeat;
        emit HeartbeatUpdated(oldHeartbeat, newHeartbeat);
    }
    
    /**
     * @notice Update deviation threshold
     */
    function updateDeviationThreshold(uint256 newThreshold) external onlyAdmin {
        uint256 oldThreshold = deviationThreshold;
        deviationThreshold = newThreshold;
        emit DeviationThresholdUpdated(oldThreshold, newThreshold);
    }
    
    /**
     * @notice Pause the aggregator
     */
    function pause() external onlyAdmin {
        paused = true;
        emit Paused(msg.sender);
    }
    
    /**
     * @notice Unpause the aggregator
     */
    function unpause() external onlyAdmin {
        paused = false;
        emit Unpaused(msg.sender);
    }
    
    /**
     * @notice Get list of transmitters
     */
    function getTransmitters() external view returns (address[] memory) {
        return transmitters;
    }
}

