from __future__ import annotations

import asyncio
from typing import Any, Dict, List, Optional
from dataclasses import dataclass, field
from enum import Enum

from .logging import get_logger
from .meta_client import MetaClient
from .bulk_operations import BulkOperationManager, BulkOperationConfig
from .cache import get_cache_manager, cache_key

logger = get_logger(__name__)


class AccountType(str, Enum):
    """Types of Meta accounts."""
    BUSINESS = "business"
    AD_ACCOUNT = "ad_account"
    PAGE = "page"
    INSTAGRAM_ACCOUNT = "instagram_account"


class PermissionLevel(str, Enum):
    """Permission levels for account access."""
    ADMIN = "ADMIN"
    ADVERTISER = "ADVERTISER"
    ANALYST = "ANALYST"
    READ_ONLY = "READ_ONLY"


@dataclass
class AccountInfo:
    """Information about a Meta account."""
    id: str
    name: str
    account_type: AccountType
    permission_level: Optional[PermissionLevel] = None
    currency: Optional[str] = None
    timezone: Optional[str] = None
    business_id: Optional[str] = None
    is_active: bool = True
    metadata: Dict[str, Any] = field(default_factory=dict)


@dataclass
class BusinessHierarchy:
    """Business account hierarchy structure."""
    business_id: str
    business_name: str
    ad_accounts: List[AccountInfo] = field(default_factory=list)
    pages: List[AccountInfo] = field(default_factory=list)
    instagram_accounts: List[AccountInfo] = field(default_factory=list)
    child_businesses: List["BusinessHierarchy"] = field(default_factory=list)


@dataclass
class CrossAccountOperation:
    """Configuration for cross-account operations."""
    account_ids: List[str]
    operation_type: str
    parameters: Dict[str, Any]
    parallel_execution: bool = True
    max_concurrent: int = 10


