from datetime import datetime, timedelta
from typing import Dict, Any, List
from uuid import UUID
from fastapi import APIRouter, Depends, Query
from sqlalchemy import text
from sqlalchemy.ext.asyncio import AsyncSession

from ..auth import get_current_account
from ..db import get_session
from ..billing.tracker import SimpleUsageTracker
from ..models import Account

router = APIRouter(prefix="/api/billing/dashboard", tags=["billing-dashboard"])


@router.get("/overview")
async def get_dashboard_overview(
    db: AsyncSession = Depends(get_session),
    account_id: UUID = Depends(get_current_account),
) -> Dict[str, Any]:
    """Get complete billing dashboard data"""

    tracker = SimpleUsageTracker(db)

    # Get account
    account = await db.get(Account, account_id)

    # Current month usage
    current_usage = await tracker.get_current_usage(account_id)

    # Calculate burn rate (last 7 days average)
    seven_days_ago = datetime.utcnow() - timedelta(days=7)
    burn_rate_query = text("""
        SELECT AVG(daily_credits) as avg_burn_rate
        FROM (
            SELECT DATE(time) as day, SUM(credits_consumed) as daily_credits
            FROM usage_events
            WHERE account_id = :account_id
                AND time > :start_date
                AND credits_consumed > 0
            GROUP BY DATE(time)
        ) daily_usage
    """)

    burn_result = await db.execute(
        burn_rate_query, {"account_id": account_id, "start_date": seven_days_ago}
    )
    burn_rate = burn_result.scalar() or 0

    # Days until depletion
    days_remaining = float("inf")
    if burn_rate > 0:
        days_remaining = account.credits_balance / burn_rate

    # Get alerts
    alerts = []
    if account.credits_balance < 100:
        alerts.append(
            {
                "type": "low_balance",
                "message": f"Low credit balance: {account.credits_balance} credits remaining",
                "severity": "critical" if account.credits_balance < 20 else "warning",
            }
        )

    if burn_rate > current_usage / datetime.now().day * 2:
        alerts.append(
            {
                "type": "high_usage",
                "message": f"Usage is higher than normal: {int(burn_rate)} credits/day",
                "severity": "info",
            }
        )

    return {
        "overview": {
            "current_balance": account.credits_balance,
            "month_to_date_usage": current_usage,
            "daily_burn_rate": round(burn_rate, 2),
            "days_until_depletion": round(days_remaining, 1)
            if days_remaining != float("inf")
            else None,
            "projected_monthly_usage": int(burn_rate * 30),
            "subscription_status": account.subscription_status
            if hasattr(account, "subscription_status")
            else "active",
        },
        "alerts": alerts,
        "quick_stats": {
            "total_api_calls_today": await _get_todays_api_calls(db, account_id),
            "active_days_this_month": await _get_active_days(db, account_id),
            "most_used_feature": await _get_most_used_feature(db, account_id),
        },
    }


