// SPDX-License-Identifier: UNLICENSED
pragma solidity 0.8.9;

import "./abstract/SafeMath.sol";
import "./abstract/Address.sol";
import "./abstract/ManagerRole.sol";
import "./abstract/StrategyHandler.sol";

/**
 * @title Deed Tenant provisionning Contract for Deed NFT owners
 */
contract TenantProvisioningStrategy is ManagerRole {
    using SafeMath for uint256;
    using Address for address;

    event DelegateeAdded(address indexed account, uint256 nftId, address indexed manager);
    event DelegateeRemoved(address indexed account, uint256 nftId, address indexed manager);

    StrategyHandler public deed;

    // NFT ID => Status (Started = true)
    mapping(uint256 => bool) public tenantStatus;

    // NFT ID => Delegatee on behalf of NFT owner
    mapping(uint256 => address) public delegatees;

    /**
     * @dev Throws if called by any account other than the owner of the NFT.
     */
    modifier onlyProvisioningManager(uint256 _nftId) {
        require(isProvisioningManager(msg.sender, _nftId), "TenantProvisioningStrategy#onlyProvisioningOwner: Not provisioning owner of the NFT");
        _;
    }

    /**
     * @dev checks if the NFT is used inside another blocker strategy,
     * if so, throws an error. This will throws an error too when the NFT is already
     * used by current strategy
     */
    modifier canStart(uint256 _nftId) {
        require(deed.getStrategyUseCount(msg.sender, _nftId, address(this)) == 0, "TenantProvisioningStrategy#canStart: NFT is already used in current strategy");
        require(tenantStatus[_nftId] == false, "TenantProvisioningStrategy#canStart: NFT is already started");
        _;
    }

    constructor (StrategyHandler _deed) {
        deed = _deed;
    }

    /**
     * @dev Enroll NFT into current strategy to start corresponding Tenant in its city
     */
    function startTenant(uint256 _nftId) external onlyProvisioningManager(_nftId) canStart(_nftId) {
        try deed.startUsingNFT(msg.sender, _nftId) returns (bool response) {
            if (response == true) {
                tenantStatus[_nftId] = true;
            } else {
                revert("TenantProvisioningStrategy#startTenant: Error Starting Strategy");
            }
        } catch Error(string memory reason) {
            revert(reason);
        } catch {
            revert("TenantProvisioningStrategy#startTenant: Error Starting Strategy");
        }
    }

    /**
     * @dev ends using NFT. This will throws an error when NFT is not in use
     */
    function stopTenant(uint256 _nftId) external onlyProvisioningManager(_nftId) {
        try deed.endUsingNFT(msg.sender, _nftId) returns (bool response) {
            if (response == true) {
                delete tenantStatus[_nftId];
            } else {
                revert("TenantProvisioningStrategy#stopTenant: Error Stopping Strategy");
            }
        } catch Error(string memory reason) {
            revert(reason);
        } catch {
            revert("TenantProvisioningStrategy#stopTenant: Error Stopping Strategy");
        }
    }

    function setDelegatee(address _address, uint256 _nftId) external onlyManager returns(bool) {
        delegatees[_nftId] = _address;
        emit DelegateeAdded(_address, _nftId, _msgSender());
        return true;
    }

    function removeDelegatee(uint256 _nftId) external onlyManager returns(bool) {
        address delegatee = delegatees[_nftId];
        delete delegatees[_nftId];
        emit DelegateeRemoved(delegatee, _nftId, _msgSender());
        return true;
    }

    /**
     * @dev returns true if the address can manage NFT Tenant provisionning (Start & Stop)
     */
    function isProvisioningManager(address _address, uint256 _nftId) public view returns(bool) {
        uint256 _balance = deed.balanceOf(_address, _nftId);
        if (_balance == 0) {
            // If this is not NFT owner, we will check if the _address is approved to manage provisioning
            return _address == delegatees[_nftId];
        } else {
            // If this is about owner, the provisionning shouldn't be delegated
            return address(0x0) == delegatees[_nftId];
        }
    }

}
