# Copyright 2016 FUJITSU LIMITED
# (C) Copyright 2016 Hewlett Packard Enterprise Development 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 logging
import os

import requests

from monasca_setup import agent_config
from monasca_setup import detection
from monasca_setup.detection import utils

LOG = logging.getLogger(__name__)

_KIBANA_CFG_FILE = '/opt/kibana/config/kibana.yml'
_API_STATUS = 'api/status'
_METRIC_ALIASES = {
    'heap_total': 'heap_size',
    'requests_per_second': 'req_sec',
    'response_time_avg': 'resp_time_avg',
    'response_time_max': 'resp_time_max'
}


def _to_snake_case(word):
    final = ''
    for item in word:
        if item.isupper():
            final += "_" + item.lower()
        else:
            final += item
    if final[0] == "_":
        final = final[1:]
    return final


def get_metric_name(metric):
    actual_name = _to_snake_case(metric)
    return _METRIC_ALIASES.get(actual_name, actual_name)


class Kibana(detection.Plugin):
    def _detect(self):
        # check process and port

        process_found = utils.find_process_cmdline('kibana') is not None
        has_deps = self.dependencies_installed()

        has_args = self.args is not None
        cfg_file = self._get_config_file() if has_args else _KIBANA_CFG_FILE
        has_config_file = os.path.isfile(cfg_file)

        available = process_found and has_deps and has_config_file

        self.available = available

        if not self.available:
            err_str = 'Plugin for Kibana will not be configured.'
            if not process_found:
                LOG.error('Kibana process has not been found. %s' % err_str)
            elif not has_deps:
                LOG.error('Kibana plugin dependencies are not satisfied. '
                          'Module "pyaml" not found. %s'
                          % err_str)
            elif not has_config_file:
                LOG.error('Kibana plugin cannot find configuration file %s. %s'
                          % (cfg_file, err_str))

    def build_config(self):
        kibana_config = self._get_config_file()

        try:
            (kibana_host,
             kibana_port,
             kibana_protocol) = self._read_config(kibana_config)
        except Exception as ex:
            LOG.error('Failed to read configuration at %s' % kibana_config)
            LOG.exception(ex)
            return

        if kibana_protocol == 'https':
            LOG.error('"https" protocol is currently not supported')
            return None

        config = agent_config.Plugins()

        # retrieve user name and set in config
        # if passed in args (note args are optional)
        if (self.args and 'kibana-user' in self.args and
                self.args['kibana-user']):
            process = detection.watch_process_by_username(
                username=self.args['kibana-user'],
                process_name='kibana',
                service='monitoring',
                component='kibana'
            )
        else:
            process = detection.watch_process(['kibana'],
                                              service='monitoring',
                                              component='kibana',
                                              process_name='kibana')

        config.merge(process)

        kibana_url = '%s://%s:%d' % (
            kibana_protocol,
            kibana_host,
            kibana_port
        )

        if not self._has_metrics_support(kibana_url):
            LOG.warning('Running kibana does not support metrics, skipping...')
            return None
        else:
            metrics = self._get_all_metrics(kibana_url)
            config['kibana'] = {
                'init_config': {
                    'url': '%s/%s' % (kibana_url, _API_STATUS),
                },
                'instances': [
                    {
                        'metrics': metrics
                    }
                ]
            }

        LOG.info('\tWatching the kibana process.')

        return config

    def dependencies_installed(self):
        try:
            import yaml
        except Exception:
            return False
        return True

    def _get_config_file(self):
        if self.args is not None:
            kibana_config = self.args.get('kibana-config', _KIBANA_CFG_FILE)
        else:
            kibana_config = _KIBANA_CFG_FILE
        return kibana_config

    @staticmethod
    def _read_config(kibana_cfg):
        import yaml
        with open(kibana_cfg, 'r') as stream:
            document = yaml.safe_load(stream=stream)

            has_ssl_support = ('server.ssl.cert' in document and
                               'server.ssl.key' in document)

            host = document.get('server.host')
            port = int(document.get('server.port'))
            protocol = 'https' if has_ssl_support else 'http'

            return host, port, protocol

    def _get_all_metrics(self, kibana_url):
        resp = self._get_metrics_request(kibana_url)
        data = resp.json()

        metrics = []
        # do not check plugins, check will go for overall status

        # get metrics
        for metric in data.get('metrics').keys():
            metrics.append(get_metric_name(metric))

        return metrics

    def _has_metrics_support(self, kibana_url):
        resp = self._get_metrics_request(kibana_url, method='HEAD')
        status_code = resp.status_code
        # although Kibana will respond with 400:Bad Request
        # it means that URL is available but simply does
        # not support HEAD request
        # Looks like guys from Kibana just support GET for this url
        return status_code == 400

    def _get_metrics_request(self, url, method='GET'):
        request_url = '%s/%s' % (url, _API_STATUS)
        return requests.request(method=method, url=request_url)
