import logging


def aggregate_metrics(raw_metrics, default_strategies, metric_strategies=None):
  """Aggregate raw TensorBoard metrics according to collection config.

  Available aggregation strategies: `final`, `min`, `max`.

  Args:
    raw_metrics: dict mapping TensorBoard tags to list of MetricPoint.
    default_strategies: list of aggregation strategies to use as the default.
    metric_strategies: dict mapping tags to aggregation strategies.

  Returns:
    dict mapping metric name to MetricPoint.
  
  Raises:
    ValueError: If `default_strategies` is empty or an invalid strategy is
      provided.
  """
  if not raw_metrics:
    logging.warning('`raw_metrics` is empty, skipping metric aggregation.')
    return {}
  if not default_strategies:
    raise ValueError('`default_strategies` cannot be empty.')
  metric_strategies = metric_strategies or {}

  def _aggregate(metrics, strategy):
    if strategy == 'final':
      # Take the MetricPoint with the latest wall_time.
      latest_metric = metrics[0]
      for metric in metrics[1:]:
        if metric.wall_time > latest_metric.wall_time:
          latest_metric = metric
      return latest_metric
    elif strategy == 'max':
      # Take the MetricPoint with the maximum metric value.
      max_metric = metrics[0]
      for metric in metrics[1:]:
        if metric.metric_value > max_metric.metric_value:
          max_metric = metric
      return max_metric
    elif strategy == 'min':
      # Take the MetricPoint with the minimum metric value.
      min_metric = metrics[0]
      for metric in metrics[1:]:
        if metric.metric_value < min_metric.metric_value:
          min_metric = metric
      return min_metric
    else:
      raise ValueError('Unknown aggregation strategy: {}'.format(strategy))

  final_metrics = {}
  for tag, metrics in raw_metrics.items():
    strategies = metric_strategies.get(tag, default_strategies)
    for strategy in strategies:
      metric_name = '{}_{}'.format(tag, strategy)
      final_metrics[metric_name] = _aggregate(metrics, strategy)

  return final_metrics