from os.path import isfile
import pandas as pd
import re
from pathlib import Path
import random
from typing import Tuple, Dict
import yaml
import json
from datetime import datetime


def get_logger_config() -> str:
    return '[%(asctime)s][%(levelname)-5.5s][%(name)-.20s] %(message)s'


def get_current_datetime() -> str:
    """
    Formats current datetime into a string.

    Returns
    ----------
    str
        Created datetime.
    """
    return datetime.now().strftime('%Y-%m-%d--%H-%M-%S')


def load_model_config(model_config_path, n_levels):

    def cut_correct_n_levels(model_config):
        for k in list(model_config.keys()):
            if k != 'model' and int(k.split('level-')[1]) >= n_levels:
                del model_config[k]
        return model_config

    model_config = load_yaml(model_config_path)
    model_config = cut_correct_n_levels(model_config)
    return model_config


def create_dir(directory: str) -> None:
    """
    Creates a directory if it does not exist.

    Parameters
    ----------
    directory : str
        Path to the directory.
    """
    Path(directory).mkdir(parents=True, exist_ok=True)


def write_to_file(filename: str, row: str) -> None:
    """
    Writes a single row to a file.
    Expects that the directory with the file exists.

    Parameters
    ----------
    filename : str
        Path to the file to write to.
    row: str
        The string to write.
    """
    writing_mode = 'a'
    if not file_exists(filename):
        writing_mode = 'w'
    with open(filename, writing_mode) as f:
        f.write(row + '\n')


def crop_job_number(pbs_job_id):
    """
    Crops the metacentrum's job number from the whole job ID.

    Parameters
    ----------
    pbs_job_id : str
        Job ID

    Returns
    ----------
    pbs_job_id : str
        Numeric portion of the job ID.
    """
    match = re.findall(r'[0-9]*', pbs_job_id)
    if match[0] != '':
        pbs_job_id = match[0]
    return pbs_job_id


def load_yaml(path):
    with open(path, 'r') as stream:
        loaded = yaml.safe_load(stream)
    return loaded


def load_json(path):
    assert isfile(path), f'{path} does not exist.'
    with open(path, 'r') as stream:
        loaded = json.load(stream)
    return loaded

def remove_key(d, key):
    r = dict(d)
    del r[key]
    return r


def save_yaml(file: Dict, target_path: str) -> None:
    """
    Saves the config file.

    Parameters
    ----------
    config : Dict
        Loaded yaml config file.
    target_path: str
        File location for config to be saved as.

    """
    with open(target_path, 'w+') as outfile:
        yaml.dump(file, outfile, default_flow_style=False)


def file_exists(filename: str) -> bool:
    """
    Checks if a file exists.

    Parameters
    ----------
    filename : str
        Path to the file.

    """
    return Path.is_file(Path(filename))


def sample(df: pd.DataFrame, labels: pd.DataFrame, n: int) -> Tuple[pd.DataFrame, pd.DataFrame]:
    # Ensures that the same indexes (i.e., object_id) are kept
    """
    Samples a dataset subset of size `n` from the original datasets.
    Ensures that the same indexes (i.e., object_id) are kept.

    Parameters
    ----------
    df : pd.DataFrame
        Dataset with descriptors.
    labels : pd.DataFrame
        Dataset with labels.
    n : int
        Number of rows to select.
    """
    assert n > 0 and n < df.shape[0], '`n` needs to be > 0 and < size of the datasets.'
    rows = random.sample(df.index.to_list(), n)
    df_sampled = df.loc[rows, ]
    if labels is not None:
        labels = labels.reindex(index=df.index)
        labels_sampled = labels.loc[rows, ]
        return df_sampled, labels_sampled
    else:
        return df_sampled
