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

import "@openzeppelin/contracts-upgradeable/token/ERC1155/ERC1155Upgradeable.sol";
import "@openzeppelin/contracts-upgradeable/token/ERC1155/extensions/ERC1155SupplyUpgradeable.sol";
import "@openzeppelin/contracts-upgradeable/access/AccessControlUpgradeable.sol";
import "@openzeppelin/contracts-upgradeable/proxy/utils/Initializable.sol";
import "./interfaces/ICouponBatch.sol";

/// @title CouponBatch — ERC-1155 同质化券合约
/// @notice 支持批量铸造相同配置的券，适用于促销折扣券、空投券等场景
/// @dev Utility Track 专用，无转售次数追踪。Transparent Proxy 部署。
contract CouponBatch is
    Initializable,
    ERC1155Upgradeable,
    ERC1155SupplyUpgradeable,
    AccessControlUpgradeable
{
    bytes32 public constant MINTER_ROLE = keccak256("MINTER_ROLE");
    bytes32 public constant BURNER_ROLE = keccak256("BURNER_ROLE");
    bytes32 public constant FACTORY_ROLE = keccak256("FACTORY_ROLE");

    uint256 private _nextTypeId;

    /// @dev typeId → 券配置（铸造后不可更改）
    mapping(uint256 => ICouponBatch.BatchCouponConfig) private _configs;
    /// @dev typeId → 原始铸造数量（用于统计）
    mapping(uint256 => uint256) private _mintedQuantities;

    // --- Events ---
    event BatchCouponMinted(
        uint256 indexed typeId,
        address indexed issuer,
        uint256 faceValue,
        uint256 quantity
    );
    event BatchCouponBurned(
        uint256 indexed typeId,
        address indexed account,
        uint256 amount
    );

    function initialize(string memory uri_, address admin) external initializer {
        __ERC1155_init(uri_);
        __ERC1155Supply_init();
        __AccessControl_init();

        _grantRole(DEFAULT_ADMIN_ROLE, admin);
        _grantRole(MINTER_ROLE, admin);
        _nextTypeId = 1;
    }

    /// @notice 创建新券类型并铸造 `quantity` 单位到 `to`
    /// @param to 接收者地址
    /// @param faceValue 面值
    /// @param quantity 数量
    /// @param config 券配置
    /// @return typeId 新创建的 token type ID
    function mint(
        address to,
        uint256 faceValue,
        uint256 quantity,
        ICouponBatch.BatchCouponConfig calldata config
    ) external onlyRole(FACTORY_ROLE) returns (uint256 typeId) {
        require(to != address(0), "CouponBatch: zero address");
        require(faceValue > 0, "CouponBatch: zero face value");
        require(quantity > 0, "CouponBatch: zero quantity");

        typeId = _nextTypeId++;

        // 存储配置（确保 faceValue 和 issuer 字段一致）
        _configs[typeId] = ICouponBatch.BatchCouponConfig({
            transferable: config.transferable,
            maxPrice: config.maxPrice,
            expiryDate: config.expiryDate,
            minPurchase: config.minPurchase,
            stackable: config.stackable,
            allowedStores: config.allowedStores,
            issuer: config.issuer,
            faceValue: faceValue
        });
        _mintedQuantities[typeId] = quantity;

        // 单次 ERC-1155 mint — O(1) 存储
        _mint(to, typeId, quantity, "");

        emit BatchCouponMinted(typeId, config.issuer, faceValue, quantity);
    }

    /// @notice 销毁 `amount` 单位的 typeId 券
    /// @param account 被销毁的地址
    /// @param typeId 券类型 ID
    /// @param amount 销毁数量
    function burn(
        address account,
        uint256 typeId,
        uint256 amount
    ) external {
        require(
            account == _msgSender()
                || isApprovedForAll(account, _msgSender())
                || hasRole(BURNER_ROLE, _msgSender()),
            "CouponBatch: not authorized to burn"
        );
        _burn(account, typeId, amount);
        emit BatchCouponBurned(typeId, account, amount);
    }

    // --- 查询函数 ---

    /// @notice 获取券类型配置
    function getConfig(uint256 typeId) external view returns (ICouponBatch.BatchCouponConfig memory) {
        require(exists(typeId), "CouponBatch: nonexistent type");
        return _configs[typeId];
    }

    /// @notice 获取面值
    function getFaceValue(uint256 typeId) external view returns (uint256) {
        require(exists(typeId), "CouponBatch: nonexistent type");
        return _configs[typeId].faceValue;
    }

    /// @notice 获取原始铸造数量
    function getMintedQuantity(uint256 typeId) external view returns (uint256) {
        return _mintedQuantities[typeId];
    }

    // --- 转让限制 ---

    /// @dev 重写 _beforeTokenTransfer 强制非转让检查
    function _beforeTokenTransfer(
        address operator,
        address from,
        address to,
        uint256[] memory ids,
        uint256[] memory amounts,
        bytes memory data
    ) internal virtual override(ERC1155Upgradeable, ERC1155SupplyUpgradeable) {
        super._beforeTokenTransfer(operator, from, to, ids, amounts, data);

        // 铸造 (from=0) 和销毁 (to=0) 不受限
        if (from == address(0) || to == address(0)) return;

        // 检查每个 token type 的可转让性
        for (uint256 i = 0; i < ids.length; i++) {
            ICouponBatch.BatchCouponConfig memory config = _configs[ids[i]];
            require(config.transferable, "CouponBatch: non-transferable");
        }
    }

    // --- ERC165 ---

    function supportsInterface(bytes4 interfaceId)
        public
        view
        override(ERC1155Upgradeable, AccessControlUpgradeable)
        returns (bool)
    {
        return super.supportsInterface(interfaceId);
    }
}
