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

import "@openzeppelin/contracts-upgradeable/proxy/utils/Initializable.sol";
import "@openzeppelin/contracts-upgradeable/access/AccessControlUpgradeable.sol";
import "@openzeppelin/contracts-upgradeable/security/ReentrancyGuardUpgradeable.sol";
import "@openzeppelin/contracts/token/ERC20/IERC20.sol";
import "@openzeppelin/contracts/token/ERC721/utils/ERC721Holder.sol";
import "./interfaces/ICoupon.sol";
import "./interfaces/ICompliance.sol";

/// @title CouponBackedSecurity — CBS 资产证券化合约
/// @notice 券收益流打包为资产支持证券，Securities Track + Broker-Dealer 牌照下运营
/// @dev 使用 Transparent Proxy 部署
contract CouponBackedSecurity is Initializable, AccessControlUpgradeable, ReentrancyGuardUpgradeable, ERC721Holder {
    bytes32 public constant ISSUER_ROLE = keccak256("ISSUER_ROLE");
    bytes32 public constant SETTLER_ROLE = keccak256("SETTLER_ROLE");
    bytes32 public constant RATING_AGENCY_ROLE = keccak256("RATING_AGENCY_ROLE");
    bytes32 public constant GOVERNANCE_ROLE = keccak256("GOVERNANCE_ROLE");

    ICompliance public compliance;
    ICoupon public couponContract;
    IERC20 public stablecoin;

    struct Pool {
        uint256[] couponIds;        // 底层券资产
        uint256 totalFaceValue;     // 底层面值总额
        uint256 totalShares;        // 份额总数
        uint256 soldShares;         // 已售份额
        uint256 maturityDate;       // 到期日
        string creditRating;        // 信用评级
        address issuer;             // 池创建方
        bool active;                // 是否活跃
        uint256 createdAt;
    }

    mapping(uint256 => Pool) private _pools;
    // 份额持有：poolId → holder → shares
    mapping(uint256 => mapping(address => uint256)) public shares;
    // 收益分配记录：poolId → holder → claimed amount
    mapping(uint256 => mapping(address => uint256)) public claimedYield;
    // 池的总收益
    mapping(uint256 => uint256) public poolYield;

    uint256 public nextPoolId;

    // --- Events ---
    event PoolCreated(
        uint256 indexed poolId,
        address indexed issuer,
        uint256 couponCount,
        uint256 totalFaceValue,
        uint256 totalShares
    );
    event SharesPurchased(uint256 indexed poolId, address indexed buyer, uint256 shareCount, uint256 amount);
    event CreditRatingSet(uint256 indexed poolId, string rating);
    event YieldDeposited(uint256 indexed poolId, uint256 totalYield);
    event YieldClaimed(uint256 indexed poolId, address indexed holder, uint256 amount);
    event PoolDeactivated(uint256 indexed poolId);

    function initialize(
        address _couponContract,
        address _compliance,
        address _stablecoin,
        address admin
    ) external initializer {
        __AccessControl_init();
        __ReentrancyGuard_init();

        couponContract = ICoupon(_couponContract);
        compliance = ICompliance(_compliance);
        stablecoin = IERC20(_stablecoin);

        _grantRole(DEFAULT_ADMIN_ROLE, admin);
        _grantRole(GOVERNANCE_ROLE, admin);
    }

    // ========================
    // 池管理
    // ========================

    /// @notice 创建 CBS 池 — 将一组券的收益流打包
    /// @param couponIds 底层券 token IDs
    /// @param totalShares 份额总数
    /// @param maturityDate 到期日
    function createPool(
        uint256[] calldata couponIds,
        uint256 totalShares,
        uint256 maturityDate
    ) external onlyRole(ISSUER_ROLE) nonReentrant returns (uint256 poolId) {
        require(couponIds.length > 0, "CBS: empty pool");
        require(totalShares > 0, "CBS: zero shares");
        require(maturityDate > block.timestamp, "CBS: invalid maturity");

        // 合规检查：创建者必须 KYC L3 + Broker-Dealer 资质
        compliance.requireKycLevel(msg.sender, 3);

        uint256 totalFace = 0;
        for (uint256 i = 0; i < couponIds.length; i++) {
            ICoupon.CouponConfig memory config = couponContract.getConfig(couponIds[i]);
            require(
                config.couponType == ICoupon.CouponType.Security,
                "CBS: only Security coupons"
            );
            totalFace += couponContract.getFaceValue(couponIds[i]);
            // 锁定底层券到池中
            couponContract.safeTransferFrom(msg.sender, address(this), couponIds[i]);
        }

        poolId = nextPoolId++;
        _pools[poolId].couponIds = couponIds;
        _pools[poolId].totalFaceValue = totalFace;
        _pools[poolId].totalShares = totalShares;
        _pools[poolId].soldShares = 0;
        _pools[poolId].maturityDate = maturityDate;
        _pools[poolId].creditRating = "";
        _pools[poolId].issuer = msg.sender;
        _pools[poolId].active = true;
        _pools[poolId].createdAt = block.timestamp;

        emit PoolCreated(poolId, msg.sender, couponIds.length, totalFace, totalShares);
    }

    /// @notice 购买 CBS 份额 — 仅合格投资者（KYC L2+）
    function purchaseShares(uint256 poolId, uint256 shareCount) external nonReentrant {
        require(_pools[poolId].active, "CBS: pool not active");
        require(shareCount > 0, "CBS: zero shares");
        require(
            _pools[poolId].soldShares + shareCount <= _pools[poolId].totalShares,
            "CBS: insufficient shares"
        );

        // 合规检查
        compliance.requireKycLevel(msg.sender, 2);

        uint256 price = (_pools[poolId].totalFaceValue * shareCount) / _pools[poolId].totalShares;
        require(price > 0, "CBS: price is zero");

        stablecoin.transferFrom(msg.sender, address(this), price);
        shares[poolId][msg.sender] += shareCount;
        _pools[poolId].soldShares += shareCount;

        emit SharesPurchased(poolId, msg.sender, shareCount, price);
    }

    // ========================
    // 信用评级
    // ========================

    /// @notice 链下评级机构写入信用评级
    function setCreditRating(uint256 poolId, string calldata rating)
        external
        onlyRole(RATING_AGENCY_ROLE)
    {
        require(_pools[poolId].active, "CBS: pool not active");
        _pools[poolId].creditRating = rating;
        emit CreditRatingSet(poolId, rating);
    }

    // ========================
    // 收益分配
    // ========================

    /// @notice 存入收益（由 clearing-service 链下计算后调用）
    function depositYield(uint256 poolId, uint256 totalYield) external onlyRole(SETTLER_ROLE) nonReentrant {
        require(_pools[poolId].active, "CBS: pool not active");
        require(totalYield > 0, "CBS: zero yield");

        stablecoin.transferFrom(msg.sender, address(this), totalYield);
        poolYield[poolId] += totalYield;

        emit YieldDeposited(poolId, totalYield);
    }

    /// @notice 持有人领取收益 — 按份额比例
    function claimYield(uint256 poolId) external nonReentrant {
        uint256 holderShares = shares[poolId][msg.sender];
        require(holderShares > 0, "CBS: no shares");

        uint256 totalEntitled = (poolYield[poolId] * holderShares) / _pools[poolId].totalShares;
        uint256 alreadyClaimed = claimedYield[poolId][msg.sender];
        uint256 claimable = totalEntitled - alreadyClaimed;
        require(claimable > 0, "CBS: nothing to claim");

        claimedYield[poolId][msg.sender] = totalEntitled;
        stablecoin.transfer(msg.sender, claimable);

        emit YieldClaimed(poolId, msg.sender, claimable);
    }

    /// @notice 停用池
    function deactivatePool(uint256 poolId) external onlyRole(GOVERNANCE_ROLE) {
        _pools[poolId].active = false;
        emit PoolDeactivated(poolId);
    }

    // ========================
    // 查询
    // ========================

    /// @notice 获取池信息
    function getPool(uint256 poolId) external view returns (
        uint256 totalFaceValue,
        uint256 totalShares,
        uint256 soldShares,
        uint256 maturityDate,
        string memory creditRating,
        address issuer,
        bool active
    ) {
        Pool storage p = _pools[poolId];
        return (p.totalFaceValue, p.totalShares, p.soldShares, p.maturityDate, p.creditRating, p.issuer, p.active);
    }

    /// @notice 获取池中的券 IDs
    function getPoolCouponIds(uint256 poolId) external view returns (uint256[] memory) {
        return _pools[poolId].couponIds;
    }

    /// @notice 计算持有人可领取的收益
    function getClaimableYield(uint256 poolId, address holder) external view returns (uint256) {
        uint256 holderShares = shares[poolId][holder];
        if (holderShares == 0 || _pools[poolId].totalShares == 0) return 0;

        uint256 totalEntitled = (poolYield[poolId] * holderShares) / _pools[poolId].totalShares;
        return totalEntitled - claimedYield[poolId][holder];
    }
}
