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

import "@openzeppelin/contracts/access/Ownable.sol";
import "@openzeppelin/contracts/security/ReentrancyGuard.sol";

/**
 * @title AereMiningSubscription (AireFlow)
 * @notice Cloud mining subscriptions paid in AERE
 * @dev Whitepaper §5.3 — "Revolutionary Subscription Mining Platform"
 *      Users pay a monthly AERE fee for guaranteed hash rate allocation,
 *      receive proportional block rewards distributed by the foundation.
 *
 * Reference whitepaper §5.3:
 *   AireFlow 15.0 TH/s example: $300/month → ~30 AERE/month rewards (illustrative).
 */
contract AereMiningSubscription is Ownable, ReentrancyGuard {
    enum Tier { BASIC, PREMIUM, ENTERPRISE, AIREFLOW }

    struct TierConfig {
        uint256 monthlyPriceAere;   // wei
        uint256 hashRateThs;        // TH/s allocated
        bool active;
    }

    struct Subscription {
        Tier tier;
        uint256 startBlock;
        uint256 expiresAt;          // block.timestamp
        uint256 totalPaid;
        uint256 unclaimedRewards;
        uint256 lastRewardBlock;
    }

    mapping(Tier => TierConfig) public tiers;
    mapping(address => Subscription) public subscriptions;
    address[] public subscribers;
    mapping(address => uint256) private subscriberIndex;

    uint256 public constant SUBSCRIPTION_PERIOD = 30 days;
    uint256 public networkHashRateThs = 2500;  // bootstrap value, governance-updatable
    uint256 public blockReward = 1.25 ether;   // matches whitepaper §5.3 (1.25 AERE/block)
    uint256 public difficultyAdjustmentBps = 10750;  // Dt = 1.075 example

    address public foundationTreasury;

    event TierConfigured(Tier indexed tier, uint256 priceAere, uint256 hashRateThs);
    event Subscribed(address indexed user, Tier tier, uint256 expiresAt);
    event RewardsAccrued(address indexed user, uint256 amount);
    event RewardsClaimed(address indexed user, uint256 amount);
    event TreasurySet(address indexed treasury);

    constructor(address _foundationTreasury) {
        foundationTreasury = _foundationTreasury;

        // Default tier configuration (whitepaper-aligned, governance-mutable)
        tiers[Tier.BASIC]       = TierConfig({monthlyPriceAere:  10 ether, hashRateThs:  1, active: true});
        tiers[Tier.PREMIUM]     = TierConfig({monthlyPriceAere:  50 ether, hashRateThs:  6, active: true});
        tiers[Tier.ENTERPRISE]  = TierConfig({monthlyPriceAere: 200 ether, hashRateThs: 30, active: true});
        tiers[Tier.AIREFLOW]    = TierConfig({monthlyPriceAere: 100 ether, hashRateThs: 15, active: true});
    }

    function configureTier(Tier tier, uint256 priceAere, uint256 hashRateThs) external onlyOwner {
        tiers[tier] = TierConfig({monthlyPriceAere: priceAere, hashRateThs: hashRateThs, active: true});
        emit TierConfigured(tier, priceAere, hashRateThs);
    }

    function setTreasury(address _treasury) external onlyOwner {
        foundationTreasury = _treasury;
        emit TreasurySet(_treasury);
    }

    function setNetworkHashRate(uint256 _ths) external onlyOwner {
        require(_ths > 0, "AereMining: hashrate>0");
        networkHashRateThs = _ths;
    }

    /**
     * @notice Subscribe (or extend) to a mining tier. Pay msg.value in native AERE.
     */
    function subscribe(Tier tier) external payable nonReentrant {
        TierConfig memory cfg = tiers[tier];
        require(cfg.active, "AereMining: tier inactive");
        require(msg.value >= cfg.monthlyPriceAere, "AereMining: insufficient payment");

        Subscription storage s = subscriptions[msg.sender];

        // Settle pending rewards before changing tier/period
        if (s.tier == tier && s.expiresAt > block.timestamp) {
            _accrue(msg.sender);
        } else {
            // New tier or expired — reset rewards baseline
            s.lastRewardBlock = block.number;
        }

        if (s.expiresAt < block.timestamp) {
            s.startBlock = block.number;
            s.expiresAt = block.timestamp + SUBSCRIPTION_PERIOD;
        } else {
            s.expiresAt += SUBSCRIPTION_PERIOD;
        }
        s.tier = tier;
        s.totalPaid += msg.value;

        // Track unique subscribers
        if (subscriberIndex[msg.sender] == 0) {
            subscribers.push(msg.sender);
            subscriberIndex[msg.sender] = subscribers.length;
        }

        // Forward payment to foundation treasury
        (bool ok, ) = foundationTreasury.call{value: msg.value}("");
        require(ok, "AereMining: treasury transfer failed");

        emit Subscribed(msg.sender, tier, s.expiresAt);
    }

    /**
     * @notice Pending rewards (in AERE wei) for a subscriber.
     * @dev Formula (whitepaper §5.3):
     *      R = B × HR × (1 - Dt) × T
     *      where HR = userHashRate / networkHashRate
     */
    function pendingRewards(address user) public view returns (uint256) {
        Subscription memory s = subscriptions[user];
        if (s.expiresAt < block.timestamp || s.lastRewardBlock == 0) return s.unclaimedRewards;
        uint256 blocksElapsed = block.number - s.lastRewardBlock;
        uint256 userHashRate = tiers[s.tier].hashRateThs;
        // Compute (1 - Dt). difficultyAdjustmentBps is e.g. 10750 = 1.075.
        // (1 - Dt) is negative if Dt > 1, but in the whitepaper formula difficulty
        // damps reward, not amplifies. Use (10000 - (difficultyAdjustmentBps - 10000))
        // capped to non-negative if Dt < 2.
        int256 difficultyFactor = int256(20000) - int256(difficultyAdjustmentBps);
        if (difficultyFactor < 0) difficultyFactor = 0;
        uint256 dampedReward = (blockReward * uint256(difficultyFactor)) / 10000;
        uint256 reward = (dampedReward * userHashRate * blocksElapsed) / networkHashRateThs;
        return s.unclaimedRewards + reward;
    }

    function _accrue(address user) internal {
        uint256 reward = pendingRewards(user);
        Subscription storage s = subscriptions[user];
        if (reward > s.unclaimedRewards) {
            s.unclaimedRewards = reward;
            emit RewardsAccrued(user, reward);
        }
        s.lastRewardBlock = block.number;
    }

    function claim() external nonReentrant {
        _accrue(msg.sender);
        Subscription storage s = subscriptions[msg.sender];
        uint256 amount = s.unclaimedRewards;
        require(amount > 0, "AereMining: nothing to claim");
        s.unclaimedRewards = 0;
        require(address(this).balance >= amount, "AereMining: pool insufficient - needs foundation top-up");
        (bool ok, ) = msg.sender.call{value: amount}("");
        require(ok, "AereMining: transfer failed");
        emit RewardsClaimed(msg.sender, amount);
    }

    function subscriberCount() external view returns (uint256) {
        return subscribers.length;
    }

    /// @notice Allow foundation to top up the reward pool
    receive() external payable {}
}
