import concurrent
import csv
import os
import re
from collections import defaultdict
from concurrent.futures import ThreadPoolExecutor, as_completed
from datetime import date, timedelta
from glob import glob
from threading import Thread, Lock

from loguru import logger
from besser_tanken.config import stations_dir, prices_dir
from besser_tanken.data_tools import fix_date_time_strings

files_re = re.compile(r"(\d\d\d\d)-(\d\d)-(\d\d)-(\S*).csv")

prices_cache = dict()
stations_cache = dict()


def load_date_hierarchical_file(base_dir, year=None, month=None,
                                day=None) -> str:
    year_dirs = glob(f"{base_dir}/*/")
    years = []
    for y_d in year_dirs:
        y = y_d[:-1].rsplit('/', maxsplit=1)[1]
        years.append(int(y))
    if year is None:
        year = sorted(years)[-1]
    if int(year) not in years:
        raise ValueError(f"No file for year {year}!")
    year_dir = f"{base_dir}/{year}"

    month_dirs = glob(f"{year_dir}/*/")
    months = []
    for m_d in month_dirs:
        m = m_d[:-1].rsplit('/', maxsplit=1)[1]
        months.append(int(m))
    if month is None:
        month = sorted(months)[-1]
    if int(month) not in months:
        raise ValueError(f"No file found for month {month}!")
    month_dir = f"{year_dir}/{month:02d}"

    day_files = glob(f"{month_dir}/*.csv")
    days = []
    file_type = None
    for d in day_files:
        match = files_re.search(d)
        if match:
            days.append(int(match.group(3)))
            file_type = match.group(4)
    if day is None:
        day = sorted(days)[-1]
    if int(day) not in days or file_type is None:
        raise ValueError(f"No file found for day {day}!")

    return os.path.join(month_dir,
                        f"{year}-{month:02d}-{day:02d}-{file_type}.csv")


def load_prices(year=None, month=None, day=None) -> list:
    file = load_date_hierarchical_file(prices_dir, year, month, day)
    if file in prices_cache:
        logger.info(f"returning prices from cache for file: {file}")
        return prices_cache[file]
    logger.info(f"loading prices file: {file}")
    with open(file, newline='') as csv_file:
        res = list(csv.DictReader(csv_file))
        logger.info(f"got {len(res)} prices")
        res = fix_date_time_strings(res)
        prices_cache[file] = res
        return res


def load_prices_from(year=None, month=None, day=None, threading=True) -> list:
    today = date.today()
    start_date = date(year, month, day)
    res = []

    def _threaded_loading(y: int, m: int, d: int):
        try:
            return load_prices(y, m, d)
        except ValueError:
            return None  # ignore ValueError, just do nothing

    if threading:
        with ThreadPoolExecutor(max_workers=16) as executor:
            futures = []
            while start_date < today:
                # Thread(target=_threaded_loading, args=(start_date.year,
                #                                       start_date.month,
                #                                       start_date.day,)).start()
                futures.append(
                    executor.submit(_threaded_loading, y=start_date.year,
                                    m=start_date.month, d=start_date.day))
                start_date = start_date + timedelta(days=1)

            for future in as_completed(futures):
                try:
                    r = future.result()
                    if r is not None:
                        res.extend(r)
                except TimeoutError:
                    print("timeout occured")
    else:
        while start_date < today:
            try:
                res.extend(load_prices(start_date.year,
                                       start_date.month,
                                       start_date.day))

                start_date = start_date + timedelta(days=1)
            except ValueError:
                return res
    return res


def load_stations(year=None, month=None, day=None) -> list:
    file = load_date_hierarchical_file(stations_dir, year, month, day)
    if file in stations_cache:
        logger.info(f"returning stations from cache for file: {file}")
        return stations_cache[file]
    with open(file, newline='') as csv_file:
        res = list(csv.DictReader(csv_file))
        logger.info(f"got {len(res)} stations")
        stations_cache[file] = res
        return res

    # stations_dir
