"""
Usage Forecasting API

This endpoint predicts when a tenant will hit their subscription limits
based on historical usage growth rates.
"""

from datetime import datetime, timedelta
from typing import List, Optional
from fastapi import APIRouter, Depends, HTTPException, Query
from pydantic import BaseModel
import redis.asyncio as redis

from shared.auth.decorators import get_current_user_dep
from app.core.config import settings
from app.core.database import database_manager
from app.services.subscription_limit_service import SubscriptionLimitService

router = APIRouter(prefix="/usage-forecast", tags=["usage-forecast"])


class UsageDataPoint(BaseModel):
    """Single usage data point"""
    date: str
    value: int


class MetricForecast(BaseModel):
    """Forecast for a single metric"""
    metric: str
    label: str
    current: int
    limit: Optional[int]  # None = unlimited
    unit: str
    daily_growth_rate: Optional[float]  # None if not enough data
    predicted_breach_date: Optional[str]  # ISO date string, None if unlimited or no breach
    days_until_breach: Optional[int]  # None if unlimited or no breach
    usage_percentage: float
    status: str  # 'safe', 'warning', 'critical', 'unlimited'
    trend_data: List[UsageDataPoint]  # 30-day history


class UsageForecastResponse(BaseModel):
    """Complete usage forecast response"""
    tenant_id: str
    forecasted_at: str
    metrics: List[MetricForecast]


async def get_redis_client() -> redis.Redis:
    """Get Redis client for usage tracking"""
    from shared.redis_utils import RedisConnectionManager

    # Create Redis connection using shared manager (handles SSL, pooling, health checks)
    manager = await RedisConnectionManager.create(
        settings.REDIS_URL, decode_responses=True
    )
    return manager.get_client()


async def get_usage_history(
    redis_client: redis.Redis,
    tenant_id: str,
    metric: str,
    days: int = 30
) -> List[UsageDataPoint]:
    """
    Get historical usage data for a metric from Redis

    Usage data is stored with keys like:
    usage:daily:{tenant_id}:{metric}:{date}
    """
    history = []
    today = datetime.utcnow().date()

    for i in range(days):
        date = today - timedelta(days=i)
        date_str = date.isoformat()
        key = f"usage:daily:{tenant_id}:{metric}:{date_str}"

        try:
            value = await redis_client.get(key)
            if value is not None:
                history.append(UsageDataPoint(
                    date=date_str,
                    value=int(value)
                ))
        except Exception as e:
            print(f"Error fetching usage for {key}: {e}")
            continue

    # Return in chronological order (oldest first)
    return list(reversed(history))


def calculate_growth_rate(history: List[UsageDataPoint]) -> Optional[float]:
    """
    Calculate daily growth rate using linear regression

    Returns average daily increase, or None if insufficient data
    """
    if len(history) < 7:  # Need at least 7 days of data
        return None

    # Simple linear regression
    n = len(history)
    sum_x = sum(range(n))
    sum_y = sum(point.value for point in history)
    sum_xy = sum(i * point.value for i, point in enumerate(history))
    sum_x_squared = sum(i * i for i in range(n))

    # Calculate slope (daily growth rate)
    denominator = (n * sum_x_squared) - (sum_x ** 2)
    if denominator == 0:
        return None

    slope = ((n * sum_xy) - (sum_x * sum_y)) / denominator

    return max(slope, 0)  # Can't have negative growth for breach prediction


def predict_breach_date(
    current: int,
    limit: int,
    daily_growth_rate: float
) -> Optional[tuple[str, int]]:
    """
    Predict when usage will breach the limit

    Returns (breach_date_iso, days_until_breach) or None if no breach predicted
    """
    if daily_growth_rate <= 0:
        return None

    remaining_capacity = limit - current
    if remaining_capacity <= 0:
        # Already at or over limit
        return datetime.utcnow().date().isoformat(), 0

    days_until_breach = int(remaining_capacity / daily_growth_rate)

    if days_until_breach > 365:  # Don't predict beyond 1 year
        return None

    breach_date = datetime.utcnow().date() + timedelta(days=days_until_breach)

    return breach_date.isoformat(), days_until_breach


def determine_status(usage_percentage: float, days_until_breach: Optional[int]) -> str:
    """Determine metric status based on usage and time to breach"""
    if usage_percentage >= 100:
        return 'critical'
    elif usage_percentage >= 90:
        return 'critical'
    elif usage_percentage >= 80 or (days_until_breach is not None and days_until_breach <= 14):
        return 'warning'
    else:
        return 'safe'