class MultiAccountManager:
    """Manages operations across multiple Meta accounts."""
    
    def __init__(self, client: MetaClient, config: Optional[BulkOperationConfig] = None):
        self.client = client
        self.config = config or BulkOperationConfig()
        self.cache_manager = get_cache_manager()
        self.bulk_manager = BulkOperationManager(client, config)
        self._account_cache: Dict[str, AccountInfo] = {}
        self._hierarchy_cache: Dict[str, BusinessHierarchy] = {}
    
    async def get_business_hierarchy(self, business_id: str) -> BusinessHierarchy:
        """Get complete business hierarchy including all child accounts."""
        cache_key_str = cache_key("business_hierarchy", business_id)
        
        # Try cache first
        cached_hierarchy = await self.cache_manager.get(cache_key_str)
        if cached_hierarchy:
            logger.debug("Cache hit for business hierarchy", business_id=business_id)
            return BusinessHierarchy(**cached_hierarchy)
        
        logger.info("Building business hierarchy", business_id=business_id)
        
        # Get business info
        business_info = await self.client.get(
            f"{business_id}",
            params={"fields": "id,name,timezone,currency"}
        )
        
        hierarchy = BusinessHierarchy(
            business_id=business_id,
            business_name=business_info.get("name", "Unknown Business")
        )
        
        # Get ad accounts
        ad_accounts = await self._get_ad_accounts_for_business(business_id)
        hierarchy.ad_accounts = ad_accounts
        
        # Get pages
        pages = await self._get_pages_for_business(business_id)
        hierarchy.pages = pages
        
        # Get Instagram accounts
        instagram_accounts = await self._get_instagram_accounts_for_business(business_id)
        hierarchy.instagram_accounts = instagram_accounts
        
        # Get child businesses
        child_businesses = await self._get_child_businesses(business_id)
        hierarchy.child_businesses = child_businesses
        
        # Cache the hierarchy
        await self.cache_manager.set(
            cache_key_str,
            hierarchy.__dict__,
            900  # 15 minutes
        )
        
        logger.info(
            "Business hierarchy built",
            business_id=business_id,
            ad_accounts=len(ad_accounts),
            pages=len(pages),
            instagram_accounts=len(instagram_accounts),
            child_businesses=len(child_businesses)
        )
        
        return hierarchy
    
    async def _get_ad_accounts_for_business(self, business_id: str) -> List[AccountInfo]:
        """Get all ad accounts for a business."""
        try:
            response = await self.client.get(
                f"{business_id}/adaccounts",
                params={
                    "fields": "id,name,account_status,currency,timezone,permissions",
                    "limit": 1000
                }
            )
            
            accounts = []
            for account_data in response.get("data", []):
                account = AccountInfo(
                    id=account_data["id"],
                    name=account_data.get("name", "Unknown Account"),
                    account_type=AccountType.AD_ACCOUNT,
                    currency=account_data.get("currency"),
                    timezone=account_data.get("timezone"),
                    business_id=business_id,
                    is_active=account_data.get("account_status") == 1,
                    metadata={
                        "permissions": account_data.get("permissions", []),
                        "account_status": account_data.get("account_status")
                    }
                )
                accounts.append(account)
                self._account_cache[account.id] = account
            
            return accounts
            
        except Exception as e:
            logger.error("Failed to get ad accounts", business_id=business_id, error=str(e))
            return []
    
    async def _get_pages_for_business(self, business_id: str) -> List[AccountInfo]:
        """Get all pages for a business."""
        try:
            response = await self.client.get(
                f"{business_id}/owned_pages",
                params={
                    "fields": "id,name,category,access_token",
                    "limit": 1000
                }
            )
            
            accounts = []
            for page_data in response.get("data", []):
                account = AccountInfo(
                    id=page_data["id"],
                    name=page_data.get("name", "Unknown Page"),
                    account_type=AccountType.PAGE,
                    business_id=business_id,
                    metadata={
                        "category": page_data.get("category"),
                        "has_access_token": bool(page_data.get("access_token"))
                    }
                )
                accounts.append(account)
                self._account_cache[account.id] = account
            
            return accounts
            
        except Exception as e:
            logger.error("Failed to get pages", business_id=business_id, error=str(e))
            return []
    
    async def _get_instagram_accounts_for_business(self, business_id: str) -> List[AccountInfo]:
        """Get all Instagram accounts for a business."""
        try:
            response = await self.client.get(
                f"{business_id}/instagram_accounts",
                params={
                    "fields": "id,username,account_type,biography",
                    "limit": 1000
                }
            )
            
            accounts = []
            for instagram_data in response.get("data", []):
                account = AccountInfo(
                    id=instagram_data["id"],
                    name=instagram_data.get("username", "Unknown Instagram"),
                    account_type=AccountType.INSTAGRAM_ACCOUNT,
                    business_id=business_id,
                    metadata={
                        "account_type": instagram_data.get("account_type"),
                        "biography": instagram_data.get("biography")
                    }
                )
                accounts.append(account)
                self._account_cache[account.id] = account
            
            return accounts
            
        except Exception as e:
            logger.error("Failed to get Instagram accounts", business_id=business_id, error=str(e))
            return []
    
    async def _get_child_businesses(self, business_id: str) -> List[BusinessHierarchy]:
        """Get child businesses recursively."""
        try:
            response = await self.client.get(
                f"{business_id}/businesses",
                params={"fields": "id,name", "limit": 100}
            )
            
            child_businesses = []
            for child_data in response.get("data", []):
                child_hierarchy = await self.get_business_hierarchy(child_data["id"])
                child_businesses.append(child_hierarchy)
            
            return child_businesses
            
        except Exception as e:
            logger.error("Failed to get child businesses", business_id=business_id, error=str(e))
            return []
    
    async def get_all_ad_accounts(self, business_id: str) -> List[AccountInfo]:
        """Get all ad accounts in the business hierarchy (including child businesses)."""
        hierarchy = await self.get_business_hierarchy(business_id)
        all_accounts = []
        
        def collect_accounts(h: BusinessHierarchy):
            all_accounts.extend(h.ad_accounts)
            for child in h.child_businesses:
                collect_accounts(child)
        
        collect_accounts(hierarchy)
        return all_accounts
    
    async def cross_account_insights(
        self,
        account_ids: List[str],
        fields: List[str],
        time_range: Optional[Dict[str, str]] = None,
        level: str = "campaign"
    ) -> Dict[str, Any]:
        """Get insights across multiple ad accounts."""
        logger.info(
            "Starting cross-account insights",
            account_count=len(account_ids),
            level=level
        )
        
        # Create tasks for each account
        insight_tasks = []
        for account_id in account_ids:
            cache_key_str = cache_key(
                "cross_account_insights",
                account_id,
                "fields", ",".join(fields),
                "level", level,
                "time_range", str(time_range) if time_range else "none"
            )
            
            async def fetch_account_insights(acc_id: str):
                return await self.bulk_manager._fetch_cached_or_execute(
                    cache_key_str,
                    lambda: self.client.get(
                        f"{acc_id}/insights",
                        params={
                            "fields": ",".join(fields),
                            "level": level,
                            "time_range": time_range
                        }
                    ),
                    300  # 5 minutes cache
                )
            
            task = self.bulk_manager._execute_with_retry(
                lambda: fetch_account_insights(account_id),
                f"insights_account_{account_id}"
            )
            insight_tasks.append((account_id, task))
        
        # Execute all tasks in parallel
        results = await asyncio.gather(
            *[task for _, task in insight_tasks],
            return_exceptions=True
        )
        
        # Aggregate results by account
        aggregated_results = {}
        total_insights = 0
        
        for i, (account_id, _) in enumerate(insight_tasks):
            result = results[i]
            
            if isinstance(result, Exception):
                aggregated_results[account_id] = {
                    "success": False,
                    "error": str(result)
                }
            elif hasattr(result, 'success') and not result.success:
                aggregated_results[account_id] = {
                    "success": False,
                    "error": result.error
                }
            else:
                # Extract insights data
                insights_data = result
                if hasattr(result, 'data'):
                    insights_data = result.data
                
                insights = insights_data.get("data", []) if isinstance(insights_data, dict) else []
                aggregated_results[account_id] = {
                    "success": True,
                    "insights": insights,
                    "count": len(insights)
                }
                total_insights += len(insights)
        
        logger.info(
            "Cross-account insights completed",
            successful_accounts=sum(1 for r in aggregated_results.values() if r["success"]),
            failed_accounts=sum(1 for r in aggregated_results.values() if not r["success"]),
            total_insights=total_insights
        )
        
        return aggregated_results
    
    async def cross_account_campaign_management(
        self,
        account_ids: List[str],
        operation: str,
        parameters: Dict[str, Any]
    ) -> Dict[str, Any]:
        """Perform campaign management operations across multiple accounts."""
        logger.info(
            "Starting cross-account campaign management",
            account_count=len(account_ids),
            operation=operation
        )
        
        # Create tasks for each account
        operation_tasks = []
        for account_id in account_ids:
            async def execute_account_operation(acc_id: str):
                if operation == "list_campaigns":
                    return await self.client.get(
                        f"{acc_id}/campaigns",
                        params={
                            "fields": parameters.get("fields", "id,name,status,objective"),
                            "limit": parameters.get("limit", 100)
                        }
                    )
                elif operation == "update_status":
                    campaign_ids = parameters.get("campaign_ids", [])
                    status = parameters.get("status")
                    
                    # Use bulk update for multiple campaigns
                    if len(campaign_ids) > 1:
                        updates = [{"id": cid, "status": status} for cid in campaign_ids]
                        return await self.bulk_manager.bulk_update(updates, "campaigns")
                    else:
                        return await self.client.post(
                            f"{campaign_ids[0]}",
                            body={"status": status}
                        )
                else:
                    raise ValueError(f"Unsupported operation: {operation}")
            
            task = self.bulk_manager._execute_with_retry(
                lambda: execute_account_operation(account_id),
                f"{operation}_account_{account_id}"
            )
            operation_tasks.append((account_id, task))
        
        # Execute all tasks in parallel
        results = await asyncio.gather(
            *[task for _, task in operation_tasks],
            return_exceptions=True
        )
        
        # Aggregate results by account
        aggregated_results = {}
        
        for i, (account_id, _) in enumerate(operation_tasks):
            result = results[i]
            
            if isinstance(result, Exception):
                aggregated_results[account_id] = {
                    "success": False,
                    "error": str(result)
                }
            elif hasattr(result, 'success') and not result.success:
                aggregated_results[account_id] = {
                    "success": False,
                    "error": result.error
                }
            else:
                aggregated_results[account_id] = {
                    "success": True,
                    "data": result.data if hasattr(result, 'data') else result
                }
        
        logger.info(
            "Cross-account campaign management completed",
            successful_accounts=sum(1 for r in aggregated_results.values() if r["success"]),
            failed_accounts=sum(1 for r in aggregated_results.values() if not r["success"])
        )
        
        return aggregated_results
    
    async def get_account_permissions(self, account_id: str) -> Dict[str, Any]:
        """Get detailed permissions for an account."""
        try:
            response = await self.client.get(
                f"{account_id}",
                params={"fields": "permissions,roles"}
            )
            
            return {
                "account_id": account_id,
                "permissions": response.get("permissions", []),
                "roles": response.get("roles", []),
                "has_admin": any(p.get("role") == "ADMIN" for p in response.get("permissions", [])),
                "has_advertiser": any(p.get("role") == "ADVERTISER" for p in response.get("permissions", [])),
                "has_analyst": any(p.get("role") == "ANALYST" for p in response.get("permissions", [])),
            }
            
        except Exception as e:
            logger.error("Failed to get account permissions", account_id=account_id, error=str(e))
            return {
                "account_id": account_id,
                "error": str(e)
            }
    
    async def validate_account_access(self, account_ids: List[str]) -> Dict[str, bool]:
        """Validate access to multiple accounts."""
        logger.info("Validating account access", account_count=len(account_ids))
        
        validation_tasks = []
        for account_id in account_ids:
            async def validate_account(acc_id: str):
                try:
                    await self.client.get(f"{acc_id}", params={"fields": "id"})
                    return True
                except Exception:
                    return False
            
            task = asyncio.create_task(validate_account(account_id))
            validation_tasks.append((account_id, task))
        
        # Execute all validations in parallel
        results = await asyncio.gather(
            *[task for _, task in validation_tasks],
            return_exceptions=True
        )
        
        # Aggregate results
        access_results = {}
        for i, (account_id, _) in enumerate(validation_tasks):
            result = results[i]
            access_results[account_id] = result is True and not isinstance(result, Exception)
        
        accessible_count = sum(access_results.values())
        logger.info(
            "Account access validation completed",
            accessible_accounts=accessible_count,
            total_accounts=len(account_ids)
        )
        
        return access_results


