"""Runtime configuration for real-time revenue forecasting."""

from __future__ import annotations

import os
from dataclasses import dataclass
from datetime import time
from decimal import Decimal


def _env_flag(name: str, default: bool) -> bool:
    raw = os.getenv(name)
    if raw is None:
        return default
    return raw.strip().lower() in {"1", "true", "yes", "on"}


def _parse_time(value: str) -> time:
    try:
        hour, minute = (int(part) for part in value.split(":"))
        return time(hour=hour, minute=minute)
    except (TypeError, ValueError) as exc:
        raise ValueError(f"Invalid HH:MM time: {value!r}") from exc


@dataclass(frozen=True)
class RevenueForecastConfig:
    enabled: bool = False
    poll_seconds: int = 900
    start_time: time = time(6, 15)
    stop_time: time = time(22, 30)
    trend_window_minutes: int = 15
    trend_window_count: int = 2
    target_cost_ratio: Decimal = Decimal("3.5")
    max_lag_minutes: int = 20
    forecast_version: str = "revenue_forecast_v6_weighted_30m_speed"
    speed_parameter_version: str = "revenue_speed_params_v1"
    speed_latest_weight: Decimal = Decimal("0.66666667")
    speed_minimum_samples: int = 3
    lock_name: str = "revenue_forecast"

    @classmethod
    def from_env(cls) -> "RevenueForecastConfig":
        config = cls(
            enabled=_env_flag("REVENUE_FORECAST_ENABLED", False),
            poll_seconds=int(os.getenv("REVENUE_FORECAST_POLL_SECONDS", "900")),
            start_time=_parse_time(
                os.getenv("REVENUE_FORECAST_START_TIME", "06:15")
            ),
            stop_time=_parse_time(
                os.getenv("REVENUE_FORECAST_STOP_TIME", "22:30")
            ),
            trend_window_minutes=int(
                os.getenv("REVENUE_FORECAST_TREND_WINDOW_MINUTES", "15")
            ),
            trend_window_count=int(
                os.getenv("REVENUE_FORECAST_TREND_WINDOW_COUNT", "2")
            ),
            target_cost_ratio=Decimal(
                os.getenv("REVENUE_FORECAST_TARGET_RATIO", "3.5")
            ),
            max_lag_minutes=int(
                os.getenv("REVENUE_FORECAST_MAX_LAG_MINUTES", "20")
            ),
            forecast_version=os.getenv(
                "REVENUE_FORECAST_VERSION",
                "revenue_forecast_v6_weighted_30m_speed",
            ).strip(),
            speed_parameter_version=os.getenv(
                "REVENUE_SPEED_PARAMETER_VERSION",
                "revenue_speed_params_v1",
            ).strip(),
            speed_latest_weight=Decimal(
                os.getenv("REVENUE_SPEED_LATEST_WEIGHT", "0.66666667")
            ),
            speed_minimum_samples=int(
                os.getenv("REVENUE_SPEED_MINIMUM_SAMPLES", "3")
            ),
            lock_name=os.getenv(
                "REVENUE_FORECAST_DB_LOCK_NAME", "revenue_forecast"
            ).strip(),
        )
        if config.start_time >= config.stop_time:
            raise ValueError("Revenue forecast start time must be before stop time")
        if config.poll_seconds < 60:
            raise ValueError("REVENUE_FORECAST_POLL_SECONDS must be at least 60")
        if config.trend_window_minutes < 5:
            raise ValueError("Trend window must be at least 5 minutes")
        if config.trend_window_count < 1:
            raise ValueError("Trend window count must be positive")
        if config.target_cost_ratio <= 0:
            raise ValueError("REVENUE_FORECAST_TARGET_RATIO must be positive")
        if config.max_lag_minutes < 5:
            raise ValueError("Maximum source lag must be at least 5 minutes")
        if config.trend_window_minutes != 15:
            raise ValueError("Weighted speed forecast requires 15-minute windows")
        if config.trend_window_count < 2:
            raise ValueError("Weighted speed forecast requires at least two windows")
        if "weighted_30m_speed" not in config.forecast_version:
            raise ValueError(
                "Revenue forecast version must identify weighted_30m_speed"
            )
        if not Decimal("0") <= config.speed_latest_weight <= Decimal("1"):
            raise ValueError("REVENUE_SPEED_LATEST_WEIGHT must be between 0 and 1")
        if config.speed_minimum_samples < 1:
            raise ValueError("REVENUE_SPEED_MINIMUM_SAMPLES must be positive")
        if not all(
            (
                config.forecast_version,
                config.speed_parameter_version,
                config.lock_name,
            )
        ):
            raise ValueError("Forecast, parameter version and lock name are required")
        return config
