from datetime import UTC, datetime, timedelta

import pandas as pd
from easyprov import csv_dump, csv_header

from .fetch import fetch
from ..era5 import closest_ref
from ..provider_base import ProviderBase


class Provider(ProviderBase):

    def __init__(self):
        super().__init__()
        self.storage = self.storage_root() / "weather_archive_daily"

    def header(self):
        """Header of data stored

        Returns:
            (dict): [unit] description per columns
        """
        pth_dir = list(self.storage.glob("*/"))[0]
        pth = list(pth_dir.glob("*.csv"))[0]
        return csv_header(pth)

    def pth_storage(self, latitude, longitude, year):
        """Normalized name associated to given coordinates

        Args:
            latitude (float): [deg] latitude of place
            longitude (float): [deg] longitude of place
            year (int): year of time series

        Returns:
            (str)
        """
        return self.storage / f"{year:d}/arch_{int((latitude + 90) * 100):05d}_{int((longitude + 180) * 100):05d}.csv"

    def _ensure(self, missing, ref_lat, ref_long, date_beg, date_end, pth, df_prev=None):
        if missing == "fetch":
            df_ext, header = fetch(ref_lat, ref_long, date_beg, date_end)
            if df_prev is None:
                df = df_ext
            else:
                df = pd.concat([df_prev, df_ext])

            # write
            pth.parent.mkdir(exist_ok=True, parents=True)
            csv_dump(df, header, pth, __file__, float_format="%.2f")

            return df, header
        else:
            raise KeyError("File not available in local storage, need to fetch")

    def get(self, latitude, longitude, date_beg, date_end, closest=True, missing="raise"):
        """Get time series for given location from local storage.

        Raises: if missing="raise", KeyError if either pos or some dates have not been fetch
                previously on server

        Args:
            latitude (float): [deg] latitude of place
            longitude (float): [deg] longitude of place
            date_beg (str|datetime): First date, included
            date_end (str|datetime): End date, included
            closest (bool): Whether to interpolate from nearby locations or fetch
                            only nearest point in model
            missing (str): whether to fetch missing entry from remote server or raise error

        Returns:
            (pd.DataFrame): name of columns varies
        """
        if isinstance(date_beg, str):
            date_beg = datetime.fromisoformat(date_beg)

        if isinstance(date_end, str):
            date_end = datetime.fromisoformat(date_end)

        now = datetime.now(UTC)
        today = datetime(now.year, now.month, now.day) - timedelta(days=1)
        assert date_beg <= today
        assert date_end <= today

        assert closest

        ref_lat, ref_long = closest_ref(latitude, longitude)
        dfs = []
        header = None
        for year in range(date_beg.year, date_end.year + 1):
            pth = self.pth_storage(ref_lat, ref_long, year)
            year_end = datetime(year, 12, 31)
            if pth.exists():
                df = pd.read_csv(pth, sep=";", comment="#", parse_dates=["date"], index_col=["date"])
                if ((year < date_end.year and df.index[-1] < year_end)
                        or (year == date_end.year and df.index[-1] < date_end)):
                    # some dates were not fetched previously
                    df, header = self._ensure(missing,
                                              ref_lat,
                                              ref_long,
                                              df.index[-1] + timedelta(days=1),
                                              min(today, year_end),
                                              pth,
                                              df)
                else:
                    header = csv_header(pth)
            else:
                df, header = self._ensure(missing,
                                          ref_lat,
                                          ref_long,
                                          datetime(year, 1, 1),
                                          min(today, year_end),
                                          pth,
                                          None)

            dfs.append(df)

        df = pd.concat(dfs)

        return df.loc[date_beg: date_end], header