@router.get("/usage-chart")
async def get_usage_chart_data(
    period: str = Query("30d", pattern="^(7d|30d|90d|1y)$"),
    grouping: str = Query("daily", pattern="^(hourly|daily|weekly|monthly)$"),
    db: AsyncSession = Depends(get_session),
    account_id: UUID = Depends(get_current_account),
) -> Dict[str, Any]:
    """Get usage data for charts"""

    # Determine date range
    end_date = datetime.utcnow()
    if period == "7d":
        start_date = end_date - timedelta(days=7)
    elif period == "30d":
        start_date = end_date - timedelta(days=30)
    elif period == "90d":
        start_date = end_date - timedelta(days=90)
    else:  # 1y
        start_date = end_date - timedelta(days=365)

    # Determine grouping
    if grouping == "hourly":
        date_trunc = "hour"
        format = "%Y-%m-%d %H:00"
    elif grouping == "daily":
        date_trunc = "day"
        format = "%Y-%m-%d"
    elif grouping == "weekly":
        date_trunc = "week"
        format = "Week of %Y-%m-%d"
    else:  # monthly
        date_trunc = "month"
        format = "%Y-%m"

    query = text("""
        SELECT
            DATE_TRUNC(:date_trunc, time) as period,
            SUM(credits_consumed) as credits,
            COUNT(*) as event_count
        FROM usage_events
        WHERE account_id = :account_id
            AND time >= :start_date
            AND time <= :end_date
            AND credits_consumed > 0
        GROUP BY DATE_TRUNC(:date_trunc, time)
        ORDER BY period
    """)

    result = await db.execute(
        query,
        {
            "date_trunc": date_trunc,
            "account_id": account_id,
            "start_date": start_date,
            "end_date": end_date,
        },
    )

    # Format data for charts
    chart_data = []
    for row in result:
        chart_data.append(
            {
                "date": row.period.strftime(format),
                "timestamp": row.period.isoformat(),
                "credits": row.credits,
                "events": row.event_count,
                "cost": float(row.credits * 0.01),  # $0.01 per credit
            }
        )

    # Calculate comparison with previous period
    prev_end = start_date
    prev_start = start_date - (end_date - start_date)

    prev_query = text("""
        SELECT SUM(credits_consumed) as total
        FROM usage_events
        WHERE account_id = :account_id
            AND time >= :start_date
            AND time < :end_date
            AND credits_consumed > 0
    """)

    prev_result = await db.execute(
        prev_query,
        {"account_id": account_id, "start_date": prev_start, "end_date": prev_end},
    )
    prev_total = prev_result.scalar() or 0

    current_total = sum(point["credits"] for point in chart_data)

    return {
        "period": period,
        "grouping": grouping,
        "data": chart_data,
        "summary": {
            "total_credits": current_total,
            "total_cost": float(current_total * 0.01),
            "average_daily": current_total / max(1, len(chart_data)),
            "change_from_previous": {
                "credits": current_total - prev_total,
                "percentage": ((current_total - prev_total) / max(1, prev_total)) * 100
                if prev_total > 0
                else 0,
            },
        },
    }


@router.get("/balance-history")
async def get_balance_history(
    days: int = Query(30, ge=1, le=365),
    db: AsyncSession = Depends(get_session),
    account_id: UUID = Depends(get_current_account),
) -> Dict[str, Any]:
    """Get credit balance history"""

    # This requires tracking balance changes
    # For now, we'll calculate based on usage events and purchases

    query = text("""
        WITH balance_changes AS (
            SELECT
                time,
                -credits_consumed as change
            FROM usage_events
            WHERE account_id = :account_id
                AND time > :start_date

            UNION ALL

            SELECT
                time,
                credits_consumed as change
            FROM usage_events
            WHERE account_id = :account_id
                AND time > :start_date
                AND event_type = 'credit_purchase'
        ),
        cumulative_balance AS (
            SELECT
                DATE(time) as date,
                SUM(SUM(change)) OVER (ORDER BY DATE(time)) as balance
            FROM balance_changes
            GROUP BY DATE(time)
        )
        SELECT * FROM cumulative_balance
        ORDER BY date
    """)

    start_date = datetime.utcnow() - timedelta(days=days)
    result = await db.execute(
        query, {"account_id": account_id, "start_date": start_date}
    )

    # Get current balance and work backwards
    account = await db.get(Account, account_id)
    current_balance = account.credits_balance

    history = []
    for row in result:
        history.append(
            {
                "date": row.date.isoformat(),
                "balance": current_balance + row.balance,
                "change": row.balance,
            }
        )

    return {
        "period_days": days,
        "current_balance": current_balance,
        "history": history,
        "statistics": {
            "lowest_balance": min(h["balance"] for h in history)
            if history
            else current_balance,
            "highest_balance": max(h["balance"] for h in history)
            if history
            else current_balance,
            "average_balance": sum(h["balance"] for h in history) / len(history)
            if history
            else current_balance,
        },
    }


