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

import "forge-std/Test.sol";
import "../src/Coupon.sol";
import "../src/interfaces/ICoupon.sol";

contract CouponTest is Test {
    Coupon coupon;

    address admin = address(1);
    address factory = address(2);
    address settler = address(3);
    address user1 = address(4);
    address user2 = address(5);

    function setUp() public {
        vm.startPrank(admin);
        coupon = new Coupon();
        coupon.initialize("Genex Coupon", "GXC", admin);
        coupon.grantRole(keccak256("FACTORY_ROLE"), factory);
        coupon.grantRole(keccak256("SETTLER_ROLE"), settler);
        vm.stopPrank();
    }

    function test_MintSetsConfigCorrectly() public {
        uint256 tokenId = _mintUtilityCoupon(user1, 100e6);
        ICoupon.CouponConfig memory config = coupon.getConfig(tokenId);

        assertEq(uint8(config.couponType), uint8(ICoupon.CouponType.Utility));
        assertTrue(config.transferable);
        assertEq(config.maxResaleCount, 3);
        assertEq(coupon.ownerOf(tokenId), user1);
        assertEq(coupon.getFaceValue(tokenId), 100e6);
    }

    function test_BurnByOwner() public {
        uint256 tokenId = _mintUtilityCoupon(user1, 100e6);
        vm.prank(user1);
        coupon.burn(tokenId);
        vm.expectRevert();
        coupon.ownerOf(tokenId);
    }

    function test_BurnBySettler() public {
        uint256 tokenId = _mintUtilityCoupon(user1, 100e6);
        vm.prank(settler);
        coupon.burn(tokenId);
    }

    function test_NonTransferableCouponReverts() public {
        uint256 tokenId = _mintNonTransferableCoupon(user1, 50e6);

        vm.prank(user1);
        vm.expectRevert("Coupon: non-transferable");
        coupon.safeTransferFrom(user1, user2, tokenId);
    }

    function test_TransferableTransferSucceeds() public {
        uint256 tokenId = _mintUtilityCoupon(user1, 100e6);

        vm.prank(user1);
        coupon.safeTransferFrom(user1, user2, tokenId);
        assertEq(coupon.ownerOf(tokenId), user2);
    }

    function test_MaxResaleCountEnforced() public {
        uint256 tokenId = _mintUtilityCoupon(user1, 100e6);

        // 使用 settler 增加到上限 (3)
        vm.startPrank(settler);
        coupon.incrementResaleCount(tokenId);
        coupon.incrementResaleCount(tokenId);
        coupon.incrementResaleCount(tokenId);
        vm.stopPrank();

        assertEq(coupon.getResaleCount(tokenId), 3);

        // 转移应被拒绝
        vm.prank(user1);
        vm.expectRevert("Coupon: max resale count exceeded");
        coupon.safeTransferFrom(user1, user2, tokenId);
    }

    function test_IncrementResaleCountOnlySettler() public {
        uint256 tokenId = _mintUtilityCoupon(user1, 100e6);

        vm.prank(user1);
        vm.expectRevert();
        coupon.incrementResaleCount(tokenId);
    }

    function test_BatchTransfer() public {
        uint256[] memory tokenIds = new uint256[](3);
        for (uint256 i = 0; i < 3; i++) {
            tokenIds[i] = _mintUtilityCoupon(user1, 100e6);
        }

        vm.prank(user1);
        coupon.batchTransfer(user1, user2, tokenIds);

        for (uint256 i = 0; i < 3; i++) {
            assertEq(coupon.ownerOf(tokenIds[i]), user2);
        }
    }

    function test_ResaleCountInitiallyZero() public {
        uint256 tokenId = _mintUtilityCoupon(user1, 100e6);
        assertEq(coupon.getResaleCount(tokenId), 0);
    }

    function test_MintIncrementsTokenId() public {
        uint256 id1 = _mintUtilityCoupon(user1, 100e6);
        uint256 id2 = _mintUtilityCoupon(user1, 200e6);
        assertEq(id2, id1 + 1);
    }

    // --- Helpers ---

    function _mintUtilityCoupon(address to, uint256 faceValue) internal returns (uint256) {
        bytes32[] memory stores = new bytes32[](0);
        ICoupon.CouponConfig memory config = ICoupon.CouponConfig({
            couponType: ICoupon.CouponType.Utility,
            transferable: true,
            maxResaleCount: 3,
            maxPrice: faceValue,
            expiryDate: block.timestamp + 180 days,
            minPurchase: 0,
            stackable: false,
            allowedStores: stores,
            issuer: admin,
            faceValue: faceValue
        });

        vm.prank(factory);
        return coupon.mint(to, faceValue, config);
    }

    function _mintNonTransferableCoupon(address to, uint256 faceValue) internal returns (uint256) {
        bytes32[] memory stores = new bytes32[](0);
        ICoupon.CouponConfig memory config = ICoupon.CouponConfig({
            couponType: ICoupon.CouponType.Utility,
            transferable: false,
            maxResaleCount: 0,
            maxPrice: faceValue,
            expiryDate: block.timestamp + 180 days,
            minPurchase: 0,
            stackable: false,
            allowedStores: stores,
            issuer: admin,
            faceValue: faceValue
        });

        vm.prank(factory);
        return coupon.mint(to, faceValue, config);
    }
}
