/*
 * Copyright (C) 2020 The Android Open Source Project
 *
 * 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.
 */

#pragma once

#include <vector>

#include "anomaly/AlarmMonitor.h"
#include "anomaly/AlarmTracker.h"
#include "condition/ConditionTracker.h"
#include "config/ConfigMetadataProvider.h"
#include "external/StatsPullerManager.h"
#include "matchers/AtomMatchingTracker.h"
#include "metrics/MetricProducer.h"
#include "metrics_manager_util.h"

namespace android {
namespace os {
namespace statsd {

// Helper functions for MetricsManager to update itself from a new StatsdConfig.
// *Note*: only updateStatsdConfig() should be called from outside this file.
// All other functions are intermediate steps, created to make unit testing easier.

// Recursive function to determine if a matcher needs to be updated.
// input:
// [config]: the input StatsdConfig
// [matcher]: the current matcher to be updated
// [oldAtomMatchingTrackerMap]: matcher id to index mapping in the existing MetricsManager
// [oldAtomMatchingTrackers]: stores the existing AtomMatchingTrackers
// [allAtomMatcherMap]: matcher id to atom matcher mapping in the input StatsdConfig
// output:
// [matchersToUpdate]: map of the matcherId to update status of each matcher. The matcher status
// will
//                     be updated from UPDATE_UNKNOWN after this call.
// [cycleTracker]: intermediate param used during recursion.
// Returns nullopt if successful and InvalidConfigReason if not.
std::optional<InvalidConfigReason> determineMatcherUpdateStatus(
        const StatsdConfig& config, const AtomMatcher& matcher,
        const std::unordered_map<int64_t, int>& oldAtomMatchingTrackerMap,
        const std::vector<sp<AtomMatchingTracker>>& oldAtomMatchingTrackers,
        const std::unordered_map<int64_t, AtomMatcherValue>& allAtomMatcherMap,
        std::unordered_map<int64_t, UpdateStatus>& matchersToUpdate,
        std::unordered_set<int64_t>& cycleTracker);

// Updates the AtomMatchingTrackers.
// input:
// [config]: the input StatsdConfig
// [oldAtomMatchingTrackerMap]: existing matcher id to index mapping
// [oldAtomMatchingTrackers]: stores the existing AtomMatchingTrackers
// output:
// [allTagIdsToMatchersMap]: maps of tag ids to atom matchers
// [newAtomMatchingTrackerMap]: new matcher id to index mapping
// [newAtomMatchingTrackers]: stores the new AtomMatchingTrackers
// [replacedMatchers]: set of matcher ids that changed and have been replaced
// [invalidEntities]: a map of entity id to the reason why the entity is invalid
// Returns true is all atom matchers are valid.
bool updateAtomMatchingTrackers(
        const StatsdConfig& config, const sp<UidMap>& uidMap,
        const std::unordered_map<int64_t, int>& oldAtomMatchingTrackerMap,
        const std::vector<sp<AtomMatchingTracker>>& oldAtomMatchingTrackers,
        std::unordered_map<int, std::vector<int>>& allTagIdsToMatchersMap,
        std::unordered_map<int64_t, int>& newAtomMatchingTrackerMap,
        std::vector<sp<AtomMatchingTracker>>& newAtomMatchingTrackers,
        std::set<int64_t>& replacedMatchers,
        std::unordered_map<InvalidEntityKey, InvalidConfigReason>& invalidEntities);

// Recursive function to determine if a condition needs to be updated.
// input:
// [config]: the input StatsdConfig
// [predicate]: the predicate to be updated
// [oldConditionTrackerMap]: condition id to index mapping in the existing MetricsManager
// [oldConditionTrackers]: stores the existing ConditionTrackers
// [allConditionsMap]: condition id to predicate mapping in the input StatsdConfig
// [replacedMatchers]: set of replaced matcher ids. conditions using these matchers must be replaced
// output:
// [conditionsToUpdate]: vector of the update status of each condition. The conditionIdx index will
//                       be updated from UPDATE_UNKNOWN after this call.
// [cycleTracker]: intermediate param used during recursion.
// Returns nullopt if successful and InvalidConfigReason if not.
std::optional<InvalidConfigReason> determineConditionUpdateStatus(
        const StatsdConfig& config, const Predicate& predicate,
        const std::unordered_map<int64_t, int>& oldConditionTrackerMap,
        const std::vector<sp<ConditionTracker>>& oldConditionTrackers,
        const std::unordered_map<int64_t, ConditionProtoAndTracker>& allConditionsMap,
        const std::set<int64_t>& replacedMatchers,
        std::unordered_map<int64_t, UpdateStatus>& conditionsToUpdate,
        std::unordered_set<int64_t>& cycleTracker);

// Updates ConditionTrackers
// input:
// [config]: the input config
// [atomMatchingTrackerMap]: AtomMatchingTracker name to index mapping from previous step.
// [replacedMatchers]: ids of replaced matchers. conditions depending on these must also be replaced
// [oldConditionTrackerMap]: existing matcher id to index mapping
// [oldConditionTrackers]: stores the existing ConditionTrackers
// output:
// [newConditionTrackerMap]: new condition id to index mapping
// [newConditionTrackers]: stores the sp to all the ConditionTrackers
// [trackerToConditionMap]: contains the mapping from the index of an atom matcher
//                          to indices of condition trackers that use the matcher
// [conditionCache]: stores the current conditions for each ConditionTracker
// [replacedConditions]: set of condition ids that have changed and have been replaced
// [invalidEntities]: a map of entity id to the reason why the entity is invalid
// Returns whether all conditions were valid.
bool updateConditions(const ConfigKey& key, const StatsdConfig& config,
                      const std::unordered_map<int64_t, int>& atomMatchingTrackerMap,
                      const std::set<int64_t>& replacedMatchers,
                      const std::unordered_map<int64_t, int>& oldConditionTrackerMap,
                      const std::vector<sp<ConditionTracker>>& oldConditionTrackers,
                      std::unordered_map<int64_t, int>& newConditionTrackerMap,
                      std::vector<sp<ConditionTracker>>& newConditionTrackers,
                      std::unordered_map<int, std::vector<int>>& trackerToConditionMap,
                      std::vector<ConditionState>& conditionCache,
                      std::set<int64_t>& replacedConditions,
                      std::unordered_map<InvalidEntityKey, InvalidConfigReason>& invalidEntities);

bool updateStates(const StatsdConfig& config,
                  const std::map<int64_t, uint64_t>& oldStateProtoHashes,
                  std::unordered_map<int64_t, int>& stateAtomIdMap,
                  std::unordered_map<int64_t, std::unordered_map<int, int64_t>>& allStateGroupMaps,
                  std::map<int64_t, uint64_t>& newStateProtoHashes,
                  std::set<int64_t>& replacedStates,
                  std::unordered_map<InvalidEntityKey, InvalidConfigReason>& invalidEntities);

// Function to determine the update status (preserve/replace/new) of all metrics in the config.
// [config]: the input StatsdConfig
// [oldMetricProducerMap]: metric id to index mapping in the existing MetricsManager
// [oldMetricProducers]: stores the existing MetricProducers
// [metricToActivationMap]:  map from metric id to metric activation index
// [replacedMatchers]: set of replaced matcher ids. metrics using these matchers must be replaced
// [replacedConditions]: set of replaced conditions. metrics using these conditions must be replaced
// [replacedStates]: set of replaced state ids. metrics using these states must be replaced
// output:
// [metricsToUpdate]: update status of each metric. Will be changed from UPDATE_UNKNOWN
// Returns nullopt if successful and InvalidConfigReason if not.
std::optional<InvalidConfigReason> determineAllMetricUpdateStatuses(
        const StatsdConfig& config, const std::unordered_map<int64_t, int>& oldMetricProducerMap,
        const std::vector<sp<MetricProducer>>& oldMetricProducers,
        const std::unordered_map<int64_t, int>& metricToActivationMap,
        const std::set<int64_t>& replacedMatchers, const std::set<int64_t>& replacedConditions,
        const std::set<int64_t>& replacedStates, std::vector<UpdateStatus>& metricsToUpdate);

// Update MetricProducers.
// input:
// [key]: the config key that this config belongs to
// [config]: the input config
// [timeBaseNs]: start time base for all metrics
// [currentTimeNs]: time of the config update
// [atomMatchingTrackerMap]: AtomMatchingTracker name to index mapping from previous step.
// [replacedMatchers]: ids of replaced matchers. Metrics depending on these must also be replaced
// [allAtomMatchingTrackers]: stores the sp of the atom matchers.
// [conditionTrackerMap]: condition name to index mapping
// [replacedConditions]: set of condition ids that have changed and have been replaced
// [stateAtomIdMap]: contains the mapping from state ids to atom ids
// [allStateGroupMaps]: contains the mapping from atom ids and state values to
//                      state group ids for all states
// output:
// [allMetricProducers]: contains the list of sp to the MetricProducers created.
// [conditionToMetricMap]: contains the mapping from condition tracker index to
//                          the list of MetricProducer index
// [trackerToMetricMap]: contains the mapping from log tracker to MetricProducer index.
// Returns nullopt if successful and InvalidConfigReason if not.
std::optional<InvalidConfigReason> updateMetrics(
        const ConfigKey& key, const StatsdConfig& config, int64_t timeBaseNs,
        const int64_t currentTimeNs, const sp<StatsPullerManager>& pullerManager,
        const std::unordered_map<int64_t, int>& oldAtomMatchingTrackerMap,
        const std::unordered_map<int64_t, int>& newAtomMatchingTrackerMap,
        const std::set<int64_t>& replacedMatchers,
        const std::vector<sp<AtomMatchingTracker>>& allAtomMatchingTrackers,
        const std::unordered_map<int64_t, int>& conditionTrackerMap,
        const std::set<int64_t>& replacedConditions,
        std::vector<sp<ConditionTracker>>& allConditionTrackers,
        const std::vector<ConditionState>& initialConditionCache,
        const std::unordered_map<int64_t, int>& stateAtomIdMap,
        const std::unordered_map<int64_t, std::unordered_map<int, int64_t>>& allStateGroupMaps,
        const std::set<int64_t>& replacedStates,
        const std::unordered_map<int64_t, int>& oldMetricProducerMap,
        const std::vector<sp<MetricProducer>>& oldMetricProducers,
        const wp<ConfigMetadataProvider> configMetadataProvider,
        std::unordered_map<int64_t, int>& newMetricProducerMap,
        std::vector<sp<MetricProducer>>& newMetricProducers,
        std::unordered_map<int, std::vector<int>>& conditionToMetricMap,
        std::unordered_map<int, std::vector<int>>& trackerToMetricMap,
        std::set<int64_t>& noReportMetricIds,
        std::unordered_map<int, std::vector<int>>& activationAtomTrackerToMetricMap,
        std::unordered_map<int, std::vector<int>>& deactivationAtomTrackerToMetricMap,
        std::vector<int>& metricsWithActivation, std::set<int64_t>& replacedMetrics);

// Function to determine the update status (preserve/replace/new) of an alert.
// [alert]: the input Alert
// [oldAlertTrackerMap]: alert id to index mapping in the existing MetricsManager
// [oldAnomalyTrackers]: stores the existing AnomalyTrackers
// [replacedMetrics]: set of replaced metric ids. alerts using these metrics must be replaced
// output:
// [updateStatus]: update status of the alert. Will be changed from UPDATE_UNKNOWN
// Returns nullopt if successful and InvalidConfigReason if not.
std::optional<InvalidConfigReason> determineAlertUpdateStatus(
        const Alert& alert, const std::unordered_map<int64_t, int>& oldAlertTrackerMap,
        const std::vector<sp<AnomalyTracker>>& oldAnomalyTrackers,
        const std::set<int64_t>& replacedMetrics, UpdateStatus& updateStatus);

// Update MetricProducers.
// input:
// [config]: the input config
// [currentTimeNs]: time of the config update
// [metricProducerMap]: metric id to index mapping in the new config
// [replacedMetrics]: set of metric ids that have changed and were replaced
// [oldAlertTrackerMap]: alert id to index mapping in the existing MetricsManager.
// [oldAnomalyTrackers]: stores the existing AnomalyTrackers
// [anomalyAlarmMonitor]: AlarmMonitor used for duration metric anomaly detection
// [allMetricProducers]: stores the sp of the metric producers, AnomalyTrackers need to be added.
// [stateAtomIdMap]: contains the mapping from state ids to atom ids
// [allStateGroupMaps]: contains the mapping from atom ids and state values to
//                      state group ids for all states
// output:
// [newAlertTrackerMap]: mapping of alert id to index in the new config
// [newAnomalyTrackers]: contains the list of sp to the AnomalyTrackers created.
// Returns nullopt if successful and InvalidConfigReason if not.
std::optional<InvalidConfigReason> updateAlerts(
        const StatsdConfig& config, int64_t currentTimeNs,
        const std::unordered_map<int64_t, int>& metricProducerMap,
        const std::set<int64_t>& replacedMetrics,
        const std::unordered_map<int64_t, int>& oldAlertTrackerMap,
        const std::vector<sp<AnomalyTracker>>& oldAnomalyTrackers,
        const sp<AlarmMonitor>& anomalyAlarmMonitor,
        std::vector<sp<MetricProducer>>& allMetricProducers,
        std::unordered_map<int64_t, int>& newAlertTrackerMap,
        std::vector<sp<AnomalyTracker>>& newAnomalyTrackers);

// Updates the existing MetricsManager from a new StatsdConfig.
// Parameters are the members of MetricsManager. See MetricsManager for declaration.
std::optional<InvalidConfigReason> updateStatsdConfig(
        const ConfigKey& key, const StatsdConfig& config, const sp<UidMap>& uidMap,
        const sp<StatsPullerManager>& pullerManager, const sp<AlarmMonitor>& anomalyAlarmMonitor,
        const sp<AlarmMonitor>& periodicAlarmMonitor, int64_t timeBaseNs,
        const int64_t currentTimeNs,
        const std::vector<sp<AtomMatchingTracker>>& oldAtomMatchingTrackers,
        const std::unordered_map<int64_t, int>& oldAtomMatchingTrackerMap,
        const std::vector<sp<ConditionTracker>>& oldConditionTrackers,
        const std::unordered_map<int64_t, int>& oldConditionTrackerMap,
        const std::vector<sp<MetricProducer>>& oldMetricProducers,
        const std::unordered_map<int64_t, int>& oldMetricProducerMap,
        const std::vector<sp<AnomalyTracker>>& oldAnomalyTrackers,
        const std::unordered_map<int64_t, int>& oldAlertTrackerMap,
        const std::map<int64_t, uint64_t>& oldStateProtoHashes,
        const wp<ConfigMetadataProvider> configMetadataProvider,
        std::unordered_map<int, std::vector<int>>& allTagIdsToMatchersMap,
        std::vector<sp<AtomMatchingTracker>>& newAtomMatchingTrackers,
        std::unordered_map<int64_t, int>& newAtomMatchingTrackerMap,
        std::vector<sp<ConditionTracker>>& newConditionTrackers,
        std::unordered_map<int64_t, int>& newConditionTrackerMap,
        std::vector<sp<MetricProducer>>& newMetricProducers,
        std::unordered_map<int64_t, int>& newMetricProducerMap,
        std::vector<sp<AnomalyTracker>>& newAlertTrackers,
        std::unordered_map<int64_t, int>& newAlertTrackerMap,
        std::vector<sp<AlarmTracker>>& newPeriodicAlarmTrackers,
        std::unordered_map<int, std::vector<int>>& conditionToMetricMap,
        std::unordered_map<int, std::vector<int>>& trackerToMetricMap,
        std::unordered_map<int, std::vector<int>>& trackerToConditionMap,
        std::unordered_map<int, std::vector<int>>& activationTrackerToMetricMap,
        std::unordered_map<int, std::vector<int>>& deactivationTrackerToMetricMap,
        std::vector<int>& metricsWithActivation, std::map<int64_t, uint64_t>& newStateProtoHashes,
        std::set<int64_t>& noReportMetricIds);

}  // namespace statsd
}  // namespace os
}  // namespace android
