import ast
from datetime import datetime

from .core import _dt2micro, _get, _micro2dt, _dt2str, build_refdes
from .parameters import get_parameter_info


def reformat_annotation(anno: dict) -> dict:
    """
    Reformat an annotation so that it is not confusing.

    :param anno: A dictionary for a single annotation.
    :return: The reformatted annotation.
    """
    del anno['@class']
    anno['begin_datetime'] = _dt2str(_micro2dt(anno.pop('beginDT')))
    anno['end_datetime'] = _dt2str(_micro2dt(anno.pop('endDT')))
    anno['qc_flag'] = anno.pop('qcFlag')
    anno['exclusion_flag']= anno.pop('exclusionFlag')
    anno['annotation_id'] = anno.pop('id')
    anno['instrument'] = anno.pop('sensor')
    anno['parameter_ids'] = anno.pop('parameters')

    # Get parameter names. Reset lists as strings for better sorting later.
    anno['parameter_names'] = get_param_names(anno['parameter_ids'])
    anno['parameter_ids'] = str(anno['parameter_ids'])

    return anno


def sort_annotation(anno: dict) -> dict:
    """
    Sort a single annotation in an order that makes sense.

    :param anno: A dictionary containing a single annotation.
    :return: The sorted annotation.
    """
    order = ['annotation_id',
             'subsite','node','instrument','method','stream',
             'begin_datetime','end_datetime',
             'annotation',
             'parameter_ids','parameter_names',
             'qc_flag','exclusion_flag']
    anno = {key: anno[key] for key in order if key in anno.keys()}
    return anno


def get_param_names(parameters: list[int]) -> str:
    """
    Get parameter names by making secondary requests to the preload API url.

    :param parameters:
        A list of parameter identifiers.
    :return:
        A stringified list of parameter names.
    """
    if len(parameters) == 0:
        pstr = str(parameters)
    elif len(parameters) == 1:
        param_id = parameters[0]
        param_info = get_parameter_info(param_id)
        if param_info['netcdf_name'] != '':
            pname = param_info['netcdf_name']
        else:
            pname = param_info['name']
        pstr = str([pname])
    else:
        param_infos = [get_parameter_info(param_id) for param_id in parameters]
        names = []
        for param_info in param_infos:
            if param_info['netcdf_name'] != '':
                name = param_info['netcdf_name']
            else:
                name = param_info['name']
            names.append(name)
        pstr = str(names)
    return pstr


def get_annotations(subsite: str,
                    node: str,
                    instrument: str,
                    begin_datetime: datetime = datetime(2000, 1, 1, 0, 0, 0, 0),
                    end_datetime: datetime = datetime(2099, 12, 31, 23, 59, 59, 999999)) -> list[dict]:
    """
    Get annotations for a specific subsite, node, and instrument combo. Requires that the inputs be exact.
    :param subsite: An 8-character OOI subsite/platform designator.
    :param node:
    :param instrument:
    :param begin_datetime:
    :param end_datetime:
    :return:
    """

    anno_url = 'https://ooinet.oceanobservatories.org/api/m2m/12580/anno/find'
    params = {'beginDT': _dt2micro(begin_datetime),
              'endDT': _dt2micro(end_datetime),
              'refdes': build_refdes(subsite, node, instrument)}
    json_data = _get(anno_url, params)

    annos = [reformat_annotation(anno) for anno in json_data]
    annos = [dict(s) for s in set(frozenset(d.items()) for d in annos)]

    annos = [{**item, 'parameter_ids': ast.literal_eval(item['parameter_ids'])} for item in annos]
    annos = [{**item, 'parameter_names': ast.literal_eval(item['parameter_names'])} for item in annos]

    annos = sorted(annos, key=lambda x: x['begin_datetime']) # Sort by time.
    annos = [sort_annotation(anno) for anno in annos]
    return annos


