/*
 * This file is part of the Meeds project (https://meeds.io/).
 * Copyright (C) 2022 Meeds Association
 * contact@meeds.io
 * This program is free software; you can redistribute it and/or
 * modify it under the terms of the GNU Lesser General Public
 * License as published by the Free Software Foundation; either
 * version 3 of the License, or (at your option) any later version.
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 * Lesser General Public License for more details.
 * You should have received a copy of the GNU Lesser General Public License
 * along with this program; if not, write to the Free Software Foundation,
 * Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
 */
package io.meeds.wallet.statistic;

import static io.meeds.wallet.statistic.StatisticUtils.DURATION;
import static io.meeds.wallet.statistic.StatisticUtils.ERROR_MSG;
import static io.meeds.wallet.statistic.StatisticUtils.LOCAL_SERVICE;
import static io.meeds.wallet.statistic.StatisticUtils.OPERATION;
import static io.meeds.wallet.statistic.StatisticUtils.REMOTE_SERVICE;
import static io.meeds.wallet.statistic.StatisticUtils.STATUS;
import static io.meeds.wallet.statistic.StatisticUtils.STATUS_CODE;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertNotNull;
import static org.junit.Assert.assertThrows;

import java.util.Collections;
import java.util.HashMap;
import java.util.Map;
import java.util.concurrent.atomic.AtomicInteger;
import java.util.concurrent.atomic.AtomicReference;

import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.extension.ExtendWith;
import org.mockito.junit.jupiter.MockitoExtension;

import io.meeds.analytics.model.StatisticData;
import io.meeds.analytics.model.StatisticData.StatisticStatus;
import io.meeds.analytics.utils.AnalyticsUtils;
import io.meeds.wallet.model.Wallet;

@ExtendWith(MockitoExtension.class)
public class StatisticUtilsTest {

  @Test
  public void testParameterValidation() {
    assertThrows(IllegalArgumentException.class, () -> StatisticUtils.addStatisticEntry(null));
    assertThrows(IllegalArgumentException.class, () -> StatisticUtils.addStatisticEntry(Collections.emptyMap())); // NOSONAR
  }

  @Test
  public void testIsAnalyticsFeatureEnabled() {
    Map<String, Object> parameters = new HashMap<>();
    parameters.put(REMOTE_SERVICE, "REMOTE_SERVICE");
    parameters.put(OPERATION, "OPERATION");
    parameters.put(STATUS, "KO");
    parameters.put(STATUS_CODE, "500");
    parameters.put(ERROR_MSG, "ERROR_MSG");
    parameters.put(DURATION, 1220l);

    Wallet toWallet = addWallet(parameters, "to", 1l, "toWallet");
    Wallet fromWallet = addWallet(parameters, "fromWallet", 2l, "fromWallet");
    Wallet byWallet = addWallet(parameters, "byWallet", 1l, "byWallet");
    Wallet wallet = addWallet(parameters, "wallet", 1l, "wallet");

    AtomicReference<StatisticData> statisticDataReference = new AtomicReference<>();
    StatisticUtils.addStatisticEntry(new HashMap<>(parameters),
                                     statisticData -> statisticDataReference.set(statisticData));

    StatisticData statisticData = statisticDataReference.get();
    assertNotNull(statisticData);
    assertEquals("wallet", statisticData.getModule());
    assertEquals(parameters.get(REMOTE_SERVICE), statisticData.getSubModule());
    assertEquals(parameters.get(OPERATION), statisticData.getOperation());
    assertEquals(parameters.get(ERROR_MSG), statisticData.getErrorMessage());
    assertEquals(Long.parseLong(parameters.get(DURATION).toString()), statisticData.getDuration());
    assertEquals(StatisticStatus.valueOf(parameters.get(STATUS).toString().toUpperCase()), statisticData.getStatus());
    assertEquals(Long.parseLong(parameters.get(STATUS_CODE).toString()), statisticData.getErrorCode());
    assertEquals(0, statisticData.getUserId());
    assertEquals(toWallet.getTechnicalId(), statisticData.getParameters().get("toIdentityId"));
    assertEquals(toWallet.getAddress(), statisticData.getParameters().get("toWalletAddress"));
    assertEquals(fromWallet.getTechnicalId(), statisticData.getParameters().get("fromIdentityId"));
    assertEquals(fromWallet.getAddress(), statisticData.getParameters().get("fromWalletAddress"));
    assertEquals(byWallet.getTechnicalId(), statisticData.getParameters().get("byIdentityId"));
    assertEquals(byWallet.getAddress(), statisticData.getParameters().get("byWalletAddress"));
    assertEquals(wallet.getTechnicalId(),
                 statisticData.getParameters().get(AnalyticsUtils.FIELD_SOCIAL_IDENTITY_ID));
    assertEquals(wallet.getAddress(), statisticData.getParameters().get("walletAddress"));
  }

  @Test
  public void testIsAnalyticsFeatureDisabled() {
    Map<String, Object> parameters = new HashMap<>();
    parameters.put(LOCAL_SERVICE, "REMOTE_SERVICE");
    parameters.put(OPERATION, "OPERATION");
    parameters.put(STATUS, "KO");
    parameters.put(STATUS_CODE, "500");
    parameters.put(ERROR_MSG, "ERROR_MSG");
    parameters.put(DURATION, 1220l);

    addWallet(parameters, "to", 1l, "toWallet");
    addWallet(parameters, "fromWallet", 2l, "fromWallet");
    addWallet(parameters, "byWallet", 1l, "byWallet");
    addWallet(parameters, "wallet", 1l, "wallet");

    AtomicInteger analyticsConsumerInvocationCount = new AtomicInteger();
    StatisticUtils.addStatisticEntry(new HashMap<>(parameters),
                                     statisticData -> analyticsConsumerInvocationCount.incrementAndGet());
    assertEquals(1, analyticsConsumerInvocationCount.get());
  }

  private Wallet addWallet(Map<String, Object> parameters, String key, long technicalId, String address) {
    Wallet toWallet = new Wallet();
    toWallet.setTechnicalId(technicalId);
    toWallet.setAddress(address);
    parameters.put(key, toWallet);
    return toWallet;
  }

}
