import errno
import json
import os
from pathlib import Path
from shutil import copyfile
from time import localtime, strftime
from typing import Any, List

from bcipy.acquisition.devices import DeviceSpec
from bcipy.config import (DEFAULT_ENCODING, DEFAULT_EXPERIMENT_ID,
                          DEFAULT_LM_PARAMETERS_FILENAME,
                          DEFAULT_LM_PARAMETERS_PATH,
                          DEFAULT_PARAMETER_FILENAME)
from bcipy.helpers.validate import validate_experiments


def save_json_data(data: Any, location: str, name: str) -> str:
    """
    Writes Parameters as a json file.

        data: any data that can be dumped as json
        location[str]: directory in which to save
        name[str]: optional name of file; default is parameters.json

    Returns path of saved file
    """
    path = Path(location, name)
    with open(Path(location, name), 'w', encoding=DEFAULT_ENCODING) as json_file:
        json.dump(data, json_file, ensure_ascii=False, indent=2)
    return str(path)


def save_experiment_data(experiments, fields, location, name) -> str:
    validate_experiments(experiments, fields)
    return save_json_data(experiments, location, name)


def save_field_data(fields, location, name) -> str:
    return save_json_data(fields, location, name)


def save_experiment_field_data(data, location, name) -> str:
    return save_json_data(data, location, name)


def init_save_data_structure(data_save_path: str,
                             user_id: str,
                             parameters: str,
                             task: str,
                             experiment_id: str = DEFAULT_EXPERIMENT_ID
                             ) -> str:
    """
    Initialize Save Data Structure.

        data_save_path[str]: string of path to save our data in
        user_id[str]: string of user name / related information
        parameters[str]: parameter location for the experiment
        experiment_id[str]: Name of the experiment. Default name is DEFAULT_EXPERIMENT_ID.
        task[str]: name of task type. Ex. RSVP Calibration

    """

    # make an experiment folder : note datetime is in utc
    save_folder_name = f'{data_save_path}{experiment_id}/{user_id}'
    dt = strftime('%a_%d_%b_%Y_%Hhr%Mmin%Ssec_%z', localtime())
    task = task.replace(' ', '_')
    save_directory = f"{save_folder_name}/{user_id}_{task}_{dt}"

    try:
        # make a directory to save data to
        os.makedirs(save_folder_name)
        os.makedirs(save_directory)
        os.makedirs(os.path.join(save_directory, 'logs'), exist_ok=True)

    except OSError as error:
        # If the error is anything other than file existing, raise an error
        if error.errno != errno.EEXIST:
            raise error

        # since this is only called on init, we can make another folder run
        os.makedirs(save_directory)
        os.makedirs(os.path.join(save_directory, 'logs'), exist_ok=True)

    copyfile(parameters, Path(save_directory, DEFAULT_PARAMETER_FILENAME))

    copyfile(DEFAULT_LM_PARAMETERS_PATH, Path(save_directory, DEFAULT_LM_PARAMETERS_FILENAME))

    return save_directory


def save_device_specs(device_specs: List[DeviceSpec], location: str,
                      name: str) -> str:
    """
    Save device spec to a json file.

    Parameters:
        device_spec (DeviceSpec): device spec to save
        location (str): location to save the file
        name (str): name of the file to be saved

    Returns:
        str: path to the saved file
    """
    return save_json_data([spec.to_dict() for spec in device_specs], location,
                          name)


def _save_session_related_data(file, session_dictionary):
    """
    Save Session Related Data.
    Parameters
    ----------
        file[str]: string of path to save our data in
        session_dictionary[dict]: dictionary of session data. It will appear
            as follows:
                {{ "series": {
                        "1": {
                          "0": {
                            "target_text": "COPY_PHRASE",
                            "current_text": "COPY_",
                            "eeg_len": 22,
                            "next_display_state": "COPY_",
                            "stimuli": [["+", "_", "G", "L", "B"]],
                            "target_info": [
                              "nontarget", ... ,
                            ],
                            "timing_sti": [[1, 0.2, 0.2, 0.2, 0.2]],
                            "triggers": [[ "+", 0.0], ["_", 0.9922] ..],
                            },
                        ... ,
                        "7": {
                            ... ,
                  },
                  "mode": "RSVP",
                  "session": "data/demo_user/demo_user",
                  "task": "Copy Phrase",
                  "total_time_spent": 83.24798703193665
                }}
    Returns
    -------
        file, session data file (json file)
    """
    # Try opening as json, if not able to use open() to create first
    try:
        file = json.load(file, 'wt')
    except BaseException:
        file = open(file, 'wt', encoding=DEFAULT_ENCODING)

    # Use the file to dump data to
    json.dump(session_dictionary, file, indent=2)
    return file
