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

import "@openzeppelin/contracts-upgradeable/token/ERC721/ERC721Upgradeable.sol";
import "@openzeppelin/contracts-upgradeable/access/AccessControlUpgradeable.sol";
import "@openzeppelin/contracts-upgradeable/proxy/utils/Initializable.sol";
import "./interfaces/ICoupon.sol";

/// @title Coupon — 券 NFT 合约
/// @notice ERC-721 实现，支持不可转让限制、转售计数、批量操作
/// @dev 使用 Transparent Proxy 部署，券类型铸造后不可修改（安全红线）
contract Coupon is Initializable, ERC721Upgradeable, AccessControlUpgradeable {
    bytes32 public constant MINTER_ROLE = keccak256("MINTER_ROLE");
    bytes32 public constant SETTLER_ROLE = keccak256("SETTLER_ROLE");
    bytes32 public constant FACTORY_ROLE = keccak256("FACTORY_ROLE");

    uint256 private _nextTokenId;

    // tokenId → 券配置（铸造后 couponType 不可修改 — 安全红线）
    mapping(uint256 => ICoupon.CouponConfig) private _configs;
    // tokenId → 面值
    mapping(uint256 => uint256) private _faceValues;
    // tokenId → 转售次数（安全红线：不可被升级绕过）
    mapping(uint256 => uint256) private _resaleCount;

    // --- Events ---
    event CouponMinted(uint256 indexed tokenId, address indexed issuer, uint256 faceValue, ICoupon.CouponType couponType);
    event CouponBurned(uint256 indexed tokenId, address indexed burner);
    event ResaleCountIncremented(uint256 indexed tokenId, uint256 newCount);

    function initialize(string memory name, string memory symbol, address admin) external initializer {
        __ERC721_init(name, symbol);
        __AccessControl_init();

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

    /// @notice 铸造单个券 NFT
    function mint(
        address to,
        uint256 faceValue,
        ICoupon.CouponConfig calldata config
    ) external onlyRole(FACTORY_ROLE) returns (uint256 tokenId) {
        tokenId = _nextTokenId++;
        _safeMint(to, tokenId);
        _configs[tokenId] = config;
        _faceValues[tokenId] = faceValue;
        _resaleCount[tokenId] = 0;

        emit CouponMinted(tokenId, config.issuer, faceValue, config.couponType);
    }

    /// @notice 销毁券（兑付时调用）
    function burn(uint256 tokenId) external {
        require(
            _isApprovedOrOwner(msg.sender, tokenId) || hasRole(SETTLER_ROLE, msg.sender),
            "Coupon: not authorized to burn"
        );
        address owner = ownerOf(tokenId);
        _burn(tokenId);
        emit CouponBurned(tokenId, owner);
    }

    /// @notice 获取券配置
    function getConfig(uint256 tokenId) external view returns (ICoupon.CouponConfig memory) {
        require(_exists(tokenId), "Coupon: nonexistent token");
        return _configs[tokenId];
    }

    /// @notice 获取面值
    function getFaceValue(uint256 tokenId) external view returns (uint256) {
        require(_exists(tokenId), "Coupon: nonexistent token");
        return _faceValues[tokenId];
    }

    /// @notice 获取转售次数
    function getResaleCount(uint256 tokenId) external view returns (uint256) {
        return _resaleCount[tokenId];
    }

    /// @notice 增加转售次数（仅 Settlement 合约可调用）
    function incrementResaleCount(uint256 tokenId) external onlyRole(SETTLER_ROLE) {
        _resaleCount[tokenId]++;
        emit ResaleCountIncremented(tokenId, _resaleCount[tokenId]);
    }

    /// @notice 批量转移
    function batchTransfer(
        address from,
        address to,
        uint256[] calldata tokenIds
    ) external {
        for (uint256 i = 0; i < tokenIds.length; i++) {
            safeTransferFrom(from, to, tokenIds[i]);
        }
    }

    /// @notice 重写 transfer 钩子 — 不可转让券直接 revert
    /// @dev 铸造（from=0）和销毁（to=0）不受限
    function _beforeTokenTransfer(
        address from,
        address to,
        uint256 tokenId,
        uint256 batchSize
    ) internal virtual override {
        super._beforeTokenTransfer(from, to, tokenId, batchSize);

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

        ICoupon.CouponConfig memory config = _configs[tokenId];

        // 不可转让券：revert
        require(config.transferable, "Coupon: non-transferable");

        // 转售次数检查（安全红线）
        require(
            _resaleCount[tokenId] < config.maxResaleCount,
            "Coupon: max resale count exceeded"
        );
    }

    /// @dev 内部辅助：检查授权（override OZ v4）
    function _isApprovedOrOwner(address spender, uint256 tokenId) internal view override returns (bool) {
        return super._isApprovedOrOwner(spender, tokenId);
    }

    // --- ERC165 ---
    function supportsInterface(bytes4 interfaceId)
        public
        view
        override(ERC721Upgradeable, AccessControlUpgradeable)
        returns (bool)
    {
        return super.supportsInterface(interfaceId);
    }
}
