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

import {AereUSDC} from "./AereUSDC.sol";
import {AccessControl} from "@openzeppelin/contracts/access/AccessControl.sol";

/// @title AereBridge — Foundation-attested on-ramp from Ethereum USDC to AereUSDC
/// @notice The off-chain bridge watcher (Foundation-operated, source under
///         /opt/aere-bridge-watcher) monitors a Foundation receiver address on
///         Ethereum mainnet for incoming USDC. When a deposit is detected and
///         confirmed (≥12 block confirmations), the watcher calls
///         `attestDeposit(ethTxHash, ethSender, aereDestination, amount)` on this
///         contract, which mints AereUSDC to the destination.
///
///         Replay protection: every (ethTxHash, logIndex) is recorded — re-attesting
///         the same deposit is a no-op.
///
///         Honest framing: this contract assumes a trusted ATTESTOR role. Phase 1
///         the attestor is a Foundation-controlled EOA. Phase 2 swaps to a multisig.
///         Phase 3 replaces with trustless settlement via Across/Hyperlane.
contract AereBridge is AccessControl {
    bytes32 public constant ATTESTOR_ROLE = keccak256("ATTESTOR_ROLE");

    AereUSDC public immutable aereUSDC;

    /// @notice Foundation's USDC receiver address on Ethereum mainnet — informational
    ///         only; the watcher actually decides which Ethereum address to monitor.
    string public ethReceiverHint;

    /// @notice (ethTxHash, logIndex) tuple → already-attested marker. Prevents double-mint.
    mapping(bytes32 => bool) public attestedDeposits;

    /// @notice Total bridged in (USDC, 6 decimals). Visible for transparency dashboard.
    uint256 public totalBridgedIn;

    struct Deposit {
        bytes32 ethTxHash;
        uint64 ethLogIndex;
        address ethSender;
        address aereDestination;
        uint256 amount;
        uint64 attestedAt;
        uint64 attestedBlock;
    }

    /// @notice Append-only deposit log. Phase 1 only; off-chain indexer reads events.
    Deposit[] public deposits;

    event Attested(
        uint256 indexed depositId,
        bytes32 indexed ethTxHash,
        address indexed aereDestination,
        address ethSender,
        uint256 amount
    );
    event EthReceiverHintUpdated(string ethReceiver);

    error AlreadyAttested();
    error InvalidAmount();
    error InvalidDestination();

    constructor(address foundation, address _aereUSDC, address initialAttestor, string memory _ethReceiverHint) {
        require(_aereUSDC != address(0), "bridge: aereUSDC=0");
        aereUSDC = AereUSDC(_aereUSDC);
        ethReceiverHint = _ethReceiverHint;
        _grantRole(DEFAULT_ADMIN_ROLE, foundation);
        _grantRole(ATTESTOR_ROLE, initialAttestor);
    }

    /// @notice Off-chain watcher calls this when an Ethereum USDC deposit is confirmed.
    /// @param ethTxHash      The Ethereum transaction hash containing the USDC Transfer.
    /// @param ethLogIndex    The log index within that tx (multiple logs per tx possible).
    /// @param ethSender      The Ethereum address that sent the USDC (used for refund routing).
    /// @param aereDestination The AERE address to receive AereUSDC.
    /// @param amount         The amount in USDC units (6 decimals).
    function attestDeposit(
        bytes32 ethTxHash,
        uint64 ethLogIndex,
        address ethSender,
        address aereDestination,
        uint256 amount
    ) external onlyRole(ATTESTOR_ROLE) {
        if (amount == 0) revert InvalidAmount();
        if (aereDestination == address(0)) revert InvalidDestination();
        bytes32 key = keccak256(abi.encodePacked(ethTxHash, ethLogIndex));
        if (attestedDeposits[key]) revert AlreadyAttested();
        attestedDeposits[key] = true;

        uint256 id = deposits.length;
        deposits.push(Deposit({
            ethTxHash: ethTxHash,
            ethLogIndex: ethLogIndex,
            ethSender: ethSender,
            aereDestination: aereDestination,
            amount: amount,
            attestedAt: uint64(block.timestamp),
            attestedBlock: uint64(block.number)
        }));
        totalBridgedIn += amount;

        aereUSDC.mint(aereDestination, amount);
        emit Attested(id, ethTxHash, aereDestination, ethSender, amount);
    }

    /// @notice Foundation can update the receiver-address hint shown to users.
    function setEthReceiverHint(string calldata _ethReceiverHint) external onlyRole(DEFAULT_ADMIN_ROLE) {
        ethReceiverHint = _ethReceiverHint;
        emit EthReceiverHintUpdated(_ethReceiverHint);
    }

    function depositCount() external view returns (uint256) {
        return deposits.length;
    }
}