@router.get("/", response_model=UsageForecastResponse)
async def get_usage_forecast(
    tenant_id: str = Query(..., description="Tenant ID"),
    current_user: dict = Depends(get_current_user_dep)
) -> UsageForecastResponse:
    """
    Get usage forecasts for all metrics

    Predicts when the tenant will hit their subscription limits based on
    historical usage growth rates from the past 30 days.

    Returns predictions for:
    - Users
    - Locations
    - Products
    - Recipes
    - Suppliers
    - Training jobs (daily)
    - Forecasts (daily)
    - API calls (hourly average converted to daily)
    - File storage
    """
    # Initialize services
    redis_client = await get_redis_client()
    limit_service = SubscriptionLimitService(database_manager=database_manager)

    try:
        # Get current usage summary (includes limits)
        usage_summary = await limit_service.get_usage_summary(tenant_id)

        if not usage_summary or 'error' in usage_summary:
            raise HTTPException(
                status_code=404,
                detail=f"No active subscription found for tenant {tenant_id}"
            )

        # Extract usage data
        usage = usage_summary.get('usage', {})

        # Define metrics to forecast
        metric_configs = [
            {
                'key': 'users',
                'label': 'Users',
                'current': usage.get('users', {}).get('current', 0),
                'limit': usage.get('users', {}).get('limit'),
                'unit': ''
            },
            {
                'key': 'locations',
                'label': 'Locations',
                'current': usage.get('locations', {}).get('current', 0),
                'limit': usage.get('locations', {}).get('limit'),
                'unit': ''
            },
            {
                'key': 'products',
                'label': 'Products',
                'current': usage.get('products', {}).get('current', 0),
                'limit': usage.get('products', {}).get('limit'),
                'unit': ''
            },
            {
                'key': 'recipes',
                'label': 'Recipes',
                'current': usage.get('recipes', {}).get('current', 0),
                'limit': usage.get('recipes', {}).get('limit'),
                'unit': ''
            },
            {
                'key': 'suppliers',
                'label': 'Suppliers',
                'current': usage.get('suppliers', {}).get('current', 0),
                'limit': usage.get('suppliers', {}).get('limit'),
                'unit': ''
            },
            {
                'key': 'training_jobs',
                'label': 'Training Jobs',
                'current': usage.get('training_jobs_today', {}).get('current', 0),
                'limit': usage.get('training_jobs_today', {}).get('limit'),
                'unit': '/day'
            },
            {
                'key': 'forecasts',
                'label': 'Forecasts',
                'current': usage.get('forecasts_today', {}).get('current', 0),
                'limit': usage.get('forecasts_today', {}).get('limit'),
                'unit': '/day'
            },
            {
                'key': 'api_calls',
                'label': 'API Calls',
                'current': usage.get('api_calls_this_hour', {}).get('current', 0),
                'limit': usage.get('api_calls_this_hour', {}).get('limit'),
                'unit': '/hour'
            },
            {
                'key': 'storage',
                'label': 'File Storage',
                'current': int(usage.get('file_storage_used_gb', {}).get('current', 0)),
                'limit': usage.get('file_storage_used_gb', {}).get('limit'),
                'unit': ' GB'
            }
        ]

        forecasts: List[MetricForecast] = []

        for config in metric_configs:
            metric_key = config['key']
            current = config['current']
            limit = config['limit']

            # Get usage history
            history = await get_usage_history(redis_client, tenant_id, metric_key, days=30)

            # Calculate usage percentage
            if limit is None or limit == -1:
                usage_percentage = 0.0
                status = 'unlimited'
                growth_rate = None
                breach_date = None
                days_until = None
            else:
                usage_percentage = (current / limit * 100) if limit > 0 else 0

                # Calculate growth rate
                growth_rate = calculate_growth_rate(history) if history else None

                # Predict breach
                if growth_rate is not None and growth_rate > 0:
                    breach_result = predict_breach_date(current, limit, growth_rate)
                    if breach_result:
                        breach_date, days_until = breach_result
                    else:
                        breach_date, days_until = None, None
                else:
                    breach_date, days_until = None, None

                # Determine status
                status = determine_status(usage_percentage, days_until)

            forecasts.append(MetricForecast(
                metric=metric_key,
                label=config['label'],
                current=current,
                limit=limit,
                unit=config['unit'],
                daily_growth_rate=growth_rate,
                predicted_breach_date=breach_date,
                days_until_breach=days_until,
                usage_percentage=round(usage_percentage, 1),
                status=status,
                trend_data=history[-30:]  # Last 30 days
            ))

        return UsageForecastResponse(
            tenant_id=tenant_id,
            forecasted_at=datetime.utcnow().isoformat(),
            metrics=forecasts
        )

    finally:
        await redis_client.close()


@router.post("/track-usage")
async def track_daily_usage(
    tenant_id: str,
    metric: str,
    value: int,
    current_user: dict = Depends(get_current_user_dep)
):
    """
    Manually track daily usage for a metric

    This endpoint is called by services to record daily usage snapshots.
    The data is stored in Redis with a 60-day TTL.
    """
    redis_client = await get_redis_client()

    try:
        date_str = datetime.utcnow().date().isoformat()
        key = f"usage:daily:{tenant_id}:{metric}:{date_str}"

        # Store usage with 60-day TTL
        await redis_client.setex(key, 60 * 24 * 60 * 60, str(value))

        return {
            "success": True,
            "tenant_id": tenant_id,
            "metric": metric,
            "value": value,
            "date": date_str
        }

    finally:
        await redis_client.close()
