// SPDX-License-Identifier: MIT
pragma solidity 0.8.15;

import { AddressManager } from "src/legacy/AddressManager.sol";

/// @custom:legacy
/// @title ResolvedDelegateProxy
/// @notice ResolvedDelegateProxy is a legacy proxy contract that makes use of the AddressManager to
///         resolve the implementation address. We're maintaining this contract for backwards
///         compatibility so we can manage all legacy proxies where necessary.
contract ResolvedDelegateProxy {
    /// @notice Mapping used to store the implementation name that corresponds to this contract. A
    ///         mapping was originally used as a way to bypass the same issue normally solved by
    ///         storing the implementation address in a specific storage slot that does not conflict
    ///         with any other storage slot. Generally NOT a safe solution but works as long as the
    ///         implementation does not also keep a mapping in the first storage slot.
    mapping(address => string) private implementationName;

    /// @notice Mapping used to store the address of the AddressManager contract where the
    ///         implementation address will be resolved from. Same concept here as with the above
    ///         mapping. Also generally unsafe but fine if the implementation doesn't keep a mapping
    ///         in the second storage slot.
    mapping(address => AddressManager) private addressManager;

    /// @param _addressManager  Address of the AddressManager.
    /// @param _implementationName implementationName of the contract to proxy to.
    constructor(AddressManager _addressManager, string memory _implementationName) {
        addressManager[address(this)] = _addressManager;
        implementationName[address(this)] = _implementationName;
    }

    /// @notice Fallback, performs a delegatecall to the resolved implementation address.
    // solhint-disable-next-line no-complex-fallback
    fallback() external payable {
        address target = addressManager[address(this)].getAddress((implementationName[address(this)]));

        require(target != address(0), "ResolvedDelegateProxy: target address must be initialized");

        // slither-disable-next-line controlled-delegatecall
        (bool success, bytes memory returndata) = target.delegatecall(msg.data);

        if (success == true) {
            assembly {
                return(add(returndata, 0x20), mload(returndata))
            }
        } else {
            assembly {
                revert(add(returndata, 0x20), mload(returndata))
            }
        }
    }
}
