import logging
from datetime import datetime, timedelta, timezone
from typing import Any, Optional

import pandas as pd

from .service import Service

logger = logging.getLogger(__name__)

MEMPOOL_MODULE = None
try:
    from pymempool.api import MempoolAPI

    MEMPOOL_MODULE = "pymempool"
except ImportError:
    pass


class Mempool(Service):
    def __init__(
        self,
        fiat,
        interval="1h",
        days_ago=1,
        enable_ohlc=False,
        enable_timeseries=False,
        enable_ohlcv=False,
    ):
        self.api_client: Optional[Any] = MempoolAPI() if MEMPOOL_MODULE else None
        self.initialize(
            fiat,
            interval=interval,
            days_ago=days_ago,
            enable_ohlc=enable_ohlc,
            enable_timeseries=enable_timeseries,
            enable_ohlcv=enable_ohlcv,
        )
        self.name = "mempool"

    def get_current_price(self, currency="USD") -> Optional[float]:
        """Fetch the current price from Mempool."""
        if not self.api_client:
            return None
        try:
            ticker = self.api_client.get_price()
            return float(ticker[currency.upper()])
        except Exception as e:
            logger.exception(f"Failed to fetch current price: {e}")
            return None

    def interval_to_seconds(self) -> int:
        """Convert a time interval string to seconds."""
        unit_multipliers = {"m": 60, "h": 3600, "d": 86400}

        try:
            value, unit = int(self.interval[:-1]), self.interval[-1]
            if unit in unit_multipliers:
                return value * unit_multipliers[unit]
            else:
                raise ValueError(f"Invalid interval format {self.interval}")
        except (ValueError, IndexError) as e:
            raise ValueError(f"Invalid interval format {self.interval}") from e

    def calculate_start_date(self):
        now = datetime.utcnow()
        return now - timedelta(days=self.days_ago)

    def calculate_time_vector(self, existing_timestamp=None):
        """Generate timestamps based on the interval."""
        now = datetime.utcnow()
        intervals = self.interval_to_seconds()

        if not existing_timestamp:
            start_time = self.calculate_start_date()
        else:
            start_time = datetime.utcfromtimestamp(
                existing_timestamp[-1] + 2 * intervals
            )
        time_vector = list(
            range(
                int(start_time.timestamp()),
                int(now.timestamp()),
                intervals,
            )
        )

        return time_vector

    def get_history_price(
        self, currency, existing_timestamp=None
    ) -> list[tuple[datetime, float]]:
        history_prices: list[tuple[datetime, float]] = []
        if self.api_client is None:
            return history_prices
        time_vector = self.calculate_time_vector(existing_timestamp=existing_timestamp)
        for timestamp in time_vector:
            price = self.api_client.get_historical_price(
                currency=currency.upper(), timestamp=timestamp
            )
            price_value = float(price["prices"][0][currency.upper()])
            history_prices.append(
                (datetime.fromtimestamp(timestamp, tz=timezone.utc), price_value)
            )
        return history_prices

    def update_price_history(self, currency):
        """Fetch historical prices from Mempool."""
        logger.info(f"Getting historical data for a {self.interval} interval")
        existing_timestamp = self.price_history.get_timestamp_list()
        history_prices = self.get_history_price(
            currency, existing_timestamp=existing_timestamp
        )
        for price_time, price_value in history_prices:
            self.price_history.add_price(price_time, price_value)

    def get_ohlcv(self, currency, existing_timestamp=None):
        df = self.price_history.data.copy()
        df["timestamp"] = pd.to_datetime(df["timestamp"])
        df.set_index("timestamp", inplace=True)

        ohlcv_df = df["price"].resample("1h").ohlcv()
        if existing_timestamp:
            cutoff = datetime.fromtimestamp(existing_timestamp[-1], tz=timezone.utc)
            ohlcv_df = ohlcv_df[ohlcv_df.index > cutoff]
        return ohlcv_df

    def get_ohlc(self, currency, existing_timestamp=None) -> pd.DataFrame:
        """Fetch OHLC data based on the number of days ago."""
        ohlcv_df = self.get_ohlcv(currency, existing_timestamp)
        ohlc_df = ohlcv_df.drop(columns=["Volume"])
        return ohlc_df
