from tqdm.auto import tqdm
import requests
import humanize
import os
from typing import List, Dict
import pandas as pd

__all__ = ["download", "sample_to_dataframe"]


def download(url: str, path: str = None, block_size: int = 32768):
    """Download file at given url showing a loading bar.

    Parameters
    ----------------------
    url:str,
        The url from where to download the data.
    path:str=None,
        The path where to store the data, if None the end of the url is used.
    block_size:int=1024,
        The download block size.

    Raises
    ----------------------
    ValueError, 
        if the downloaded file size does not match the header file size.
    """
    if path is None:
        path = url.split("/")[-1]
    r = requests.get(url, stream=True)
    total_size = int(r.headers.get('content-length', 0))
    t = tqdm(
        total=total_size,
        unit='iB',
        unit_scale=True,
        desc="Downloading to {path}".format(path=path),
        dynamic_ncols=True,
        leave=False
    )
    directory = os.path.dirname(os.path.abspath(path))
    if directory:
        os.makedirs(directory, exist_ok=True)
    with open(path, 'wb') as f:
        for data in r.iter_content(block_size):
            t.update(len(data))
            f.write(data)
    t.close()


def sample_informations(sample: Dict) -> Dict:
    """Return generic informations from the sample.
        sample:Dict, the sample from which to extract the informations.
    """
    return {
        "organism": sample["target"]["organism"]["name"],
        "cell_line": sample["biosample_ontology"]["term_name"],
        "target": sample["target"]["label"]
    }


def sample_files_informations(sample: Dict) -> List[Dict]:
    """Return list of informations for every files in the sample.
        sample:Dict, the sample from which to extract the files informations.
    """
    return [
        {
            "status": f["status"] if "status" in f else None,
            "accession":f["accession"] if "accession" in f else None,
            "file_size":f["file_size"] if "file_size" in f else None,
            "file_format":f["file_format"] if "file_format" in f else None,
            "assembly":f["assembly"] if "assembly" in f else None,
            "biological_replicates":sorted(f["biological_replicates"]),
            "output_type":f["output_type"] if "output_type" in f else None,
            "url":f["cloud_metadata"]["url"] if "cloud_metadata" in f else None,
        } for f in sample["files"]
    ]


def sample_to_dataframe(sample: Dict) -> pd.DataFrame:
    """Return simple dataframe representation for given sample.
        sample:Dict, the sample to convert into a simple DataFrame.
    """
    sample = normalize_sample(sample)
    df = pd.DataFrame(sample_files_informations(sample))
    for key, value in sample_informations(sample).items():
        df[key] = value
    return df


def normalize_sample(sample: Dict) -> Dict:
    if "files" not in sample:
        return {
            "files": [sample.copy()]
        }
    return sample
