// File: src/collab/CollabEligibility.sol



// SPDX-License-Identifier: UNLICENSED
pragma solidity ^0.8.19;

import {IEligibilityFilter} from "./IEligibilityFilter.sol";
import {IERC721Ownership} from "../interfaces/IERC721Ownership.sol";
import {Owned} from "solmate/auth/Owned.sol";

struct Collab {
    IEligibilityFilter eligibilityFilter;
    IERC721Ownership collection;
}

contract CollabEligibility is Owned {
    IEligibilityFilter defaultFilter;
    IERC721Ownership primaryCollection;
    mapping(uint256 => Collab) public collabs;

    constructor(IEligibilityFilter defaultFilter_, IERC721Ownership primaryCollection_)
        Owned(msg.sender)
    {
        defaultFilter = defaultFilter_;
        primaryCollection = primaryCollection_;
    }

    function registerCollab(
        uint256 collabId,
        IEligibilityFilter eligibilityFilter,
        IERC721Ownership collection
    ) external onlyOwner {
        collabs[collabId] = Collab(eligibilityFilter, collection);
    }

    function eligibilityCriteria(uint256 collabId) external view returns (string memory) {
        IEligibilityFilter eligibilityFilter = getEligibilityFilter(collabId);
        return eligibilityFilter.eligibilityCriteria();
    }

    function tokenIsEligible(uint256 collabId, uint256 tokenId) external view returns (bool) {
        IEligibilityFilter eligibilityFilter = getEligibilityFilter(collabId);

        return eligibilityFilter.tokenIsEligible(tokenId);
    }

    function tokensAreEligible(uint256 collabId, uint256[] calldata tokenIds)
        external
        view
        returns (bool[] memory)
    {
        IEligibilityFilter eligibilityFilter = getEligibilityFilter(collabId);
        uint256 count = tokenIds.length;
        bool[] memory result = new bool[](count);

        for (uint256 i; i < count;) {
            uint256 tokenId = tokenIds[i];
            if (eligibilityFilter.tokenIsEligible(tokenId)) {
                result[i] = true;
            }
            unchecked {
                ++i;
            }
        }

        return result;
    }

    function ownerIsEligible(uint256 collabId, address owner) external view returns (bool) {
        if (primaryCollection.balanceOf(owner) == 0) {
            return false;
        }
        IERC721Ownership collection = collabs[collabId].collection;
        if (address(collection) == address(0)) {
            return true;
        }

        return collection.balanceOf(owner) > 0;
    }

    function getEligibilityFilter(uint256 collabId) private view returns (IEligibilityFilter) {
        IEligibilityFilter eligibilityFilter = collabs[collabId].eligibilityFilter;
        if (address(eligibilityFilter) == address(0)) {
            eligibilityFilter = defaultFilter;
        }

        return eligibilityFilter;
    }
}

// File: src/collab/IEligibilityFilter.sol



// SPDX-License-Identifier: UNLICENSED
pragma solidity ^0.8.19;

interface IEligibilityFilter {
    function tokenIsEligible(uint256) external view returns (bool);
    function eligibilityCriteria() external view returns (string memory);
}

// File: src/interfaces/IERC721Ownership.sol



// SPDX-License-Identifier: UNLICENSED
pragma solidity ^0.8.13;

interface IERC721Ownership {
    function balanceOf(address) external view returns (uint256);
    function ownerOf(uint256) external view returns (address);
}

// File: lib/solmate/src/auth/Owned.sol



// SPDX-License-Identifier: AGPL-3.0-only
pragma solidity >=0.8.0;

/// @notice Simple single owner authorization mixin.
/// @author Solmate (https://github.com/transmissions11/solmate/blob/main/src/auth/Owned.sol)
abstract contract Owned {
    /*//////////////////////////////////////////////////////////////
                                 EVENTS
    //////////////////////////////////////////////////////////////*/

    event OwnershipTransferred(address indexed user, address indexed newOwner);

    /*//////////////////////////////////////////////////////////////
                            OWNERSHIP STORAGE
    //////////////////////////////////////////////////////////////*/

    address public owner;

    modifier onlyOwner() virtual {
        require(msg.sender == owner, "UNAUTHORIZED");

        _;
    }

    /*//////////////////////////////////////////////////////////////
                               CONSTRUCTOR
    //////////////////////////////////////////////////////////////*/

    constructor(address _owner) {
        owner = _owner;

        emit OwnershipTransferred(address(0), _owner);
    }

    /*//////////////////////////////////////////////////////////////
                             OWNERSHIP LOGIC
    //////////////////////////////////////////////////////////////*/

    function transferOwnership(address newOwner) public virtual onlyOwner {
        owner = newOwner;

        emit OwnershipTransferred(msg.sender, newOwner);
    }
}