# Utility functions for common multi-account operations
async def get_all_campaigns_across_accounts(
    client: MetaClient,
    business_id: str,
    fields: List[str] = None
) -> Dict[str, List[Dict[str, Any]]]:
    """Get all campaigns across all ad accounts in a business."""
    if fields is None:
        fields = ["id", "name", "status", "objective", "created_time"]
    
    manager = MultiAccountManager(client)
    all_accounts = await manager.get_all_ad_accounts(business_id)
    
    account_ids = [acc.id for acc in all_accounts if acc.is_active]
    results = await manager.cross_account_campaign_management(
        account_ids,
        "list_campaigns",
        {"fields": fields, "limit": 1000}
    )
    
    # Extract campaigns from results
    campaigns_by_account = {}
    for account_id, result in results.items():
        if result["success"]:
            campaigns = result["data"].get("data", [])
            campaigns_by_account[account_id] = campaigns
        else:
            campaigns_by_account[account_id] = []
    
    return campaigns_by_account


async def bulk_update_campaigns_across_accounts(
    client: MetaClient,
    account_campaign_updates: Dict[str, List[Dict[str, Any]]]
) -> Dict[str, Any]:
    """Update campaigns across multiple accounts."""
    manager = MultiAccountManager(client)
    
    # Group updates by account
    account_updates = {}
    for account_id, updates in account_campaign_updates.items():
        account_updates[account_id] = {
            "operation": "update_status",
            "parameters": {
                "campaign_ids": [update["id"] for update in updates],
                "status": updates[0]["status"] if updates else None
            }
        }
    
    # Execute updates for each account
    results = {}
    for account_id, update_config in account_updates.items():
        result = await manager.cross_account_campaign_management(
            [account_id],
            update_config["operation"],
            update_config["parameters"]
        )
        results[account_id] = result[account_id]
    
    return results
