# (C) Copyright 2015 Hewlett Packard Enterprise Development Company LP
# 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.

import urllib

from monasca_agent.collector.checks import AgentCheck
from monasca_agent.collector.checks.utils import add_basic_auth
from monasca_agent.common.util import headers


class Lighttpd(AgentCheck):

    """Tracks basic connection/requests/workers metrics

    See http://redmine.lighttpd.net/projects/1/wiki/Docs_ModStatus for Lighttpd details
    See http://redmine.lighttpd.net/projects/lighttpd2/wiki/Mod_status for Lighttpd2 details
    """

    URL_SUFFIX_PER_VERSION = {
        1: '?auto',
        2: '?format=plain',
        'Unknown': '?auto'
    }

    GAUGES = {
        'IdleServers': 'lighttpd.performance.idle_server',
        'BusyServers': 'lighttpd.performance.busy_servers',
        'Uptime': 'lighttpd.performance.uptime',
        'Total kBytes': 'lighttpd.net.bytes',
        'Total Accesses': 'lighttpd.net.hits',
        'memory_usage': 'lighttpd.performance.memory_usage',
        'requests_avg': 'lighttpd.net.requests_avg',
        'traffic_out_avg': 'lighttpd.net.bytes_out_avg',
        'traffic_in_avg': 'lighttpd.net.bytes_in_avg',
        'connections_avg': 'lighttpd.net.connections_avg',
        'connection_state_start': 'lighttpd.connections.state_start',
        'connection_state_read_header': 'lighttpd.connections.state_read_header',
        'connection_state_handle_request': 'lighttpd.connections.state_handle_request',
        'connection_state_write_response': 'lighttpd.connections.state_write_response',
        'connection_state_keep_alive': 'lighttpd.connections.state_keep_alive',
        'requests_avg_5sec': 'lighttpd.net.requests_avg_5sec',
        'traffic_out_avg_5sec': 'lighttpd.net.bytes_out_avg_5sec',
        'traffic_in_avg_5sec': 'lighttpd.net.bytes_in_avg_5sec',
        'connections_avg_5sec': 'lighttpd.net.connections_avg_5sec',
    }

    COUNTERS = {
        'requests_abs': 'lighttpd.net.requests_total',
        'traffic_out_abs': 'lighttpd.net.bytes_out',
        'traffic_in_abs': 'lighttpd.net.bytes_in',
        'connections_abs': 'lighttpd.net.connections_total',
        'status_1xx': 'lighttpd.response.status_1xx',
        'status_2xx': 'lighttpd.response.status_2xx',
        'status_3xx': 'lighttpd.response.status_3xx',
        'status_4xx': 'lighttpd.response.status_4xx',
        'status_5xx': 'lighttpd.response.status_5xx',
    }

    RATES = {
        'Total kBytes': 'lighttpd.net.bytes_per_s',
        'Total Accesses': 'lighttpd.net.request_per_s'
    }

    def __init__(self, name, init_config, agent_config, instances=None):
        AgentCheck.__init__(self, name, init_config, agent_config, instances)
        self.assumed_url = {}

    def check(self, instance):
        if 'lighttpd_status_url' not in instance:
            raise Exception("Missing 'lighttpd_status_url' variable in Lighttpd config")

        url = self.assumed_url.get(instance['lighttpd_status_url'], instance['lighttpd_status_url'])

        dimensions = self._set_dimensions(None, instance)
        self.log.debug("Connecting to %s" % url)
        req = urllib.request.Request(url, None, headers(self.agent_config))
        if 'user' in instance and 'password' in instance:
            add_basic_auth(req, instance['user'], instance['password'])
        request = urllib.request.urlopen(req)
        headers_resp = request.info().headers
        server_version = self._get_server_version(headers_resp)
        response = request.read()

        metric_count = 0
        # Loop through and extract the numerical values
        for line in response.split('\n'):
            values = line.split(': ')
            if len(values) == 2:  # match
                metric, value = values
                try:
                    value = float(value)
                except ValueError:
                    continue

                # Special case: kBytes => bytes
                if metric == 'Total kBytes':
                    value *= 1024

                # Send metric as a gauge, if applicable
                if metric in self.GAUGES:
                    metric_count += 1
                    metric_name = self.GAUGES[metric]
                    self.gauge(metric_name, value, dimensions=dimensions)

                # Send metric as a rate, if applicable
                if metric in self.RATES:
                    metric_count += 1
                    metric_name = self.RATES[metric]
                    self.rate(metric_name, value, dimensions=dimensions)

                # Send metric as a counter, if applicable
                if metric in self.COUNTERS:
                    metric_count += 1
                    metric_name = self.COUNTERS[metric]
                    self.increment(metric_name, value, dimensions=dimensions)

        if metric_count == 0:
            url_suffix = self.URL_SUFFIX_PER_VERSION[server_version]
            if self.assumed_url.get(instance['lighttpd_status_url'],
                                    None) is None and url[-len(url_suffix):] != url_suffix:
                self.assumed_url[instance['lighttpd_status_url']] = '%s%s' % (url, url_suffix)
                self.log.warn(
                    "Assuming url was not correct. Trying to add %s suffix to the url" %
                    url_suffix)
                self.check(instance)
            else:
                raise Exception(
                    "No metrics were fetched for this instance. "
                    "Make sure that %s is the proper url." %
                    instance['lighttpd_status_url'])

    def _get_server_version(self, headers):
        for h in headers:
            if "Server:" not in h:
                continue
            try:
                version = int(h.split('/')[1][0])
            except Exception as e:
                self.log.debug("Error while trying to get server version %s" % str(e))
                version = "Unknown"
            self.log.debug("Lighttpd server version is %s" % version)
            return version

        self.log.debug("Lighttpd server version is Unknown")
        return "Unknown"
