"""
Granular budget management tools for Meta Ads campaigns and ad sets.

This module provides fine-grained budget control with validation,
scheduling, and monitoring capabilities.
"""

from datetime import datetime, timedelta
from typing import Any, Dict, Optional

from .meta_client import MetaClient
from .errors import MetaApiError
from .logging import get_logger

logger = get_logger(__name__)


class BudgetManager:
    """Manages budget operations for campaigns and ad sets."""

    def __init__(self, client: MetaClient):
        self._client = client

    async def get_current_budget(
        self,
        entity_id: str,
        entity_type: str,
    ) -> Dict[str, Any]:
        """
        Get current budget and spend status for a campaign or ad set.
        """
        try:
            # Get entity details
            response = await self._client.get(
                entity_id,
                params={"fields": "daily_budget,lifetime_budget,status,spend"}
            )
            entity_data = response.get("data", {})

            # Get recent spend data
            spend_response = await self._client.get(
                f"{entity_id}/insights",
                params={
                    "fields": "spend,impressions,clicks",
                    "time_range": {"since": (datetime.now() - timedelta(days=7)).strftime("%Y-%m-%d"), "until": datetime.now().strftime("%Y-%m-%d")},
                }
            )
            spend_data = spend_response.get("data", [])

            # Calculate spend metrics
            total_spend = sum(float(insight.get("spend", 0)) for insight in spend_data)
            avg_daily_spend = total_spend / 7 if spend_data else 0

            daily_budget = float(entity_data.get("daily_budget", 0))
            lifetime_budget = float(entity_data.get("lifetime_budget", 0))
            status = entity_data.get("status", "unknown")

            # Calculate budget utilization
            daily_utilization = (avg_daily_spend / daily_budget * 100) if daily_budget > 0 else 0
            lifetime_utilization = (total_spend / lifetime_budget * 100) if lifetime_budget > 0 else 0

            return {
                "entity_id": entity_id,
                "entity_type": entity_type,
                "current_budgets": {
                    "daily_budget": daily_budget,
                    "lifetime_budget": lifetime_budget,
                },
                "spend_metrics": {
                    "total_spend_7d": total_spend,
                    "avg_daily_spend": avg_daily_spend,
                    "daily_utilization_pct": daily_utilization,
                    "lifetime_utilization_pct": lifetime_utilization,
                },
                "status": status,
                "budget_health": {
                    "daily_status": "over_budget" if daily_utilization > 100 else "under_budget" if daily_utilization < 80 else "on_track",
                    "lifetime_status": "over_budget" if lifetime_utilization > 100 else "under_budget" if lifetime_utilization < 80 else "on_track",
                },
                "next_recommended_actions": [
                    "Monitor daily spend vs. budget",
                    "Adjust budget if utilization is consistently high/low",
                    "Review lifetime budget if approaching limit",
                ],
            }
        except MetaApiError as exc:
            logger.error("Failed to get current budget", entity_id=entity_id, error=exc.to_dict())
            raise

    async def get_budget_recommendations(
        self,
        entity_id: str,
        entity_type: str,
        include_forecast: bool = True,
    ) -> Dict[str, Any]:
        """
        Get Meta's budget recommendations based on performance.
        """
        try:
            # Get recent performance data
            performance_response = await self._client.get(
                f"{entity_id}/insights",
                params={
                    "fields": "spend,impressions,clicks,ctr,cpc,conversions",
                    "time_range": {"since": (datetime.now() - timedelta(days=14)).strftime("%Y-%m-%d"), "until": datetime.now().strftime("%Y-%m-%d")},
                }
            )
            performance_data = performance_response.get("data", [])

            # Get current budget
            budget_data = await self.get_current_budget(entity_id, entity_type)

            # Calculate performance metrics
            total_spend = sum(float(insight.get("spend", 0)) for insight in performance_data)
            total_impressions = sum(int(insight.get("impressions", 0)) for insight in performance_data)
            total_clicks = sum(int(insight.get("clicks", 0)) for insight in performance_data)
            total_conversions = sum(
                sum(action.get("value", 0) for action in insight.get("actions", [])
                    if action.get("action_type") == "offsite_conversion.fb_pixel_purchase")
                for insight in performance_data
            )

            avg_ctr = total_clicks / total_impressions if total_impressions > 0 else 0
            avg_cpc = total_spend / total_clicks if total_clicks > 0 else 0
            cost_per_conversion = total_spend / total_conversions if total_conversions > 0 else 0

            current_daily_budget = budget_data["current_budgets"]["daily_budget"]
            budget_data["spend_metrics"]["avg_daily_spend"]

            # Generate recommendations
            recommendations = []

            # Budget utilization recommendation
            if budget_data["spend_metrics"]["daily_utilization_pct"] > 95:
                recommendations.append({
                    "type": "increase_budget",
                    "reason": "High budget utilization (95%+)",
                    "suggested_daily_budget": current_daily_budget * 1.2,
                    "confidence": "high",
                })
            elif budget_data["spend_metrics"]["daily_utilization_pct"] < 50:
                recommendations.append({
                    "type": "decrease_budget",
                    "reason": "Low budget utilization (<50%)",
                    "suggested_daily_budget": current_daily_budget * 0.8,
                    "confidence": "medium",
                })

            # Performance-based recommendations
            if avg_ctr > 0.02 and cost_per_conversion < 50:  # Good performance
                recommendations.append({
                    "type": "scale_up",
                    "reason": "Strong performance metrics",
                    "suggested_daily_budget": current_daily_budget * 1.5,
                    "confidence": "high",
                })
            elif avg_ctr < 0.005 or cost_per_conversion > 100:  # Poor performance
                recommendations.append({
                    "type": "optimize_before_scaling",
                    "reason": "Poor performance metrics",
                    "suggested_daily_budget": current_daily_budget * 0.7,
                    "confidence": "high",
                })

            result = {
                "entity_id": entity_id,
                "entity_type": entity_type,
                "current_performance": {
                    "avg_ctr": avg_ctr,
                    "avg_cpc": avg_cpc,
                    "cost_per_conversion": cost_per_conversion,
                    "total_conversions": total_conversions,
                },
                "recommendations": recommendations,
                "summary": {
                    "primary_recommendation": recommendations[0] if recommendations else None,
                    "confidence_level": "high" if recommendations and recommendations[0]["confidence"] == "high" else "medium",
                },
            }

            # Add forecast if requested
            if include_forecast and recommendations:
                primary_rec = recommendations[0]
                if primary_rec["type"] in ["increase_budget", "scale_up"]:
                    result["forecast"] = {
                        "projected_daily_spend": primary_rec["suggested_daily_budget"],
                        "projected_weekly_spend": primary_rec["suggested_daily_budget"] * 7,
                        "expected_impressions": int(total_impressions * (primary_rec["suggested_daily_budget"] / current_daily_budget)),
                        "expected_conversions": int(total_conversions * (primary_rec["suggested_daily_budget"] / current_daily_budget)),
                    }

            result["next_recommended_actions"] = [
                "Review and implement budget recommendations",
                "Monitor performance after budget changes",
                "A/B test different budget levels",
            ]

            return result
        except MetaApiError as exc:
            logger.error("Failed to get budget recommendations", entity_id=entity_id, error=exc.to_dict())
            raise

    async def set_daily_budget(
        self,
        entity_id: str,
        entity_type: str,
        daily_budget: float,
        validate_limits: bool = True,
    ) -> Dict[str, Any]:
        """
        Set daily budget with validation.
        """
        try:
            # Validate budget if requested
            if validate_limits:
                validation_result = await self._validate_budget_limits(entity_id, entity_type, daily_budget, "daily")
                if not validation_result["valid"]:
                    return {
                        "success": False,
                        "error": validation_result["error"],
                        "suggested_fix": validation_result["suggested_fix"],
                    }

            # Update budget
            update_data = {"daily_budget": daily_budget}
            await self._client.post(
                entity_id,
                body=update_data,
                encoding=self._client.GraphBodyEncoding.FORM,
            )

            # Get updated budget info
            updated_budget = await self.get_current_budget(entity_id, entity_type)

            return {
                "success": True,
                "entity_id": entity_id,
                "entity_type": entity_type,
                "new_daily_budget": daily_budget,
                "updated_budget_info": updated_budget,
                "next_recommended_actions": [
                    "Monitor spend vs. new budget",
                    "Track performance after budget change",
                    "Adjust if needed based on results",
                ],
            }
        except MetaApiError as exc:
            logger.error("Failed to set daily budget", entity_id=entity_id, error=exc.to_dict())
            return {
                "success": False,
                "error": exc.context.message,
                "suggested_fix": "Check account limits and entity status",
            }

    async def set_lifetime_budget(
        self,
        entity_id: str,
        entity_type: str,
        lifetime_budget: float,
        validate_limits: bool = True,
    ) -> Dict[str, Any]:
        """
        Set lifetime budget with validation.
        """
        try:
            # Validate budget if requested
            if validate_limits:
                validation_result = await self._validate_budget_limits(entity_id, entity_type, lifetime_budget, "lifetime")
                if not validation_result["valid"]:
                    return {
                        "success": False,
                        "error": validation_result["error"],
                        "suggested_fix": validation_result["suggested_fix"],
                    }

            # Update budget
            update_data = {"lifetime_budget": lifetime_budget}
            await self._client.post(
                entity_id,
                body=update_data,
                encoding=self._client.GraphBodyEncoding.FORM,
            )

            # Get updated budget info
            updated_budget = await self.get_current_budget(entity_id, entity_type)

            return {
                "success": True,
                "entity_id": entity_id,
                "entity_type": entity_type,
                "new_lifetime_budget": lifetime_budget,
                "updated_budget_info": updated_budget,
                "next_recommended_actions": [
                    "Monitor lifetime spend vs. new budget",
                    "Track performance after budget change",
                    "Adjust if needed based on results",
                ],
            }
        except MetaApiError as exc:
            logger.error("Failed to set lifetime budget", entity_id=entity_id, error=exc.to_dict())
            return {
                "success": False,
                "error": exc.context.message,
                "suggested_fix": "Check account limits and entity status",
            }

    async def adjust_budget_relative(
        self,
        entity_id: str,
        entity_type: str,
        adjustment_percentage: float,
        max_adjustment: Optional[float] = None,
        validate_limits: bool = True,
    ) -> Dict[str, Any]:
        """
        Adjust budget by percentage with optional maximum adjustment.
        """
        try:
            # Get current budget
            current_budget = await self.get_current_budget(entity_id, entity_type)
            current_daily_budget = current_budget["current_budgets"]["daily_budget"]

            # Calculate new budget
            adjustment_factor = 1 + (adjustment_percentage / 100)
            new_budget = current_daily_budget * adjustment_factor

            # Apply maximum adjustment if specified
            if max_adjustment:
                max_new_budget = current_daily_budget + max_adjustment
                if new_budget > max_new_budget:
                    new_budget = max_new_budget
                    adjustment_percentage = ((new_budget - current_daily_budget) / current_daily_budget) * 100

            # Validate budget if requested
            if validate_limits:
                validation_result = await self._validate_budget_limits(entity_id, entity_type, new_budget, "daily")
                if not validation_result["valid"]:
                    return {
                        "success": False,
                        "error": validation_result["error"],
                        "suggested_fix": validation_result["suggested_fix"],
                    }

            # Update budget
            update_data = {"daily_budget": new_budget}
            await self._client.post(
                entity_id,
                body=update_data,
                encoding=self._client.GraphBodyEncoding.FORM,
            )

            return {
                "success": True,
                "entity_id": entity_id,
                "entity_type": entity_type,
                "original_budget": current_daily_budget,
                "adjustment_percentage": adjustment_percentage,
                "new_budget": new_budget,
                "actual_adjustment": new_budget - current_daily_budget,
                "next_recommended_actions": [
                    "Monitor performance after budget adjustment",
                    "Track spend vs. new budget",
                    "Consider further adjustments based on results",
                ],
            }
        except MetaApiError as exc:
            logger.error("Failed to adjust budget", entity_id=entity_id, error=exc.to_dict())
            return {
                "success": False,
                "error": exc.context.message,
                "suggested_fix": "Check account limits and entity status",
            }

    async def pause_on_limit(
        self,
        entity_id: str,
        entity_type: str,
        limit_type: str,
        limit_amount: float,
        action: str = "pause",
    ) -> Dict[str, Any]:
        """
        Set up automatic pause when limit is reached.
        Note: This is a conceptual implementation as Meta doesn't have direct pause-on-limit.
        In practice, this would be implemented using automated rules or monitoring.
        """
        try:
            # Get current spend
            current_budget = await self.get_current_budget(entity_id, entity_type)
            current_spend = current_budget["spend_metrics"]["total_spend_7d"]

            # Calculate remaining budget
            if limit_type == "daily_budget":
                remaining_budget = limit_amount - (current_budget["spend_metrics"]["avg_daily_spend"])
            else:  # lifetime_budget
                remaining_budget = limit_amount - current_spend

            # Create monitoring rule (conceptual - would use Meta's automated rules API)
            rule_data = {
                "name": f"Auto-{action} on {limit_type} limit",
                "entity_id": entity_id,
                "entity_type": entity_type,
                "limit_type": limit_type,
                "limit_amount": limit_amount,
                "action": action,
                "remaining_budget": remaining_budget,
                "status": "active",
            }

            return {
                "success": True,
                "entity_id": entity_id,
                "entity_type": entity_type,
                "limit_configuration": rule_data,
                "current_status": {
                    "current_spend": current_spend,
                    "limit_amount": limit_amount,
                    "remaining_budget": remaining_budget,
                    "utilization_pct": (current_spend / limit_amount * 100) if limit_amount > 0 else 0,
                },
                "next_recommended_actions": [
                    "Monitor budget utilization regularly",
                    "Set up alerts for approaching limits",
                    "Review and adjust limits based on performance",
                ],
            }
        except Exception as exc:
            logger.error("Failed to set pause on limit", entity_id=entity_id, error=str(exc))
            return {
                "success": False,
                "error": str(exc),
                "suggested_fix": "Check entity status and limit configuration",
            }

    async def schedule_budget_change(
        self,
        entity_id: str,
        entity_type: str,
        new_budget: float,
        schedule_date: str,
        schedule_time: str = "00:00",
        timezone: str = "UTC",
    ) -> Dict[str, Any]:
        """
        Schedule a future budget change.
        Note: This is a conceptual implementation. In practice, this would use
        a scheduling system or Meta's automated rules with time-based triggers.
        """
        try:
            # Get current budget
            current_budget = await self.get_current_budget(entity_id, entity_type)
            current_daily_budget = current_budget["current_budgets"]["daily_budget"]

            # Create schedule entry (conceptual - would be stored in a database)
            schedule_data = {
                "entity_id": entity_id,
                "entity_type": entity_type,
                "current_budget": current_daily_budget,
                "new_budget": new_budget,
                "schedule_date": schedule_date,
                "schedule_time": schedule_time,
                "timezone": timezone,
                "status": "scheduled",
                "created_at": datetime.now().isoformat(),
            }

            return {
                "success": True,
                "entity_id": entity_id,
                "entity_type": entity_type,
                "schedule_configuration": schedule_data,
                "change_summary": {
                    "current_budget": current_daily_budget,
                    "new_budget": new_budget,
                    "change_amount": new_budget - current_daily_budget,
                    "change_percentage": ((new_budget - current_daily_budget) / current_daily_budget * 100) if current_daily_budget > 0 else 0,
                },
                "next_recommended_actions": [
                    "Monitor current performance until scheduled change",
                    "Review schedule before execution",
                    "Track performance after budget change",
                ],
            }
        except Exception as exc:
            logger.error("Failed to schedule budget change", entity_id=entity_id, error=str(exc))
            return {
                "success": False,
                "error": str(exc),
                "suggested_fix": "Check schedule parameters and entity status",
            }

    async def _validate_budget_limits(
        self,
        entity_id: str,
        entity_type: str,
        budget_amount: float,
        budget_type: str,
    ) -> Dict[str, Any]:
        """
        Validate budget against account limits and constraints.
        """
        try:
            # Get account information
            account_response = await self._client.get(
                entity_id.split("_")[0],  # Extract account ID
                params={"fields": "account_status,currency,timezone_name"}
            )
            account_data = account_response.get("data", {})

            # Basic validation rules
            min_daily_budget = 1.0  # Minimum daily budget
            max_daily_budget = 10000.0  # Maximum daily budget (adjust based on account)

            if budget_type == "daily":
                if budget_amount < min_daily_budget:
                    return {
                        "valid": False,
                        "error": f"Daily budget too low: ${budget_amount}",
                        "suggested_fix": f"Increase daily budget to at least ${min_daily_budget}",
                    }
                if budget_amount > max_daily_budget:
                    return {
                        "valid": False,
                        "error": f"Daily budget too high: ${budget_amount}",
                        "suggested_fix": f"Decrease daily budget to maximum ${max_daily_budget}",
                    }

            # Check account status
            if account_data.get("account_status") != 1:  # 1 = active
                return {
                    "valid": False,
                    "error": "Account is not active",
                    "suggested_fix": "Activate account before setting budgets",
                }

            return {"valid": True}
        except Exception as exc:
            logger.error("Failed to validate budget limits", entity_id=entity_id, error=str(exc))
            return {
                "valid": False,
                "error": "Validation failed",
                "suggested_fix": "Check account status and limits",
            }
