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

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

contract RedemptionTest is Test {
    Redemption redemption;
    Coupon coupon;
    Compliance compliance;

    address admin = address(1);
    address consumer = address(4);
    bytes32 storeA = keccak256("STORE_A");
    bytes32 storeB = keccak256("STORE_B");

    function setUp() public {
        vm.startPrank(admin);

        compliance = new Compliance();
        compliance.initialize(admin);

        coupon = new Coupon();
        coupon.initialize("Genex Coupon", "GXC", admin);
        coupon.grantRole(keccak256("FACTORY_ROLE"), admin);

        redemption = new Redemption();
        redemption.initialize(address(coupon), address(compliance), admin);

        // Grant Redemption contract the SETTLER_ROLE so it can burn coupons
        coupon.grantRole(keccak256("SETTLER_ROLE"), address(redemption));

        compliance.setKycLevel(consumer, 1);

        vm.stopPrank();
    }

    function test_RedeemSuccess() public {
        uint256 tokenId = _mintCoupon(consumer, 100e6, false);

        vm.prank(consumer);
        redemption.redeem(tokenId, storeA);

        // 券已销毁
        vm.expectRevert();
        coupon.ownerOf(tokenId);

        // 记录已创建
        Redemption.RedemptionRecord memory record = redemption.getRedemption(0);
        assertEq(record.tokenId, tokenId);
        assertEq(record.consumer, consumer);
        assertEq(record.storeId, storeA);
    }

    function test_RedeemExpiredCouponReverts() public {
        uint256 tokenId = _mintCoupon(consumer, 100e6, false);

        vm.warp(block.timestamp + 200 days);

        vm.prank(consumer);
        vm.expectRevert("Redemption: coupon expired");
        redemption.redeem(tokenId, storeA);
    }

    function test_RedeemNotOwnerReverts() public {
        uint256 tokenId = _mintCoupon(consumer, 100e6, false);

        vm.prank(admin);
        vm.expectRevert("Redemption: not owner");
        redemption.redeem(tokenId, storeA);
    }

    function test_RedeemBlacklistedReverts() public {
        uint256 tokenId = _mintCoupon(consumer, 100e6, false);

        vm.prank(admin);
        compliance.addToBlacklist(consumer, "OFAC");

        vm.prank(consumer);
        vm.expectRevert("Redemption: blacklisted");
        redemption.redeem(tokenId, storeA);
    }

    function test_RedeemStoreRestricted() public {
        uint256 tokenId = _mintCouponWithStore(consumer, 100e6);

        // 使用不在列表中的门店
        bytes32 storeC = keccak256("STORE_C");
        vm.prank(consumer);
        vm.expectRevert("Redemption: store not allowed");
        redemption.redeem(tokenId, storeC);

        // 使用允许的门店
        vm.prank(consumer);
        redemption.redeem(tokenId, storeA);
    }

    function test_TotalRedemptionsIncrement() public {
        uint256 tokenId1 = _mintCoupon(consumer, 100e6, false);
        uint256 tokenId2 = _mintCoupon(consumer, 200e6, false);

        vm.startPrank(consumer);
        redemption.redeem(tokenId1, storeA);
        redemption.redeem(tokenId2, storeA);
        vm.stopPrank();

        assertEq(redemption.totalRedemptions(), 2);
    }

    // --- Helpers ---

    function _mintCoupon(address to, uint256 faceValue, bool withStores) internal returns (uint256) {
        bytes32[] memory stores;
        if (withStores) {
            stores = new bytes32[](2);
            stores[0] = storeA;
            stores[1] = storeB;
        } else {
            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(admin);
        return coupon.mint(to, faceValue, config);
    }

    function _mintCouponWithStore(address to, uint256 faceValue) internal returns (uint256) {
        return _mintCoupon(to, faceValue, true);
    }
}
