# Copyright 2022 Google 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.
'Prepares descriptors and timeseries for network-level route metrics.'

import itertools
import logging

from . import MetricDescriptor, TimeSeries, register_timeseries

DESCRIPTOR_ATTRS = {
    'network/routes_dynamic_used':
        'Dynamic routes limit per network',
    'network/routes_dynamic_available':
        'Dynamic routes used per network',
    'network/routes_dynamic_used_ratio':
        'Dynamic routes used ratio per network',
    'network/routes_static_used':
        'Static routes limit per network',
    'project/routes_dynamic_used':
        'Dynamic routes limit per project',
    'project/routes_dynamic_available':
        'Dynamic routes used per project',
    'project/routes_dynamic_used_ratio':
        'Dynamic routes used ratio per project',
    'project/routes_static_used':
        'Static routes limit per project',
    'project/routes_static_available':
        'Static routes used per project',
    'project/routes_static_used_ratio':
        'Static routes used ratio per project'
}
LIMITS = {'ROUTES': 250, 'ROUTES_DYNAMIC': 100}
LOGGER = logging.getLogger('net-dash.timeseries.routes')


def _dynamic(resources):
  'Computes network-level timeseries for dynamic routes.'
  for network_id, router_counts in resources['routes_dynamic'].items():
    network = resources['networks'][network_id]
    count = sum(router_counts.values())
    labels = {'project': network['project_id'], 'network': network['name']}
    limit = LIMITS['ROUTES_DYNAMIC']
    yield TimeSeries('network/routes_dynamic_used', count, labels)
    yield TimeSeries('network/routes_dynamic_available', limit, labels)
    yield TimeSeries('network/routes_dynamic_used_ratio', count / limit, labels)


def _static(resources):
  'Computes network and project-level timeseries for dynamic routes.'
  filter = lambda v: v['next_hop_type'] in ('peering', 'network')
  routes = itertools.filterfalse(filter, resources['routes'].values())
  grouped = itertools.groupby(sorted(routes, key=lambda i: i['network']),
                              lambda i: i['network'])
  project_counts = {}
  for network_id, elements in grouped:
    network = resources['networks'].get(network_id)
    count = len(list(elements))
    labels = {'project': network['project_id'], 'network': network['name']}
    yield TimeSeries('network/routes_static_used', count, labels)
    project_counts[network['project_id']] = project_counts.get(
        network['project_id'], 0) + count
  for project_id, count in project_counts.items():
    labels = {'project': project_id}
    quota = resources['quota'][project_id]['global']
    limit = quota.get('ROUTES', LIMITS['ROUTES'])
    yield TimeSeries('project/routes_static_used', count, labels)
    yield TimeSeries('project/routes_static_available', limit, labels)
    yield TimeSeries('project/routes_static_used_ratio', count / limit, labels)


@register_timeseries
def timeseries(resources):
  'Returns used/available/ratio timeseries by network and project.'
  LOGGER.info('timeseries')
  # return descriptors
  for dtype, name in DESCRIPTOR_ATTRS.items():
    labels = ('project') if dtype.startswith('project') else ('project',
                                                              'network')
    yield MetricDescriptor(dtype, name, labels, dtype.endswith('ratio'))
  # chain static and dynamic route timeseries then return each one individually
  results = itertools.chain(_static(resources), _dynamic(resources))
  for result in results:
    yield result
