"""
PM-017: Budget Dashboard Endpoint
API endpoint for budget status for Genesis.

Acceptance Criteria:
- [x] GIVEN GET /api/budget THEN returns JSON status
- [x] AND shows per-provider breakdown
- [x] AND shows per-model costs

Dependencies: PM-002
"""

import os
import json
import logging
from datetime import datetime
from typing import Optional, Dict, Any
from dataclasses import dataclass, asdict

try:
    from flask import Flask, jsonify, request, Blueprint
    FLASK_AVAILABLE = True
except ImportError:
    FLASK_AVAILABLE = False
    Flask = None
    Blueprint = None

try:
    from fastapi import FastAPI, HTTPException, Query
    from fastapi.responses import JSONResponse
    FASTAPI_AVAILABLE = True
except ImportError:
    FASTAPI_AVAILABLE = False
    FastAPI = None

from core.api_token_manager import get_token_manager, TokenManager
from core.cost_tracker_v2 import get_cost_tracker, CostTracker
from core.budget_alerts import get_budget_alert_system, BudgetAlertSystem
from core.success_analytics import get_success_analytics, SuccessRateAnalytics

logger = logging.getLogger(__name__)


@dataclass
class BudgetStatusResponse:
    """Response structure for budget status."""
    timestamp: str
    providers: Dict[str, Dict[str, Any]]
    totals: Dict[str, float]
    alerts: Dict[str, Any]
    model_costs: Dict[str, float]
    success_rates: Dict[str, float]

    def to_dict(self) -> Dict[str, Any]:
        return asdict(self)


class BudgetDashboardAPI:
    """
    API for budget dashboard endpoints.

    Supports both Flask and FastAPI frameworks.
    Can also be used standalone for data retrieval.
    """

    def __init__(self,
                 token_manager: Optional[TokenManager] = None,
                 cost_tracker: Optional[CostTracker] = None,
                 alert_system: Optional[BudgetAlertSystem] = None,
                 success_analytics: Optional[SuccessRateAnalytics] = None):
        """
        Initialize BudgetDashboardAPI.

        Args:
            token_manager: TokenManager instance
            cost_tracker: CostTracker instance
            alert_system: BudgetAlertSystem instance
            success_analytics: SuccessRateAnalytics instance
        """
        self.token_manager = token_manager or get_token_manager()
        self.cost_tracker = cost_tracker or get_cost_tracker()
        self.alert_system = alert_system or get_budget_alert_system()
        self.success_analytics = success_analytics or get_success_analytics()

    def get_budget_status(self) -> BudgetStatusResponse:
        """
        Get comprehensive budget status.

        Returns:
            BudgetStatusResponse with all budget data
        """
        # Get provider status from token manager
        provider_status = {}
        for provider in ["anthropic", "gemini"]:
            provider_status[provider] = {
                "budget": self.token_manager.budgets.get(provider, 0),
                "spent": self.token_manager.spent.get(provider, 0),
                "remaining": self.token_manager.get_remaining(provider),
                "percentage_remaining": self.token_manager.get_remaining_percentage(provider),
                "can_execute": self.alert_system.can_execute(provider),
                "halted": self.alert_system.is_execution_halted(provider)
            }

        # Calculate totals
        totals = {
            "total_budget": sum(self.token_manager.budgets.values()),
            "total_spent": sum(self.token_manager.spent.values()),
            "total_remaining": sum(
                self.token_manager.get_remaining(p) for p in ["anthropic", "gemini"]
            )
        }

        # Get alerts
        alerts = {
            "active_alerts": len(self.alert_system.get_alerts(unacknowledged_only=True)),
            "thresholds_crossed": {
                provider: list(self.alert_system._crossed_thresholds.get(provider, set()))
                for provider in ["anthropic", "gemini"]
            }
        }

        # Get per-model costs
        model_costs = self.cost_tracker.get_model_spending()

        # Get success rates
        success_rates = self.success_analytics.get_success_rates()

        return BudgetStatusResponse(
            timestamp=datetime.utcnow().isoformat(),
            providers=provider_status,
            totals=totals,
            alerts=alerts,
            model_costs=model_costs,
            success_rates=success_rates
        )

    def get_detailed_breakdown(self) -> Dict[str, Any]:
        """
        Get detailed cost breakdown.

        Returns:
            Detailed breakdown dictionary
        """
        stats = self.cost_tracker.get_statistics()

        return {
            "timestamp": datetime.utcnow().isoformat(),
            "overview": {
                "total_calls": stats.get("total_calls", 0),
                "successful_calls": stats.get("successful_calls", 0),
                "failed_calls": stats.get("failed_calls", 0),
                "total_cost": stats.get("total_cost", 0),
                "total_input_tokens": stats.get("total_input_tokens", 0),
                "total_output_tokens": stats.get("total_output_tokens", 0)
            },
            "by_model": stats.get("spending_by_model", {}),
            "by_provider": stats.get("spending_by_provider", {}),
            "budgets": stats.get("budgets", {})
        }

    def get_alerts_history(self, limit: int = 50) -> Dict[str, Any]:
        """
        Get alert history.

        Args:
            limit: Maximum alerts to return

        Returns:
            Alert history dictionary
        """
        alerts = self.alert_system.get_alerts()[-limit:]

        return {
            "timestamp": datetime.utcnow().isoformat(),
            "total_alerts": len(self.alert_system._alerts),
            "alerts": [a.to_dict() for a in alerts]
        }


