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

import "forge-std/Test.sol";
import "../src/Redemption1155.sol";
import "../src/CouponBatch.sol";
import "../src/Compliance.sol";
import "../src/interfaces/ICouponBatch.sol";

contract Redemption1155Test is Test {
    Redemption1155 redemption;
    CouponBatch batch;
    Compliance compliance;

    address admin = address(1);
    address factory = address(2);
    address consumer = address(4);
    address issuer = address(5);

    bytes32 storeA = keccak256("STORE_A");
    bytes32 storeB = keccak256("STORE_B");

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

        // 部署 Compliance
        compliance = new Compliance();
        compliance.initialize(admin);

        // 部署 CouponBatch
        batch = new CouponBatch();
        batch.initialize("https://genex.io/api/coupon-batch/{id}.json", admin);

        // 部署 Redemption1155
        redemption = new Redemption1155();
        redemption.initialize(address(batch), address(compliance), admin);

        // 角色授权
        batch.grantRole(keccak256("FACTORY_ROLE"), factory);
        batch.grantRole(keccak256("BURNER_ROLE"), address(redemption));

        compliance.setKycLevel(consumer, 1);

        vm.stopPrank();
    }

    function test_RedeemSuccess() public {
        uint256 typeId = _mintBatch(consumer, 100e6, 10, false);

        vm.prank(consumer);
        redemption.redeem(typeId, 3, storeA);

        // 余额减少
        assertEq(batch.balanceOf(consumer, typeId), 7);

        // 记录已创建
        Redemption1155.RedemptionRecord memory record = redemption.getRedemption(0);
        assertEq(record.typeId, typeId);
        assertEq(record.amount, 3);
        assertEq(record.consumer, consumer);
        assertEq(record.issuer, issuer);
        assertEq(record.storeId, storeA);
    }

    function test_RedeemAllUnits() public {
        uint256 typeId = _mintBatch(consumer, 50e6, 5, false);

        vm.prank(consumer);
        redemption.redeem(typeId, 5, storeA);

        assertEq(batch.balanceOf(consumer, typeId), 0);
        assertEq(redemption.totalRedemptions(), 1);
    }

    function test_RedeemMultipleTimes() public {
        uint256 typeId = _mintBatch(consumer, 100e6, 100, false);

        vm.startPrank(consumer);
        redemption.redeem(typeId, 30, storeA);
        redemption.redeem(typeId, 50, storeA);
        vm.stopPrank();

        assertEq(batch.balanceOf(consumer, typeId), 20);
        assertEq(redemption.totalRedemptions(), 2);

        Redemption1155.RedemptionRecord memory r0 = redemption.getRedemption(0);
        assertEq(r0.amount, 30);
        Redemption1155.RedemptionRecord memory r1 = redemption.getRedemption(1);
        assertEq(r1.amount, 50);
    }

    function test_RedeemZeroAmountReverts() public {
        uint256 typeId = _mintBatch(consumer, 100e6, 10, false);

        vm.prank(consumer);
        vm.expectRevert("Redemption1155: zero amount");
        redemption.redeem(typeId, 0, storeA);
    }

    function test_RedeemInsufficientBalanceReverts() public {
        uint256 typeId = _mintBatch(consumer, 100e6, 5, false);

        vm.prank(consumer);
        vm.expectRevert("Redemption1155: insufficient balance");
        redemption.redeem(typeId, 10, storeA);
    }

    function test_RedeemBlacklistedReverts() public {
        uint256 typeId = _mintBatch(consumer, 100e6, 10, false);

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

        vm.prank(consumer);
        vm.expectRevert("Redemption1155: blacklisted");
        redemption.redeem(typeId, 1, storeA);
    }

    function test_RedeemExpiredReverts() public {
        uint256 typeId = _mintBatch(consumer, 100e6, 10, false);

        // 快进到过期
        vm.warp(block.timestamp + 200 days);

        vm.prank(consumer);
        vm.expectRevert("Redemption1155: coupon expired");
        redemption.redeem(typeId, 1, storeA);
    }

    function test_RedeemStoreRestricted() public {
        uint256 typeId = _mintBatch(consumer, 100e6, 10, true);

        // 不在允许门店列表中
        bytes32 storeC = keccak256("STORE_C");
        vm.prank(consumer);
        vm.expectRevert("Redemption1155: store not allowed");
        redemption.redeem(typeId, 1, storeC);

        // 使用允许的门店
        vm.prank(consumer);
        redemption.redeem(typeId, 1, storeA);
        assertEq(batch.balanceOf(consumer, typeId), 9);
    }

    function test_RedeemNoBalanceReverts() public {
        uint256 typeId = _mintBatch(issuer, 100e6, 10, false);

        // consumer 没有该 typeId 的余额
        vm.prank(consumer);
        vm.expectRevert("Redemption1155: insufficient balance");
        redemption.redeem(typeId, 1, storeA);
    }

    // ==========================================
    // Helpers
    // ==========================================

    function _mintBatch(
        address to,
        uint256 faceValue,
        uint256 quantity,
        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);
        }

        ICouponBatch.BatchCouponConfig memory config = ICouponBatch.BatchCouponConfig({
            transferable: true,
            maxPrice: faceValue,
            expiryDate: block.timestamp + 180 days,
            minPurchase: 0,
            stackable: false,
            allowedStores: stores,
            issuer: issuer,
            faceValue: faceValue
        });

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