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

import "./IRouterClient.sol";
import "./CCIPMessageValidator.sol";
import "../oracle/IAggregator.sol";

// Note: This contract must be added as a transmitter to the oracle aggregator
// to be able to update oracle answers. The aggregator's updateAnswer function
// requires the caller to be a transmitter.

/**
 * @title CCIP Receiver with Oracle Integration
 * @notice Receives CCIP messages and updates oracle aggregator
 * @dev Implements CCIP message receiving and oracle update logic with validation
 */
contract CCIPReceiver {
    using CCIPMessageValidator for IRouterClient.Any2EVMMessage;
    
    IRouterClient public immutable router;
    address public oracleAggregator;
    address public admin;
    
    mapping(bytes32 => bool) public processedMessages;
    mapping(uint64 => uint256) public lastNonce; // Track nonces per source chain
    
    event MessageReceived(
        bytes32 indexed messageId,
        uint64 indexed sourceChainSelector,
        address sender,
        bytes data
    );
    event OracleUpdated(uint256 answer, uint256 roundId);
    event OracleAggregatorUpdated(address oldAggregator, address newAggregator);
    
    modifier onlyAdmin() {
        require(msg.sender == admin, "CCIPReceiver: only admin");
        _;
    }
    
    modifier onlyRouter() {
        require(msg.sender == address(router), "CCIPReceiver: only router");
        _;
    }
    
    constructor(address _router, address _oracleAggregator) {
        require(_router != address(0), "CCIPReceiver: zero router address");
        require(_oracleAggregator != address(0), "CCIPReceiver: zero aggregator address");
        
        router = IRouterClient(_router);
        oracleAggregator = _oracleAggregator;
        admin = msg.sender;
    }
    
    /**
     * @notice Handle CCIP message (called by CCIP Router)
     * @param message The received CCIP message
     */
    function ccipReceive(
        IRouterClient.Any2EVMMessage calldata message
    ) external onlyRouter {
        // Replay protection: check if message already processed
        require(!processedMessages[message.messageId], "CCIPReceiver: message already processed");
        
        // Validate message format
        require(
            CCIPMessageValidator.validateMessageFormat(message),
            "CCIPReceiver: invalid message format"
        );
        
        // Validate oracle data format
        (
            bool valid,
            uint256 answer,
            uint256 roundId,
            uint256 timestamp
        ) = CCIPMessageValidator.validateOracleData(message.data);
        
        require(valid, "CCIPReceiver: invalid oracle data");
        
        // Mark message as processed (replay protection)
        processedMessages[message.messageId] = true;
        
        // Update last nonce for source chain (additional replay protection)
        lastNonce[message.sourceChainSelector] = roundId;
        
        // Update oracle aggregator
        // Note: The aggregator's updateAnswer function can be called directly
        // The aggregator will handle access control (onlyTransmitter)
        // We need to ensure this receiver is added as a transmitter
        try IAggregator(oracleAggregator).updateAnswer(answer) {
            address sender = abi.decode(message.sender, (address));
            emit MessageReceived(message.messageId, message.sourceChainSelector, sender, message.data);
            emit OracleUpdated(answer, roundId);
        } catch {
            // If update fails, emit error event
            // In production, consider adding error tracking
            address sender = abi.decode(message.sender, (address));
            emit MessageReceived(message.messageId, message.sourceChainSelector, sender, message.data);
            // Don't emit OracleUpdated if update failed
        }
    }
    
    /**
     * @notice Update oracle aggregator address
     */
    function updateOracleAggregator(address newAggregator) external onlyAdmin {
        require(newAggregator != address(0), "CCIPReceiver: zero address");
        
        address oldAggregator = oracleAggregator;
        oracleAggregator = newAggregator;
        
        emit OracleAggregatorUpdated(oldAggregator, newAggregator);
    }
    
    /**
     * @notice Change admin
     */
    function changeAdmin(address newAdmin) external onlyAdmin {
        require(newAdmin != address(0), "CCIPReceiver: zero address");
        admin = newAdmin;
    }
    
    /**
     * @notice Check if message has been processed
     */
    function isMessageProcessed(bytes32 messageId) external view returns (bool) {
        return processedMessages[messageId];
    }
}