# Flask Blueprint (if Flask is available)
def create_flask_blueprint(api: Optional[BudgetDashboardAPI] = None) -> Optional[Any]:
    """Create Flask blueprint for budget endpoints."""
    if not FLASK_AVAILABLE:
        return None

    blueprint = Blueprint('budget', __name__, url_prefix='/api/budget')
    api = api or BudgetDashboardAPI()

    @blueprint.route('', methods=['GET'])
    @blueprint.route('/', methods=['GET'])
    def get_budget():
        """GET /api/budget - Get budget status."""
        try:
            status = api.get_budget_status()
            return jsonify(status.to_dict())
        except Exception as e:
            logger.error(f"Budget endpoint error: {e}")
            return jsonify({"error": str(e)}), 500

    @blueprint.route('/detailed', methods=['GET'])
    def get_detailed():
        """GET /api/budget/detailed - Get detailed breakdown."""
        try:
            return jsonify(api.get_detailed_breakdown())
        except Exception as e:
            return jsonify({"error": str(e)}), 500

    @blueprint.route('/alerts', methods=['GET'])
    def get_alerts():
        """GET /api/budget/alerts - Get alert history."""
        try:
            limit = request.args.get('limit', 50, type=int)
            return jsonify(api.get_alerts_history(limit))
        except Exception as e:
            return jsonify({"error": str(e)}), 500

    @blueprint.route('/provider/<provider>', methods=['GET'])
    def get_provider(provider: str):
        """GET /api/budget/provider/<provider> - Get single provider status."""
        try:
            if provider not in ["anthropic", "gemini"]:
                return jsonify({"error": f"Unknown provider: {provider}"}), 400

            status = api.get_budget_status()
            return jsonify({
                "timestamp": status.timestamp,
                "provider": provider,
                **status.providers[provider]
            })
        except Exception as e:
            return jsonify({"error": str(e)}), 500

    return blueprint


# FastAPI Router (if FastAPI is available)
def create_fastapi_router(api: Optional[BudgetDashboardAPI] = None):
    """Create FastAPI router for budget endpoints."""
    if not FASTAPI_AVAILABLE:
        return None

    from fastapi import APIRouter

    router = APIRouter(prefix="/api/budget", tags=["budget"])
    api = api or BudgetDashboardAPI()

    @router.get("")
    @router.get("/")
    async def get_budget():
        """GET /api/budget - Get budget status."""
        try:
            status = api.get_budget_status()
            return status.to_dict()
        except Exception as e:
            raise HTTPException(status_code=500, detail=str(e))

    @router.get("/detailed")
    async def get_detailed():
        """GET /api/budget/detailed - Get detailed breakdown."""
        try:
            return api.get_detailed_breakdown()
        except Exception as e:
            raise HTTPException(status_code=500, detail=str(e))

    @router.get("/alerts")
    async def get_alerts(limit: int = Query(50, ge=1, le=500)):
        """GET /api/budget/alerts - Get alert history."""
        try:
            return api.get_alerts_history(limit)
        except Exception as e:
            raise HTTPException(status_code=500, detail=str(e))

    @router.get("/provider/{provider}")
    async def get_provider(provider: str):
        """GET /api/budget/provider/{provider} - Get single provider status."""
        if provider not in ["anthropic", "gemini"]:
            raise HTTPException(status_code=400, detail=f"Unknown provider: {provider}")

        try:
            status = api.get_budget_status()
            return {
                "timestamp": status.timestamp,
                "provider": provider,
                **status.providers[provider]
            }
        except Exception as e:
            raise HTTPException(status_code=500, detail=str(e))

    return router


# Standalone JSON response (no web framework needed)
def get_budget_json() -> str:
    """
    Get budget status as JSON string.

    Use this for CLI or non-web contexts.

    Returns:
        JSON string with budget status
    """
    api = BudgetDashboardAPI()
    status = api.get_budget_status()
    return json.dumps(status.to_dict(), indent=2)


# Simple HTTP handler for basic web server
class BudgetHTTPHandler:
    """Simple HTTP handler for budget endpoint."""

    def __init__(self):
        self.api = BudgetDashboardAPI()

    def handle_request(self, path: str, params: Dict[str, str] = None) -> Dict[str, Any]:
        """
        Handle HTTP-like request.

        Args:
            path: Request path (e.g., "/api/budget")
            params: Query parameters

        Returns:
            Response dictionary
        """
        params = params or {}

        if path in ["/api/budget", "/api/budget/"]:
            return self.api.get_budget_status().to_dict()

        elif path == "/api/budget/detailed":
            return self.api.get_detailed_breakdown()

        elif path == "/api/budget/alerts":
            limit = int(params.get("limit", 50))
            return self.api.get_alerts_history(limit)

        elif path.startswith("/api/budget/provider/"):
            provider = path.split("/")[-1]
            if provider not in ["anthropic", "gemini"]:
                return {"error": f"Unknown provider: {provider}"}
            status = self.api.get_budget_status()
            return {
                "timestamp": status.timestamp,
                "provider": provider,
                **status.providers[provider]
            }

        else:
            return {"error": f"Unknown path: {path}"}


if __name__ == "__main__":
    # Test the BudgetDashboardAPI
    logging.basicConfig(level=logging.INFO)

    api = BudgetDashboardAPI()

    print("Budget Status (JSON):")
    print(get_budget_json())

    print("\n\nDetailed Breakdown:")
    print(json.dumps(api.get_detailed_breakdown(), indent=2))

    print("\n\nAlerts History:")
    print(json.dumps(api.get_alerts_history(10), indent=2))

    # Test HTTP handler
    print("\n\nHTTP Handler Test:")
    handler = BudgetHTTPHandler()
    print(json.dumps(handler.handle_request("/api/budget"), indent=2))
