/*
 * Copyright (C) 2023 Hedera Hashgraph, LLC
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *      http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

package com.hedera.services.store.contracts.precompile.codec;

import static com.hedera.node.app.service.evm.store.contracts.precompile.codec.EvmDecodingFacade.decodeFunctionCall;
import static com.hedera.node.app.service.evm.store.contracts.utils.EvmParsingConstants.ARRAY_BRACKETS;
import static com.hedera.node.app.service.evm.store.contracts.utils.EvmParsingConstants.FIXED_FEE;
import static com.hedera.node.app.service.evm.store.contracts.utils.EvmParsingConstants.INT;
import static com.hedera.node.app.service.evm.store.contracts.utils.EvmParsingConstants.ROYALTY_FEE;
import static com.hedera.services.store.contracts.precompile.codec.DecodingFacade.FIXED_FEE_DECODER;
import static com.hedera.services.store.contracts.precompile.codec.DecodingFacade.HEDERA_TOKEN_STRUCT_DECODER;
import static com.hedera.services.store.contracts.precompile.codec.DecodingFacade.HEDERA_TOKEN_STRUCT_V2;
import static com.hedera.services.store.contracts.precompile.codec.DecodingFacade.ROYALTY_FEE_DECODER;
import static com.hedera.services.store.contracts.precompile.codec.DecodingFacade.decodeAccountIds;
import static com.hedera.services.store.contracts.precompile.codec.DecodingFacade.decodeTokenExpiry;
import static com.hedera.services.store.contracts.precompile.codec.DecodingFacade.decodeTokenKeys;
import static com.hedera.services.store.contracts.precompile.codec.DecodingFacade.removeBrackets;
import static java.util.function.UnaryOperator.identity;
import static org.junit.jupiter.api.Assertions.*;

import com.esaulpaugh.headlong.abi.ABIType;
import com.esaulpaugh.headlong.abi.Function;
import com.esaulpaugh.headlong.abi.Tuple;
import com.esaulpaugh.headlong.abi.TypeFactory;
import org.apache.tuweni.bytes.Bytes;
import org.junit.jupiter.api.Test;

class DecodingFacadeTest {

    private static final String CREATE_NON_FUNGIBLE_TOKEN_WITH_FEES_STRING = "createNonFungibleTokenWithCustomFees(";
    private static final ABIType<Tuple> TOKEN_CREATE_NON_FUNGIBLE_WITH_FEES_DECODER = TypeFactory.create("("
            + HEDERA_TOKEN_STRUCT_DECODER
            + ","
            + FIXED_FEE_DECODER
            + ARRAY_BRACKETS
            + ","
            + ROYALTY_FEE_DECODER
            + ARRAY_BRACKETS
            + ")");

    private static final Function TOKEN_CREATE_NON_FUNGIBLE_WITH_FEES_FUNCTION_V2 =
            new Function(CREATE_NON_FUNGIBLE_TOKEN_WITH_FEES_STRING
                    + HEDERA_TOKEN_STRUCT_V2
                    + ","
                    + FIXED_FEE
                    + ARRAY_BRACKETS
                    + ","
                    + ROYALTY_FEE
                    + ARRAY_BRACKETS
                    + ")");

    private static final Bytes TOKEN_CREATE_NON_FUNGIBLE_WITH_FEES_SELECTOR_V2 =
            Bytes.wrap(TOKEN_CREATE_NON_FUNGIBLE_WITH_FEES_FUNCTION_V2.selector());
    private static final Function TRANSFER_NFTS_FUNCTION =
            new Function("transferNFTs(address,address[],address[],int64[])", INT);
    private static final ABIType<Tuple> TRANSFER_NFTS_DECODER =
            TypeFactory.create("(bytes32,bytes32[],bytes32[],int64[])");
    private static final Bytes TRANSFER_NFTS_SELECTOR = Bytes.wrap(TRANSFER_NFTS_FUNCTION.selector());
    private static final Bytes CREATE_NON_FUNGIBLE_WITH_FEES_INPUT_V2 = Bytes.fromHexString(
            "0x457339690000000000000000000000000000000000000000000000000000000000000060000000000000000000000000000000000000000000000000000000000000052000000000000000000000000000000000000000000000000000000000000005e0000000000000000000000000000000000000000000000000000000000000016000000000000000000000000000000000000000000000000000000000000001a0000000000000000000000000000000000000000000000000000000000000000100000000000000000000000000000000000000000000000000000000000001e000000000000000000000000000000000000000000000000000000000000000010000000000000000000000000000000000000000000000007fffffffffffffff000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000002200000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000400000000000000000000000000000000000000000000000000000000007a120000000000000000000000000000000000000000000000000000000000000000074d794e465456320000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000054e4654563200000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000096e66744d656d6f56320000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000200000000000000000000000000000000000000000000000000000000000000400000000000000000000000000000000000000000000000000000000000000160000000000000000000000000000000000000000000000000000000000000000100000000000000000000000000000000000000000000000000000000000000400000000000000000000000000000000000000000000000000000000000000001000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000a000000000000000000000000000000000000000000000000000000000000000c0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000005000000000000000000000000000000000000000000000000000000000000000400000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000100000000000000000000000000000000000000000000000000000000000000a000000000000000000000000000000000000000000000000000000000000000c00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000001000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000010000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000010000000000000000000000000000000000000000000000000000000000000001000000000000000000000000000000000000000000000000000000000000000100000000000000000000000000000000000000000000000000000000000000020000000000000000000000000000000000000000000000000000000000000003000000000000000000000000000000000000000000000000000000000000000100000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000002");

    private static final Bytes TRANSFER_NFTS_INPUT = Bytes.fromHexString(
            "0x2c4ba191000000000000000000000000000000000000000000000000000000000000047a000000000000000000000000000000000000000000000000000000000000008000000000000000000000000000000000000000000000000000000000000000e000000000000000000000000000000000000000000000000000000000000001400000000000000000000000000000000000000000000000000000000000000002000000000000000000000000000000000000000000000000000000000000047700000000000000000000000000000000000000000000000000000000000004770000000000000000000000000000000000000000000000000000000000000002000000000000000000000000000000000000000000000000000000000000047c000000000000000000000000000000000000000000000000000000000000047c0000000000000000000000000000000000000000000000000000000000000002000000000000000000000000000000000000000000000000000000000000007b00000000000000000000000000000000000000000000000000000000000000ea");

    @Test
    void decodeTokenKeysTest() {

        final Tuple decodedArguments = decodeFunctionCall(
                CREATE_NON_FUNGIBLE_WITH_FEES_INPUT_V2,
                TOKEN_CREATE_NON_FUNGIBLE_WITH_FEES_SELECTOR_V2,
                TOKEN_CREATE_NON_FUNGIBLE_WITH_FEES_DECODER);
        final Tuple tokenCreateStruct = decodedArguments.get(0);
        final var decodedInput = decodeTokenKeys(tokenCreateStruct.get(7), identity());

        assertFalse(decodedInput.get(1).isUsedForAdminKey());
        assertTrue(decodedInput.get(0).isUsedForAdminKey());
        assertFalse(decodedInput.get(0).isUsedForKycKey());
        assertFalse(decodedInput.get(0).isUsedForFreezeKey());
        assertFalse(decodedInput.get(0).isUsedForSupplyKey());
        assertFalse(decodedInput.get(0).isUsedForWipeKey());
        assertFalse(decodedInput.get(0).isUsedForFeeScheduleKey());
        assertFalse(decodedInput.get(0).isUsedForPauseKey());
        assertEquals(0, decodedInput.get(0).key().getEd25519Key().length);
        assertNull(decodedInput.get(0).key().getContractID());
        assertNull(decodedInput.get(0).key().getDelegatableContractID());
        assertNotNull(decodedInput.get(0).key().getEcdsaSecp256k1());
        assertNotNull(decodedInput.get(0).key().getKeyValueType());
    }

    @Test
    void decodeTokenExpiryTest() {
        final Tuple decodedArguments = decodeFunctionCall(
                CREATE_NON_FUNGIBLE_WITH_FEES_INPUT_V2,
                TOKEN_CREATE_NON_FUNGIBLE_WITH_FEES_SELECTOR_V2,
                TOKEN_CREATE_NON_FUNGIBLE_WITH_FEES_DECODER);
        final Tuple tokenCreateStruct = decodedArguments.get(0);
        final var decodedInput = decodeTokenExpiry(tokenCreateStruct.get(8), identity());
        assertEquals(0, decodedInput.second());
    }

    @Test
    void decodeAccountIdTest() {
        final Tuple decodedArguments =
                decodeFunctionCall(TRANSFER_NFTS_INPUT, TRANSFER_NFTS_SELECTOR, TRANSFER_NFTS_DECODER);
        final var decodedInput = decodeAccountIds(decodedArguments.get(1), identity());
        assertEquals(1143, decodedInput.get(0).getAccountNum());
    }

    @Test
    void removeBracketsTest() {
        String input = "(test)";
        String expectedOutput = "test";
        String actualOutput = removeBrackets(input);

        assertEquals(expectedOutput, actualOutput);
    }
}