@router.get("/top-endpoints")
async def get_top_endpoints(
    limit: int = Query(10, ge=1, le=50),
    days: int = Query(30, ge=1, le=365),
    db: AsyncSession = Depends(get_session),
    account_id: UUID = Depends(get_current_account),
) -> Dict[str, Any]:
    """Get top API endpoints by usage"""

    query = text("""
        SELECT
            metadata->>'endpoint' as endpoint,
            COUNT(*) as call_count,
            SUM(credits_consumed) as total_credits,
            AVG(credits_consumed) as avg_credits_per_call,
            MAX(time) as last_used
        FROM usage_events
        WHERE account_id = :account_id
            AND time > :start_date
            AND metadata->>'endpoint' IS NOT NULL
            AND credits_consumed > 0
        GROUP BY metadata->>'endpoint'
        ORDER BY total_credits DESC
        LIMIT :limit
    """)

    start_date = datetime.utcnow() - timedelta(days=days)
    result = await db.execute(
        query, {"account_id": account_id, "start_date": start_date, "limit": limit}
    )

    endpoints = []
    total_credits = 0

    for row in result:
        endpoints.append(
            {
                "endpoint": row.endpoint,
                "calls": row.call_count,
                "total_credits": row.total_credits,
                "avg_credits": round(row.avg_credits_per_call, 2),
                "last_used": row.last_used.isoformat(),
                "cost": float(row.total_credits * 0.01),
            }
        )
        total_credits += row.total_credits

    # Calculate percentage for each endpoint
    for endpoint in endpoints:
        endpoint["percentage"] = (
            round((endpoint["total_credits"] / total_credits) * 100, 2)
            if total_credits > 0
            else 0
        )

    return {
        "period_days": days,
        "endpoints": endpoints,
        "summary": {
            "total_endpoints": len(endpoints),
            "total_credits_used": total_credits,
            "most_expensive": endpoints[0] if endpoints else None,
            "optimization_suggestions": _get_optimization_suggestions(endpoints),
        },
    }


# Helper functions
async def _get_todays_api_calls(db: AsyncSession, account_id: UUID) -> int:
    """Get today's API call count"""
    query = text("""
        SELECT COUNT(*)
        FROM usage_events
        WHERE account_id = :account_id
            AND DATE(time) = CURRENT_DATE
            AND event_type LIKE '%api%'
    """)
    result = await db.execute(query, {"account_id": account_id})
    return result.scalar() or 0


async def _get_active_days(db: AsyncSession, account_id: UUID) -> int:
    """Get number of active days this month"""
    query = text("""
        SELECT COUNT(DISTINCT DATE(time))
        FROM usage_events
        WHERE account_id = :account_id
            AND DATE_TRUNC('month', time) = DATE_TRUNC('month', CURRENT_DATE)
            AND credits_consumed > 0
    """)
    result = await db.execute(query, {"account_id": account_id})
    return result.scalar() or 0


async def _get_most_used_feature(db: AsyncSession, account_id: UUID) -> str:
    """Get most used feature this month"""
    query = text("""
        SELECT event_type, COUNT(*) as count
        FROM usage_events
        WHERE account_id = :account_id
            AND DATE_TRUNC('month', time) = DATE_TRUNC('month', CURRENT_DATE)
            AND credits_consumed > 0
        GROUP BY event_type
        ORDER BY count DESC
        LIMIT 1
    """)
    result = await db.execute(query, {"account_id": account_id})
    row = result.first()
    return row.event_type.replace("_", " ").title() if row else "None"


def _get_optimization_suggestions(endpoints: List[dict]) -> List[str]:
    """Generate optimization suggestions based on usage patterns"""
    suggestions = []

    if endpoints:
        # Check for expensive endpoints
        if endpoints[0]["avg_credits"] > 10:
            suggestions.append(
                f"The {endpoints[0]['endpoint']} endpoint uses {endpoints[0]['avg_credits']} credits per call. "
                "Consider batching requests to reduce costs."
            )

        # Check for high-frequency endpoints
        high_freq = [e for e in endpoints if e["calls"] > 1000]
        if high_freq:
            suggestions.append(
                f"You have {len(high_freq)} endpoints with over 1000 calls. "
                "Consider caching results to reduce API calls."
            )

    if not suggestions:
        suggestions.append("Your usage patterns look optimal!")

    return suggestions
