pragma solidity ^0.8.25;

import "@openzeppelin/contracts-upgradeable/security/ReentrancyGuardUpgradeable.sol";
import "@openzeppelin/contracts-upgradeable/proxy/utils/UUPSUpgradeable.sol";
import "@openzeppelin/contracts-upgradeable/token/ERC20/utils/SafeERC20Upgradeable.sol";

interface IZKProofVerifier {
    function verifyProof(
        uint256[2] memory a,
        uint256[2][2] memory b,
        uint256[2] memory c,
        uint256[4] memory input,
        uint256[2] memory commitment
    ) external view returns (bool);
}

contract HDGLComputeMarketplaceCHGL2 is ReentrancyGuardUpgradeable, UUPSUpgradeable {
    using SafeERC20Upgradeable for IERC20Upgradeable;

    uint256 constant SCALE = 1e6;
    uint256 constant MAX_COMPUTE_UNITS = 1e12; // Added to limit griefing

    address public owner;
    IERC20Upgradeable public chgToken;
    IZKProofVerifier public zkVerifier;
    
    struct Counters {
        uint128 counterB;
        uint128 freeCounter;
    }
    Counters public counters;
    
    bytes32 public lastSnapshot;
    uint64 public currentEpoch;
    mapping(address => bytes32) public lastLatticeByContributor;
    mapping(address => uint256) public balancesETH;
    mapping(address => uint256) public balancesCHG;
    mapping(uint64 => bytes32) public epochSnapshots;
    mapping(address => bytes32) public stealthAddresses;

    event FreeTick(uint256 freeCounter);
    event PremiumSubmission(
        address indexed contributor,
        uint256 blockNumber,
        bytes32 latticeHash,
        uint256 counterB,
        uint256 rewardETH,
        uint256 rewardCHG,
        uint8 recursionDepth,
        uint256 computeUnitsUsed,
        uint8 instanceId
    );
    event EpochSubmission(
        address indexed aggregator,
        uint64 epoch,
        bytes32 latticeHash,
        uint256 computeUnitsTotal,
        uint256 rewardETH,
        uint256 rewardCHG
    );
    event SnapshotInvalidated(bytes32 indexed latticeHash);
    event WithdrawalETH(address indexed user, bytes32 stealthAddress, uint256 amount);
    event WithdrawalCHG(address indexed user, bytes32 stealthAddress, uint256 amount);
    event BatchSubmissionError(uint256 index, string error);

    function initialize(address _chgTokenAddress, address _zkVerifierAddress) external initializer {
        __ReentrancyGuard_init();
        __UUPSUpgradeable_init();
        require(_chgTokenAddress != address(0), "Invalid token address");
        require(_zkVerifierAddress != address(0), "Invalid verifier address");
        chgToken = IERC20Upgradeable(_chgTokenAddress);
        zkVerifier = IZKProofVerifier(_zkVerifierAddress);
        owner = msg.sender;
        currentEpoch = 1;
    }

    function _authorizeUpgrade(address) internal override onlyOwner {}

    modifier onlyOwner() {
        require(msg.sender == owner, "Only owner");
        _;
    }

    function freeTick() external {
        unchecked { counters.freeCounter += 1; }
        emit FreeTick(counters.freeCounter);
    }

    function premiumFunction(
        bytes32 latticeHash,
        uint8 recursionDepth,
        uint256 computeUnitsUsed,
        uint8 instanceId,
        uint256[2] memory a,
        uint256[2][2] memory b,
        uint256[2] memory c,
        uint256[4] memory zkpInput,
        uint256[2] memory commitment
    ) external nonReentrant {
        require(instanceId < 8, "Invalid instance ID");
        require(computeUnitsUsed < MAX_COMPUTE_UNITS, "Compute units too large");
        require(zkpInput[0] != 0, "ZKP required");
        require(zkVerifier.verifyProof(a, b, c, zkpInput, commitment), "Invalid ZKP");
        require(bytes32(zkpInput[0]) == latticeHash, "ZKP hash mismatch");
        
        unchecked { counters.counterB += 1; }
        lastSnapshot = latticeHash;
        lastLatticeByContributor[msg.sender] = latticeHash;

        (uint256 rewardETH, uint256 rewardCHG) = _computeReward(computeUnitsUsed);
        balancesETH[msg.sender] += rewardETH;
        balancesCHG[msg.sender] += rewardCHG;

        emit PremiumSubmission(
            msg.sender, block.number, latticeHash, counters.counterB,
            rewardETH, rewardCHG, recursionDepth, computeUnitsUsed, instanceId
        );
    }

    function batchPremiumFunction(
        bytes32[] memory latticeHashes,
        uint8[] memory recursionDepths,
        uint256[] memory computeUnitsUsed,
        uint8[] memory instanceIds,
        uint256[2][] memory a,
        uint256[2][2][] memory b,
        uint256[2][] memory c,
        uint256[4][] memory zkpInputs,
        uint256[2][] memory commitments
    ) external nonReentrant {
        require(
            latticeHashes.length == recursionDepths.length &&
            latticeHashes.length == computeUnitsUsed.length &&
            latticeHashes.length == instanceIds.length &&
            latticeHashes.length == zkpInputs.length &&
            latticeHashes.length == commitments.length,
            "Array length mismatch"
        );
        require(latticeHashes.length > 0 && latticeHashes.length <= 50, "Invalid batch size");
        
        uint256 totalRewardETH;
        uint256 totalRewardCHG;
        for (uint256 i = 0; i < latticeHashes.length; i++) {
            if (instanceIds[i] >= 8) {
                emit BatchSubmissionError(i, "Invalid instance ID");
                revert("Invalid instance ID");
            }
            if (computeUnitsUsed[i] >= MAX_COMPUTE_UNITS) {
                emit BatchSubmissionError(i, "Compute units too large");
                revert("Compute units too large");
            }
            if (zkpInputs[i][0] == 0) {
                emit BatchSubmissionError(i, "ZKP required");
                revert("ZKP required");
            }
            if (!zkVerifier.verifyProof(a[i], b[i], c[i], zkpInputs[i], commitments[i])) {
                emit BatchSubmissionError(i, "Invalid ZKP");
                revert("Invalid ZKP");
            }
            if (bytes32(zkpInputs[i][0]) != latticeHashes[i]) {
                emit BatchSubmissionError(i, "ZKP hash mismatch");
                revert("ZKP hash mismatch");
            }
            
            unchecked { counters.counterB += 1; }
            lastSnapshot = latticeHashes[i];
            lastLatticeByContributor[msg.sender] = latticeHashes[i];
            (uint256 rewardETH, uint256 rewardCHG) = _computeReward(computeUnitsUsed[i]);
            totalRewardETH += rewardETH;
            totalRewardCHG += rewardCHG;
            emit PremiumSubmission(
                msg.sender, block.number, latticeHashes[i], counters.counterB,
                rewardETH, rewardCHG, recursionDepths[i], computeUnitsUsed[i], instanceIds[i]
            );
        }
        balancesETH[msg.sender] += totalRewardETH;
        balancesCHG[msg.sender] += totalRewardCHG;
    }

    function epochSubmit(
        bytes32 latticeHash,
        uint256 computeUnitsTotal,
        uint64 epoch,
        uint256[2] memory a,
        uint256[2][2] memory b,
        uint256[2] memory c,
        uint256[4] memory zkpInput,
        uint256[2] memory commitment
    ) external nonReentrant {
        require(epoch == currentEpoch, "Invalid epoch");
        require(computeUnitsTotal < MAX_COMPUTE_UNITS, "Compute units too large");
        require(zkpInput[0] != 0, "ZKP required");
        require(zkVerifier.verifyProof(a, b, c, zkpInput, commitment), "Invalid ZKP");
        require(bytes32(zkpInput[0]) == latticeHash, "ZKP hash mismatch");

        unchecked { counters.counterB += 1; }
        lastSnapshot = latticeHash;
        epochSnapshots[epoch] = latticeHash;
        unchecked { currentEpoch += 1; }

        (uint256 rewardETH, uint256 rewardCHG) = _computeReward(computeUnitsTotal);
        balancesETH[msg.sender] += rewardETH;
        balancesCHG[msg.sender] += rewardCHG;

        emit EpochSubmission(msg.sender, epoch, latticeHash, computeUnitsTotal, rewardETH, rewardCHG);
    }

    function _computeReward(uint256 computeUnitsUsed) internal pure returns (uint256 ethReward, uint256 chgReward) {
        require(computeUnitsUsed < MAX_COMPUTE_UNITS, "Compute units too large");
        ethReward = (1e15 * 1e6) / (computeUnitsUsed + 1);
        chgReward = (1000 * 1e6) / (computeUnitsUsed + 1);
        return (ethReward, chgReward);
    }

    function verifyPremiumHash(bytes32 latticeHash) external view returns (bool) {
        return latticeHash == lastSnapshot;
    }

    function registerStealthAddress(bytes32 stealthAddress) external {
        require(stealthAddress != bytes32(0) && stealthAddress[0] != 0, "Invalid stealth address format");
        stealthAddresses[msg.sender] = stealthAddress;
    }

    function invalidateSnapshot(
        bytes32 latticeHash,
        uint256[2] memory a,
        uint256[2][2] memory b,
        uint256[2] memory c,
        uint256[4] memory zkpInput,
        uint256[2] memory commitment
    ) external onlyOwner {
        require(latticeHash == lastSnapshot, "Invalid snapshot");
        require(zkVerifier.verifyProof(a, b, c, zkpInput, commitment), "Invalid ZKP");
        delete lastSnapshot;
        emit SnapshotInvalidated(latticeHash);
    }

    function withdrawETH() external nonReentrant {
        uint256 amount = balancesETH[msg.sender];
        bytes32 stealthAddress = stealthAddresses[msg.sender];
        require(amount > 0, "No ETH to withdraw");
        require(stealthAddress != bytes32(0), "Register stealth address first");
        (bool success, ) = payable(msg.sender).call{value: amount}("");
        require(success, "ETH transfer failed");
        balancesETH[msg.sender] = 0;
        emit WithdrawalETH(msg.sender, stealthAddress, amount);
    }

    function withdrawCHG() external nonReentrant {
        uint256 amount = balancesCHG[msg.sender];
        bytes32 stealthAddress = stealthAddresses[msg.sender];
        require(amount > 0, "No CHG to withdraw");
        require(stealthAddress != bytes32(0), "Register stealth address first");
        chgToken.safeTransfer(msg.sender, amount);
        balancesCHG[msg.sender] = 0;
        emit WithdrawalCHG(msg.sender, stealthAddress, amount);
    }

    receive() external payable {}
}