# -*- coding: utf-8 -*-
# Description: litespeed netdata python.d module
# Author: Ilya Maschenko (ilyam8)
# SPDX-License-Identifier: GPL-3.0-or-later

import glob
import re
import os

from collections import namedtuple

from bases.FrameworkServices.SimpleService import SimpleService


update_every = 10

# charts order (can be overridden if you want less charts, or different order)
ORDER = [
    'net_throughput_http',   # net throughput
    'net_throughput_https',  # net throughput
    'connections_http',      # connections
    'connections_https',     # connections
    'requests',              # requests
    'requests_processing',   # requests
    'pub_cache_hits',        # cache
    'private_cache_hits',    # cache
    'static_hits',           # static
]

CHARTS = {
    'net_throughput_http': {
        'options': [None, 'Network Throughput HTTP', 'kilobits/s', 'net throughput',
                    'litespeed.net_throughput', 'area'],
        'lines': [
            ['bps_in', 'in', 'absolute'],
            ['bps_out', 'out', 'absolute', -1]
        ]
    },
    'net_throughput_https': {
        'options': [None, 'Network Throughput HTTPS', 'kilobits/s', 'net throughput',
                    'litespeed.net_throughput', 'area'],
        'lines': [
            ['ssl_bps_in', 'in', 'absolute'],
            ['ssl_bps_out', 'out', 'absolute', -1]
        ]
    },
    'connections_http': {
        'options': [None, 'Connections HTTP', 'conns', 'connections', 'litespeed.connections', 'stacked'],
        'lines': [
            ['conn_free', 'free', 'absolute'],
            ['conn_used', 'used', 'absolute']
        ]
    },
    'connections_https': {
        'options': [None, 'Connections HTTPS', 'conns', 'connections', 'litespeed.connections', 'stacked'],
        'lines': [
            ['ssl_conn_free', 'free', 'absolute'],
            ['ssl_conn_used', 'used', 'absolute']
        ]
    },
    'requests': {
        'options': [None, 'Requests', 'requests/s', 'requests', 'litespeed.requests', 'line'],
        'lines': [
            ['requests', None, 'absolute', 1, 100]
        ]
    },
    'requests_processing': {
        'options': [None, 'Requests In Processing', 'requests', 'requests', 'litespeed.requests_processing', 'line'],
        'lines': [
            ['requests_processing', 'processing', 'absolute']
        ]
    },
    'pub_cache_hits': {
        'options': [None, 'Public Cache Hits', 'hits/s', 'cache', 'litespeed.cache', 'line'],
        'lines': [
            ['pub_cache_hits', 'hits', 'absolute', 1, 100]
        ]
    },
    'private_cache_hits': {
        'options': [None, 'Private Cache Hits', 'hits/s', 'cache', 'litespeed.cache', 'line'],
        'lines': [
            ['private_cache_hits', 'hits', 'absolute', 1, 100]
        ]
    },
    'static_hits': {
        'options': [None, 'Static Hits', 'hits/s', 'static', 'litespeed.static', 'line'],
        'lines': [
            ['static_hits', 'hits', 'absolute', 1, 100]
        ]
    }
}

t = namedtuple('T', ['key', 'id', 'mul'])

T = [
    t('BPS_IN', 'bps_in', 8),
    t('BPS_OUT', 'bps_out', 8),
    t('SSL_BPS_IN', 'ssl_bps_in', 8),
    t('SSL_BPS_OUT', 'ssl_bps_out', 8),
    t('REQ_PER_SEC', 'requests', 100),
    t('REQ_PROCESSING', 'requests_processing', 1),
    t('PUB_CACHE_HITS_PER_SEC', 'pub_cache_hits', 100),
    t('PRIVATE_CACHE_HITS_PER_SEC', 'private_cache_hits', 100),
    t('STATIC_HITS_PER_SEC', 'static_hits', 100),
    t('PLAINCONN', 'conn_used', 1),
    t('AVAILCONN', 'conn_free', 1),
    t('SSLCONN', 'ssl_conn_used', 1),
    t('AVAILSSL', 'ssl_conn_free', 1),
]

RE = re.compile(r'([A-Z_]+): ([0-9.]+)')

ZERO_DATA = {
    'bps_in': 0,
    'bps_out': 0,
    'ssl_bps_in': 0,
    'ssl_bps_out': 0,
    'requests': 0,
    'requests_processing': 0,
    'pub_cache_hits': 0,
    'private_cache_hits': 0,
    'static_hits': 0,
    'conn_used': 0,
    'conn_free': 0,
    'ssl_conn_used': 0,
    'ssl_conn_free': 0,
}


class Service(SimpleService):
    def __init__(self, configuration=None, name=None):
        SimpleService.__init__(self, configuration=configuration, name=name)
        self.order = ORDER
        self.definitions = CHARTS
        self.path = self.configuration.get('path', '/tmp/lshttpd/')
        self.files = list()

    def check(self):
        if not self.path:
            self.error('"path" not specified')
            return False

        fs = glob.glob(os.path.join(self.path, '.rtreport*'))

        if not fs:
            self.error('"{0}" has no "rtreport" files or dir is not readable'.format(self.path))
            return None

        self.debug('stats files:', fs)

        for f in fs:
            if not is_readable_file(f):
                self.error('{0} is not readable'.format(f))
                continue
            self.files.append(f)

        return bool(self.files)

    def get_data(self):
        """
        Format data received from http request
        :return: dict
        """
        data = dict(ZERO_DATA)

        for f in self.files:
            try:
                with open(f) as b:
                    lines = b.readlines()
            except (OSError, IOError) as err:
                self.error(err)
                return None
            else:
                parse_file(data, lines)

        return data


def parse_file(data, lines):
    for line in lines:
        if not line.startswith(('BPS_IN:', 'MAXCONN:', 'REQ_RATE []:')):
            continue
        m = dict(RE.findall(line))
        for v in T:
            if v.key in m:
                data[v.id] += float(m[v.key]) * v.mul


def is_readable_file(v):
    return os.path.isfile(v) and os.access(v, os.R_OK)