import numpy as np
import pandas as pd
from procesing.pricers.base import PricingFunction


class StaticPricer(PricingFunction):
    """Static pricing: always return fixed base prices"""

    def __init__(self, base_prices: np.ndarray = None):
        self.base_prices = base_prices

    def fit(self, historical_data: pd.DataFrame):
        """Extract base prices from historical data"""
        if 'base_price' in historical_data.columns:
            self.base_prices = historical_data['base_price'].values
        elif 'price' in historical_data.columns:
            self.base_prices = historical_data['price'].values
        else:
            raise ValueError("historical_data must contain 'base_price' or 'price' column")
        return self

    def predict(self, state_space) -> np.ndarray:
        """Return static base prices regardless of state"""
        if self.base_prices is None:
            raise ValueError("Must call fit() or provide base_prices in constructor")
        return self.base_prices.copy()


class RandomPricer(PricingFunction):
    """Random pricing within bounds (for baseline comparison)"""

    def __init__(self, price_min: float = 50.0, price_max: float = 500.0, seed: int = None):
        self.price_min = price_min
        self.price_max = price_max
        self.seed = seed
        self.n_products = None
        self.rng = np.random.default_rng(seed)

    def fit(self, historical_data: pd.DataFrame):
        """Learn number of products"""
        self.n_products = len(historical_data)
        return self

    def predict(self, state_space) -> np.ndarray:
        """Generate random prices"""
        if self.n_products is None:
            self.n_products = len(state_space.demand)
        return self.rng.uniform(self.price_min, self.price_max, size=self.n_products)


class SimpleSurgePricer(PricingFunction):
    """
    Rule-based surge pricer adjusting prices via demand thresholds.
    Logic: if demand > high_threshold -> surge, if demand < low_threshold -> discount.
    Simpler and more controllable than curve fitting approaches.
    """

    def __init__(self,
                 base_prices: np.ndarray = None,
                 high_threshold: int = 10,
                 low_threshold: int = 2,
                 surge_multiplier: float = 1.2,
                 discount_multiplier: float = 0.9):
        self.base_prices = base_prices
        self.high_threshold = high_threshold
        self.low_threshold = low_threshold
        self.surge_multiplier = surge_multiplier
        self.discount_multiplier = discount_multiplier

    def fit(self, market_data : pd.DataFrame):
        """Extract base prices from product catalog or historical averages"""
        self.base_prices = market_data['base_price'].to_numpy() if 'base_price' in market_data.columns else market_data['price'].values
        self.demand_history = market_data['demand'].to_numpy() if 'demand' in market_data.columns else np.zeros_like(self.base_prices)

    def predict(self) -> np.ndarray:
        """
        Adjust prices based on current demand using surge rules.
        state_space.demand: demand counts per product
        state_space.prices: current prices (fallback if base_prices not set)
        """
        current_prices = self.base_prices if self.base_prices is not None else np.ones_like(demand_vector) * 99.99
        demand = self.demand_history if self.demand_history is not None else np.zeros_like(current_prices)
        new_prices = current_prices.copy()

        high_mask = demand >= self.high_threshold
        new_prices[high_mask] *= self.surge_multiplier

        low_mask = demand <= self.low_threshold
        new_prices[low_mask] *= self.discount_multiplier

        return new_prices
