# -*- coding: utf-8 -*-

import json
import logging
import os
import time

import requests

from .exceptions import GrafanaError
from .settings import Settings

logger = logging.getLogger('grafana')


class GrafanaRestClient(object):

    @staticmethod
    def url_validation(method, path):
        response = requests.request(
            method,
            path,
            verify=Settings.GRAFANA_API_SSL_VERIFY)
        return response.status_code

    @staticmethod
    def push_dashboard(dashboard_obj):
        if not Settings.GRAFANA_API_URL:
            raise GrafanaError("The Grafana API URL is not set")
        if not Settings.GRAFANA_API_URL.startswith('http'):
            raise GrafanaError("The Grafana API URL is invalid")
        if not Settings.GRAFANA_API_USERNAME:
            raise GrafanaError("The Grafana API username is not set")
        if not Settings.GRAFANA_API_PASSWORD:
            raise GrafanaError("The Grafana API password is not set")
        url = Settings.GRAFANA_API_URL.rstrip('/') + \
            '/api/dashboards/db'
        headers = {
            'Accept': 'application/json',
            'Content-Type': 'application/json',
        }
        payload = {
            'dashboard': dashboard_obj,
            'overwrite': True,
        }
        try:
            response = requests.post(
                url,
                headers=headers,
                data=json.dumps(payload),
                auth=(Settings.GRAFANA_API_USERNAME,
                      Settings.GRAFANA_API_PASSWORD),
                verify=Settings.GRAFANA_API_SSL_VERIFY
            )
        except requests.ConnectionError:
            raise GrafanaError("Could not connect to Grafana server")
        response.raise_for_status()
        return response.status_code, response.json()


class Retrier(object):
    def __init__(self, tries, sleep, func, *args, **kwargs):
        """
        Wraps a function. An instance of this class may be called to call that
        function, retrying if it raises an exception. Sleeps between retries,
        eventually reraising the original exception when retries are exhausted.
        Once the function returns a value, that value is returned.

        :param tries: How many times to try, before reraising the exception
        :type tries: int
        :param sleep: How many seconds to wait between tries
        :type sleep: int|float
        :param func: The function to execute
        :type func: function
        :param args: Any arguments to pass to the function
        :type args: list
        :param kwargs: Any keyword arguments to pass to the function
        :type kwargs: dict
        """
        assert tries >= 1
        self.tries = int(tries)
        self.tried = 0
        self.sleep = sleep
        self.func = func
        self.args = args
        self.kwargs = kwargs

    def __call__(self):
        result = None
        while self.tried < self.tries:
            try:
                result = self.func(*self.args, **self.kwargs)
            except Exception:  # pylint: disable=broad-except
                if self.tried == self.tries - 1:
                    raise
                else:
                    self.tried += 1
                    time.sleep(self.sleep)
            else:
                return result


def load_local_dashboards():
    if os.environ.get('CEPH_DEV') == '1' or 'UNITTEST' in os.environ:
        path = os.path.abspath(os.path.join(
            os.path.dirname(__file__),
            '../../../../monitoring/ceph-mixin/dashboards_out/'
        ))
    else:
        path = '/etc/grafana/dashboards/ceph-dashboard'
    dashboards = dict()
    for item in [p for p in os.listdir(path) if p.endswith('.json')]:
        db_path = os.path.join(path, item)
        with open(db_path) as f:
            dashboards[item] = json.loads(f.read())
    return dashboards


def push_local_dashboards(tries=1, sleep=0):
    try:
        dashboards = load_local_dashboards()
    except (EnvironmentError, ValueError):
        logger.exception("Failed to load local dashboard files")
        raise

    def push():
        try:
            grafana = GrafanaRestClient()
            for body in dashboards.values():
                grafana.push_dashboard(body)
        except Exception:
            logger.exception("Failed to push dashboards to Grafana")
            raise
    retry = Retrier(tries, sleep, push)
    retry()
    return True
