import importlib.resources
import json
from datetime import datetime
from enum import Enum
from os import getcwd, remove, system, walk
from os.path import join
from shutil import rmtree
from typing import Union

import pandas as pd

from backtesterrb30.libs.data_sources.data_source_base import DataSource
from backtesterrb30.libs.interfaces.utils.data_symbol import DataSymbol
from backtesterrb30.libs.utils.timestamps import timestamp_to_datetime

# from backtesterrb30.historical_data_feeds.modules.utils import validate_dataframe_timestamps


class DUKASCOPY_INTERVALS_2(str, Enum):
    tick: str = "tick"
    minute: str = "minute"
    minute5: str = "minute5"
    minute15: str = "minute15"
    minute30: str = "minute30"
    hour: str = "hour"
    hour4: str = "hour4"
    day: str = "day"
    month: str = "month"


class DukascopyDataSource(DataSource):
    INTERVALS = DUKASCOPY_INTERVALS_2
    NAME = "dukascopy"

    def __init__(self, logger=print):
        super().__init__(False, logger)

    def __get_ducascopy_interval(self, interval: str) -> str:
        if interval == "tick":
            return "tick"
        if interval == "minute":
            return "m1"
        if interval == "minute5":
            return "m5"
        if interval == "minute15":
            return "m15"
        if interval == "minute30":
            return "m30"
        if interval == "hour":
            return "h1"
        if interval == "day":
            return "d1"
        if interval == "month":
            return "mn1"

    def _get_interval_miliseconds(self, interval: str) -> Union[int, None]:
        if interval == "tick":
            return None
        if interval == "minute":
            return 60 * 1000
        if interval == "minute5":
            return 5 * 60 * 1000
        if interval == "minute15":
            return 15 * 60 * 1000
        if interval == "minute30":
            return 30 * 60 * 1000
        if interval == "hour":
            return 60 * 60 * 1000
        if interval == "day":
            return 24 * 60 * 60 * 1000
        if interval == "month":
            return None

    async def _validate_instrument_data(self, data: DataSymbol) -> bool:
        # https://raw.githubusercontent.com/Leo4815162342/dukascopy-node/master/src/utils/instrument-meta-data/generated/raw-meta-data-2022-04-23.json
        # response = requests.get("http://api.open-notify.org/astros.json")
        from_datetime_timestamp = int(
            round(datetime.timestamp(data.backtest_date_start) * 1000)
        )
        # f = open('temporary_ducascopy_list.json')
        # instrument_list = load(f)['instruments']
        with importlib.resources.open_text(
            "backtesterrb30", "temporary_ducascopy_list.json"
        ) as file:
            instrument_list = json.load(file)["instruments"]
        # validate if instrument exists:
        if data.symbol.upper() not in [
            v["historical_filename"] for k, v in instrument_list.items()
        ]:
            self._log(
                'Error. Instrument "' + data.symbol + '" does not exists on dukascopy.'
            )
            return False

        # validate it timestamps perios is right:
        for k, v in instrument_list.items():
            if v["historical_filename"] == data.symbol.upper():
                first_timestamp = int(v["history_start_day"])
                if first_timestamp > from_datetime_timestamp:
                    print(
                        "Error. First avaliable date of ",
                        data.symbol,
                        "is",
                        datetime.fromtimestamp(first_timestamp / 1000.0),
                    )
                    return False

        return True

    async def _download_instrument_data(
        self,
        instrument: str,
        interval: str,
        time_start: int,
        time_stop: Union[int, None],
    ) -> pd.DataFrame:
        self._log("Downloading dukascopy data", instrument)
        """
        documentation: 
        https://github.com/Leo4815162342/dukascopy-node
        """

        duca_interval = self.__get_ducascopy_interval(interval)
        time_start_datetime = timestamp_to_datetime(time_start)
        if [
            time_start_datetime.hour,
            time_start_datetime.minute,
            time_start_datetime.second,
            time_start_datetime.microsecond,
        ] != [0, 0, 0, 0]:
            raise Exception(
                "Cannot dowload data with this time_start! Intervals must be in day graduality"
            )
        time_stop_datetime = timestamp_to_datetime(time_stop)
        if [
            time_stop_datetime.hour,
            time_stop_datetime.minute,
            time_stop_datetime.second,
            time_stop_datetime.microsecond,
        ] != [0, 0, 0, 0]:
            raise Exception(
                "Cannot dowload data with this time_stop! Intervals must be in day graduality"
            )
        from_param = datetime.fromtimestamp(time_start // 1000.0).strftime("%Y-%m-%d")
        to_param = datetime.fromtimestamp(time_stop // 1000.0).strftime("%Y-%m-%d")
        if from_param == to_param:
            raise Exception("The same start and stop date")
        here = getcwd()
        cache_path = join(here, "cache_dukascopy")
        try:
            rmtree(cache_path)
        except Exception:
            pass
        string_params = [
            " -i " + instrument,
            " -from " + from_param,
            " -to " + to_param,
            " -s",
            " -t " + duca_interval,
            " -fl",
            " -f csv",
            " -dir " + cache_path,
            " -p bid",
        ]
        command = "npx --yes dukascopy-node"
        for param in string_params:
            command += param
        result = system(command)
        if result != 0:
            self._log("Node is not installed?")
            raise Exception("Command not found or failed to execute: " + command)
        name_of_created_file = next(walk(cache_path), (None, None, []))[2][0]
        df = pd.read_csv(
            join(cache_path, name_of_created_file), index_col=None, header=None
        )
        if duca_interval == "tick":
            df = df.iloc[1:, [0, 2]]
        else:
            df = df.iloc[1:, [0, 1]]
        if name_of_created_file and name_of_created_file != "":
            remove(join(cache_path, name_of_created_file))
        # print('Dukascopy df shape after dl', df.shape)
        # print('head', str(df.head(1)))
        # print('tail', str(df.tail(1)))
        return df
