"""
    Machine Learning Experiment Hooks.
"""
from typing import Dict, Union
from pathlib import Path
import json
import requests
import logging

ENDPOINT = 'https://ragnarok.zumok8s.org/api/v1/experiment/'
experiment = None
logger = None

def init(
    name: str,
    sim: str = None,
    dataset: str = None,
    config: Dict = None,
    api_key: str = None,
) -> None:
    """ Initialize a experiment run.

    Args:
        name (str): identifier for experiment
        sim (str. optional): identifier for simulation associated with experiment
        dataset (str, optional): identifier for dataset associated with experiment
        config (dict, optional): configuration details about experiment
        api_key (str, required): api_key to auth with backend
    Returns:
        experiment object
    """
    if api_key is None:
        raise PermissionError('please input zpy api_key')
    global logger
    logger = logging.getLogger(__name__)
    exp = Experiment(name=name, sim=sim, dataset=dataset, config=config, api_key=api_key)
    global experiment
    experiment = exp
    exp._create()


def log(
    metrics: str = None,
    file_path: str = None,
) -> None:
    """ Log an update to experiment.

    Args:
        metrics (str, optional): free form dictionary of data to log
        file_path (str, optional): file path to upload

    Raises:
        FileNotFoundError: if file_path doesnt exist
    """
    global experiment
    exp = experiment
    if file_path:
        file_path = Path(file_path).resolve()
    exp._update(file_path=file_path, metrics=metrics)


class Experiment:
    """ experiment class for uploading """

    def __init__(
            self,
            name: str = None,
            sim: str = None,
            dataset: str = None,
            config: Dict = None,
            api_key: str = None
        ) -> None:
        """ Experiment Class for ragnarok upload.

        Args:
            name (str): identifier for experiment
            sim (str. optional): identifier for simulation associated with experiment
            dataset (str, optional): identifier for dataset associated with experiment
            config (dict, optional): configuration details about experiment
            api_key (str, required): api_key to auth with backend
        """
        self.name = name
        self.sim = sim
        self.dataset = dataset
        self.config = config
        self.auth_headers = {'Authorization': 'token {}'.format(api_key)}
        self.id = None

    def _post(self, data=None):
        """ post to endpoint """
        r = requests.post(ENDPOINT, data=data, headers=self.auth_headers)
        if r.status_code != 201:
            logger.debug(f'{r.text}')
            r.raise_for_status()
        self.id = json.loads(r.text)['id']
        logger.debug(f'{r.status_code}: {r.text}')

    def _put(self, data=None, files=None):
        """ put to endpoint """
        r = requests.put(f'{ENDPOINT}{self.id}/', data=data, files=files, headers=self.auth_headers)
        if r.status_code != 200:
            logger.debug(f'{r.text}')
            r.raise_for_status()
        logger.debug(f'{r.status_code}: {r.text}')

    def _create(self):
        """ request to create experiment """
        data = {'name': self.name}
        if self.sim:
            data['sim_name'] = self.sim
        if self.dataset:
            data['data_set_name'] = self.dataset
        if self.config:
            data['config'] = json.dumps(self.config)
        self._post(data=data)

    def _update(
            self,
            file_path: Union[Path, str] = None,
            metrics: Dict = None
        ) -> None:
        """ request to update experiment """
        data = {'name': self.name}
        if metrics:
            data['metrics'] = json.dumps(metrics)
        files = None
        if file_path:
            files = {'file': open(file_path, 'rb')}
            data['file_name'] = Path(file_path).name
        self._put(data=data, files=files)
