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

import "@openzeppelin/contracts/access/Ownable.sol";
import "@openzeppelin/contracts/access/AccessControlEnumerable.sol";
import "./PBT/PBTSimple.sol";

error MintNotOpen();
error TotalSupplyReached();
error CannotUpdateDeadline();
error CannotMakeChanges();

contract PropsPBT is PBTSimple, Ownable, AccessControlEnumerable {
    bytes32 private constant CONTRACT_ADMIN_ROLE = keccak256("CONTRACT_ADMIN_ROLE");
  
    uint256 public TOTAL_SUPPLY = 300;
    uint256 public supply;

    string private _baseTokenURI;

    mapping(uint256 => bool) public tokenIsLockedState;
    mapping(uint256 => address) public tokenDelegates;

    struct UnlockRequest {
        uint256 tokenId;
        address requester;
        uint256 firstRequestedOn;
    }

    mapping(uint256 => UnlockRequest) public tokenUnlockRequests;

    event MintedTokenWithChip(address indexed chipAddress, uint256 indexed tokenId, address indexed minter);

    constructor(string memory name_, string memory symbol_)
        PBTSimple(name_, symbol_)
    {
         _setupRole(DEFAULT_ADMIN_ROLE, _msgSender());
        _setRoleAdmin(CONTRACT_ADMIN_ROLE, DEFAULT_ADMIN_ROLE);
    }

    function mint(
        bytes calldata signatureFromChip,
        uint256 blockNumberUsedInSig
    ) external {
        if (supply == TOTAL_SUPPLY) {
            revert TotalSupplyReached();
        }

         TokenData memory tokenData = _getTokenDataForChipSignature(signatureFromChip, blockNumberUsedInSig);
        
        _mintTokenWithChip(signatureFromChip, blockNumberUsedInSig);

        tokenDelegates[tokenData.tokenId] =_msgSender();
        tokenIsLockedState[tokenData.tokenId] = true;
        
        unchecked {
            ++supply;
        }

        emit MintedTokenWithChip(tokenData.chipAddress, tokenData.tokenId, _msgSender());
    }

     function updateTokenLockedState(
         bytes calldata signatureFromChip,
         uint256 blockNumberUsedInSig,
         bool isLocked
    ) external {
       uint256 tokenId = _getTokenDataForChipSignature(signatureFromChip, blockNumberUsedInSig).tokenId;
       require(tokenDelegates[tokenId] == _msgSender(), "Only authorized delegate can update token locked state");
       tokenIsLockedState[tokenId] = isLocked;
    }


    //@dev - these functions allow for someone with the chip in their possession for more than 24 hours to unlock the token for transfer

    function initiateTokenUnlock(
        bytes calldata signatureFromChip,
        uint256 blockNumberUsedInSig
    ) external {
        uint256 tokenId = _getTokenDataForChipSignature(signatureFromChip, blockNumberUsedInSig).tokenId;
        tokenUnlockRequests[tokenId] = UnlockRequest(tokenId, _msgSender(), block.timestamp);
    }

    function completeTokenUnlock(
        bytes calldata signatureFromChip,
        uint256 blockNumberUsedInSig
    ) external {
        uint256 currentTime = block.timestamp;
        uint256 tokenId = _getTokenDataForChipSignature(signatureFromChip, blockNumberUsedInSig).tokenId;
        UnlockRequest storage unlockRequest = tokenUnlockRequests[tokenId];
        require(unlockRequest.requester == _msgSender(), "Only initial requester can complete token unlock");

        //first request must have been between 48 and 24 hours ago
        require(unlockRequest.firstRequestedOn >= currentTime - 172800 && unlockRequest.firstRequestedOn <= currentTime - 86400, "No initial unlock request found for token in the required timeframe window");
        tokenIsLockedState[tokenId] = false;
    }

    function getTokenData(address chipAddress) external view returns (TokenData memory) {
        return _tokenDatas[chipAddress];
    }
    
    function getTokenDataFromScan( 
        bytes calldata signatureFromChip,
        uint256 blockNumberUsedInSig
    ) external view returns (TokenData memory) {
         TokenData memory tokenData = _getTokenDataForChipSignature(signatureFromChip, blockNumberUsedInSig);
        return tokenData;
    }

     function updateTotalSupply(
        uint256 newSupply
    ) external minRole(CONTRACT_ADMIN_ROLE) {
        TOTAL_SUPPLY = newSupply;
    }

    function seedChipToTokenMapping(
        address[] calldata chipAddresses,
        uint256[] calldata tokenIds,
        bool throwIfTokenAlreadyMinted
    ) external minRole(CONTRACT_ADMIN_ROLE) {
        _seedChipToTokenMapping(
            chipAddresses,
            tokenIds,
            throwIfTokenAlreadyMinted
        );
    }

    function updateChips(
        address[] calldata chipAddressesOld,
        address[] calldata chipAddressesNew
    ) external minRole(CONTRACT_ADMIN_ROLE) {
        _updateChips(chipAddressesOld, chipAddressesNew);
    }


    function _baseURI() internal view virtual override returns (string memory) {
        return _baseTokenURI;
    }

    function setBaseURI(string calldata baseURI) external minRole(CONTRACT_ADMIN_ROLE) {
        _baseTokenURI = baseURI;
    }

    function transferTokenViaChip(
        bytes calldata signatureFromChip,
        uint256 blockNumberUsedInSig,
        bool useSafeTransferFrom
    ) external {
        uint256 tokenId = _getTokenDataForChipSignature(signatureFromChip, blockNumberUsedInSig).tokenId;
        require( !tokenIsLockedState[tokenId], "Token is locked. If you are the owner use updateTokenLockedState() to unlock. If you are not the owner use initiateTokenUnlock() to begin the unlock process.");
        _transferTokenWithChip(signatureFromChip, blockNumberUsedInSig, useSafeTransferFrom);
        tokenDelegates[tokenId] =_msgSender();
        tokenIsLockedState[tokenId] = true;
    }

    function grantRole(bytes32 role, address account) public virtual override(AccessControl, IAccessControl) minRole(CONTRACT_ADMIN_ROLE) {
        if(!hasRole(role, account)){
            super._grantRole(role,account);
        }
    }

    /**
   * @dev Check if minimum role for function is required.
   */
    modifier minRole(bytes32 _role) {
        require(_hasMinRole(_role), "Not authorized");
        _;
    }

    function hasMinRole(bytes32 _role) public view virtual returns (bool){
        return _hasMinRole(_role);
    }

    function _hasMinRole(bytes32 _role) internal view returns (bool) {
        // @dev does account have role?
        if(hasRole(_role, _msgSender())) return true;
        // @dev are we checking against default admin?
        if(_role == DEFAULT_ADMIN_ROLE) return false;
        // @dev walk up tree to check if user has role admin role
        return _hasMinRole(getRoleAdmin(_role));
    }

     /**
   * @dev see {IERC165-supportsInterface}
   */
  function supportsInterface(bytes4 interfaceId) public view virtual override(PBTSimple, AccessControlEnumerable) returns (bool) {
      return super.supportsInterface(interfaceId);
  }

}
[{"inputs":[{"internalType":"string","name":"name_","type":"string"},{"internalType":"string","name":"symbol_","type":"string"}],"stateMutability":"nonpayable","type":"constructor"},{"inputs":[],"name":"ArrayLengthMismatch","type":"error"},{"inputs":[],"name":"BlockNumberTooOld","type":"error"},{"inputs":[],"name":"InvalidBlockNumber","type":"error"},{"inputs":[],"name":"InvalidSignature","type":"error"},{"inputs":[],"name":"NoMappedTokenForChip","type":"error"},{"inputs":[],"name":"NoMintedTokenForChip","type":"error"},{"inputs":[],"name":"SeedingChipDataForExistingToken","type":"error"},{"inputs":[],"name":"TotalSupplyReached","type":"error"},{"inputs":[],"name":"UpdatingChipForUnsetChipMapping","type":"error"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"owner","type":"address"},{"indexed":true,"internalType":"address","name":"approved","type":"address"},{"indexed":true,"internalType":"uint256","name":"tokenId","type":"uint256"}],"name":"Approval","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"owner","type":"address"},{"indexed":true,"internalType":"address","name":"operator","type":"address"},{"indexed":false,"internalType":"bool","name":"approved","type":"bool"}],"name":"ApprovalForAll","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"chipAddress","type":"address"},{"indexed":true,"internalType":"uint256","name":"tokenId","type":"uint256"},{"indexed":true,"internalType":"address","name":"minter","type":"address"}],"name":"MintedTokenWithChip","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"previousOwner","type":"address"},{"indexed":true,"internalType":"address","name":"newOwner","type":"address"}],"name":"OwnershipTransferred","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"uint256","name":"tokenId","type":"uint256"},{"indexed":true,"internalType":"address","name":"oldChipAddress","type":"address"},{"indexed":true,"internalType":"address","name":"newChipAddress","type":"address"}],"name":"PBTChipRemapping","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"uint256","name":"tokenId","type":"uint256"},{"indexed":true,"internalType":"address","name":"chipAddress","type":"address"}],"name":"PBTMint","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"bytes32","name":"role","type":"bytes32"},{"indexed":true,"internalType":"bytes32","name":"previousAdminRole","type":"bytes32"},{"indexed":true,"internalType":"bytes32","name":"newAdminRole","type":"bytes32"}],"name":"RoleAdminChanged","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"bytes32","name":"role","type":"bytes32"},{"indexed":true,"internalType":"address","name":"account","type":"address"},{"indexed":true,"internalType":"address","name":"sender","type":"address"}],"name":"RoleGranted","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"bytes32","name":"role","type":"bytes32"},{"indexed":true,"internalType":"address","name":"account","type":"address"},{"indexed":true,"internalType":"address","name":"sender","type":"address"}],"name":"RoleRevoked","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"from","type":"address"},{"indexed":true,"internalType":"address","name":"to","type":"address"},{"indexed":true,"internalType":"uint256","name":"tokenId","type":"uint256"}],"name":"Transfer","type":"event"},{"inputs":[],"name":"DEFAULT_ADMIN_ROLE","outputs":[{"internalType":"bytes32","name":"","type":"bytes32"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"TOTAL_SUPPLY","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"to","type":"address"},{"internalType":"uint256","name":"tokenId","type":"uint256"}],"name":"approve","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"owner","type":"address"}],"name":"balanceOf","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"bytes","name":"signatureFromChip","type":"bytes"},{"internalType":"uint256","name":"blockNumberUsedInSig","type":"uint256"}],"name":"completeTokenUnlock","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"tokenId","type":"uint256"}],"name":"getApproved","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"getMaxBlockhashValidWindow","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"bytes32","name":"role","type":"bytes32"}],"name":"getRoleAdmin","outputs":[{"internalType":"bytes32","name":"","type":"bytes32"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"bytes32","name":"role","type":"bytes32"},{"internalType":"uint256","name":"index","type":"uint256"}],"name":"getRoleMember","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"bytes32","name":"role","type":"bytes32"}],"name":"getRoleMemberCount","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"chipAddress","type":"address"}],"name":"getTokenData","outputs":[{"components":[{"internalType":"uint256","name":"tokenId","type":"uint256"},{"internalType":"address","name":"chipAddress","type":"address"},{"internalType":"bool","name":"set","type":"bool"}],"internalType":"struct PBTSimple.TokenData","name":"","type":"tuple"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"bytes","name":"signatureFromChip","type":"bytes"},{"internalType":"uint256","name":"blockNumberUsedInSig","type":"uint256"}],"name":"getTokenDataFromScan","outputs":[{"components":[{"internalType":"uint256","name":"tokenId","type":"uint256"},{"internalType":"address","name":"chipAddress","type":"address"},{"internalType":"bool","name":"set","type":"bool"}],"internalType":"struct PBTSimple.TokenData","name":"","type":"tuple"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"bytes32","name":"role","type":"bytes32"},{"internalType":"address","name":"account","type":"address"}],"name":"grantRole","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"bytes32","name":"_role","type":"bytes32"}],"name":"hasMinRole","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"bytes32","name":"role","type":"bytes32"},{"internalType":"address","name":"account","type":"address"}],"name":"hasRole","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"bytes","name":"signatureFromChip","type":"bytes"},{"internalType":"uint256","name":"blockNumberUsedInSig","type":"uint256"}],"name":"initiateTokenUnlock","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"owner","type":"address"},{"internalType":"address","name":"operator","type":"address"}],"name":"isApprovedForAll","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"tokenId","type":"uint256"},{"internalType":"bytes","name":"payload","type":"bytes"},{"internalType":"bytes","name":"signature","type":"bytes"}],"name":"isChipSignatureForToken","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"bytes","name":"signatureFromChip","type":"bytes"},{"internalType":"uint256","name":"blockNumberUsedInSig","type":"uint256"}],"name":"mint","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"name","outputs":[{"internalType":"string","name":"","type":"string"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"owner","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"tokenId","type":"uint256"}],"name":"ownerOf","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"renounceOwnership","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"bytes32","name":"role","type":"bytes32"},{"internalType":"address","name":"account","type":"address"}],"name":"renounceRole","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"bytes32","name":"role","type":"bytes32"},{"internalType":"address","name":"account","type":"address"}],"name":"revokeRole","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"from","type":"address"},{"internalType":"address","name":"to","type":"address"},{"internalType":"uint256","name":"tokenId","type":"uint256"}],"name":"safeTransferFrom","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"from","type":"address"},{"internalType":"address","name":"to","type":"address"},{"internalType":"uint256","name":"tokenId","type":"uint256"},{"internalType":"bytes","name":"data","type":"bytes"}],"name":"safeTransferFrom","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address[]","name":"chipAddresses","type":"address[]"},{"internalType":"uint256[]","name":"tokenIds","type":"uint256[]"},{"internalType":"bool","name":"throwIfTokenAlreadyMinted","type":"bool"}],"name":"seedChipToTokenMapping","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"operator","type":"address"},{"internalType":"bool","name":"approved","type":"bool"}],"name":"setApprovalForAll","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"string","name":"baseURI","type":"string"}],"name":"setBaseURI","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"supply","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"bytes4","name":"interfaceId","type":"bytes4"}],"name":"supportsInterface","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"symbol","outputs":[{"internalType":"string","name":"","type":"string"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"","type":"uint256"}],"name":"tokenDelegates","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"chipAddress","type":"address"}],"name":"tokenIdFor","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"chipAddress","type":"address"}],"name":"tokenIdMappedFor","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"","type":"uint256"}],"name":"tokenIsLockedState","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"tokenId","type":"uint256"}],"name":"tokenURI","outputs":[{"internalType":"string","name":"","type":"string"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"","type":"uint256"}],"name":"tokenUnlockRequests","outputs":[{"internalType":"uint256","name":"tokenId","type":"uint256"},{"internalType":"address","name":"requester","type":"address"},{"internalType":"uint256","name":"firstRequestedOn","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"from","type":"address"},{"internalType":"address","name":"to","type":"address"},{"internalType":"uint256","name":"tokenId","type":"uint256"}],"name":"transferFrom","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"newOwner","type":"address"}],"name":"transferOwnership","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"bytes","name":"signatureFromChip","type":"bytes"},{"internalType":"uint256","name":"blockNumberUsedInSig","type":"uint256"},{"internalType":"bool","name":"useSafeTransferFrom","type":"bool"}],"name":"transferTokenViaChip","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"bytes","name":"signatureFromChip","type":"bytes"},{"internalType":"uint256","name":"blockNumberUsedInSig","type":"uint256"}],"name":"transferTokenWithChip","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"bytes","name":"signatureFromChip","type":"bytes"},{"internalType":"uint256","name":"blockNumberUsedInSig","type":"uint256"},{"internalType":"bool","name":"useSafeTransferFrom","type":"bool"}],"name":"transferTokenWithChip","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address[]","name":"chipAddressesOld","type":"address[]"},{"internalType":"address[]","name":"chipAddressesNew","type":"address[]"}],"name":"updateChips","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"bytes","name":"signatureFromChip","type":"bytes"},{"internalType":"uint256","name":"blockNumberUsedInSig","type":"uint256"},{"internalType":"bool","name":"isLocked","type":"bool"}],"name":"updateTokenLockedState","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"newSupply","type":"uint256"}],"name":"updateTotalSupply","outputs":[],"stateMutability":"nonpayable","type":"function"}]