import datetime
from pathlib import Path
from librec_auto.core.util import xml_load_from_path, extract_from_path, force_list, SubPaths, LogFile

# A .status file looks like this
# <librec-auto-status>
#    <message>Completed</message>
#    <exp-no>1</exp-no>\
#    <param><name>rec.neighbors.knn.number</name><value>30</value></param>
#    <date>June 28, 11:00 PM</date>
# </librec-auto-status>
# TODO: Rewrite with lxml. This is kind of embarrassing.

class Status():

    _HEADER = '<?xml version="1.0"?>\n<!-- DO NOT EDIT. File automatically generated by librec-auto -->\n'
    _TEMPLATE_FRONT = '<librec-auto-status>\n<message>{}</message>\n<exp-no>{}</exp-no>\n<date>{}</date>\n'
    _TEMPLATE_END = '</librec-auto-status>\n'
    _TEMPLATE_LINE = '<param><name>{}</name><value>{}</value></param>\n'

    def __init__(self, sub_paths):
        self._subpaths = sub_paths
        status_path = self._subpaths.get_path('status')

        if status_path.exists():
            self._name = sub_paths.subexp_name
            self._status_xml = xml_load_from_path(status_path)
            self._message = extract_from_path(self._status_xml, ['librec-auto-status', 'message'])
            if self._subpaths.get_path('log').exists():
                self._log = LogFile(self._subpaths)
            else:
                self._log = None
            params = extract_from_path(self._status_xml, ['librec-auto-status', 'param'])
            if params != None:
                self.process_params(force_list(params))
            else:
                self._params = []
                self.m_vals = []

    def __str__(self):
        params_string = self.get_params_string()
        if self._log:
            results_string = self.get_log_info()
        else:
            results_string = "No LibRec results"
        return f'Status({self._name}:{self._message}{params_string} Overall{results_string})'

    def is_completed(self):
        return self._message == 'Completed'

    def process_params(self, param_xml):
        param_list = []
        val_list = []

        for pdict in param_xml:
            param_list.append(pdict['name'])
            val_list.append(pdict['value'])

        self._params = param_list
        self._vals = val_list

    def get_params_string(self):
        params_string = ''
        if self._params == []:
            return " No parameters"
        for param, val in zip(self._params, self.m_vals):
            params_string += f' {param}: {val}'
        return params_string

    def get_log_info(self):
        kcv_count = self._log.get_kcv_count()
        if kcv_count is None:
            return self.get_metric_info(self._log, 0)
        else:
            return self.get_metric_info(self._log, -1)

    def get_metric_info(self, log, idx):
        metric_info = ''
        for metric_name in log.get_metrics():
            metric_value = log.get_metric_values(metric_name)[idx]
            metric_info = metric_info + f' {metric_name}: {float(metric_value):.3f}'
        return metric_info


    # Accept list of vars and tuples
    @staticmethod
    def save_status(msg, exp_count, config, paths):
        status_file = paths.get_path('status')
        status_front = Status._TEMPLATE_FRONT.format(msg, exp_count, datetime.datetime.now())

        status_params = ''
        conf_xml = config.get_files().get_sub_paths(exp_count).get_exp_conf()
        var_elems = conf_xml.xpath("//*[@var='true']")
        for var_elem in var_elems:
            if var_elem.tag == 'param':
                var_name = var_elem.get('name')
            else:
                var_name = var_elem.tag
            var_value = var_elem.text

            status_params = status_params + Status._TEMPLATE_LINE.format(var_name, var_value)

        status_info = Status._HEADER + status_front + status_params + Status._TEMPLATE_END

        with status_file.open(mode='w') as fh:
            fh.write(str(status_info))
