"""
Threads Ads Tools for Meta Ads MCP Server

This module provides tools for creating and managing ads on Threads,
Meta's text-based social platform. Threads ads support targeting the
threads_stream placement and require threads_user_id in creatives.

Based on Meta Marketing API v24.0:
https://developers.facebook.com/docs/marketing-api/ad-creative/threads-ads
"""

from __future__ import annotations

from typing import Dict, Any, Optional

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

logger = get_logger(__name__)


class ThreadsAdsEngine:
    """Handles Threads advertising operations for Meta Ads"""
    
    def __init__(self, client: MetaClient):
        self.client = client
    
    async def get_threads_user_id(
        self, 
        arguments: Dict[str, Any]
    ) -> ToolResult:
        """
        Get the Threads User ID for creating Threads ads.
        
        There are two types of Threads accounts:
        1. Instagram-connected Threads - Linked to an Instagram account
        2. Instagram-backed Threads - Created for ads without full Threads account
        
        Args:
            arguments: {
                "instagram_user_id": "123456789",
                "ad_account_id": "act_123"
            }
        
        Returns:
            ToolResult with threads_user_id
        """
        try:
            instagram_user_id = arguments.get("instagram_user_id")
            
            if not instagram_user_id:
                return ToolResult(
                    ok=False,
                    error={"message": "instagram_user_id is required"}
                )
            
            logger.info(f"Getting Threads user ID for Instagram user: {instagram_user_id}")
            
            # Get the Threads user ID linked to this Instagram account
            response = await self.client.get(
                f"{instagram_user_id}",
                params={"fields": "threads_user_id,username"}
            )
            
            threads_user_id = response.get("threads_user_id")
            
            return ToolResult(
                ok=True,
                data={
                    "instagram_user_id": instagram_user_id,
                    "threads_user_id": threads_user_id,
                    "username": response.get("username"),
                    "account_type": "INSTAGRAM_CONNECTED" if threads_user_id else "NEEDS_CREATION",
                    "next_steps": [
                        "Use this threads_user_id in ad creatives",
                        "Set threads_positions in ad set targeting"
                    ] if threads_user_id else [
                        "Create an Instagram-backed Threads account",
                        "Or connect the Instagram account to Threads"
                    ]
                }
            )
            
        except MetaApiError as exc:
            logger.error("get_threads_user_id_failed", **exc.to_dict())
            return ToolResult(ok=False, error=exc.to_dict())
        except Exception as exc:
            logger.error(f"get_threads_user_id_error: {exc}")
            return ToolResult(ok=False, error={"message": str(exc)})
    
    async def create_instagram_backed_threads(
        self, 
        arguments: Dict[str, Any]
    ) -> ToolResult:
        """
        Create an Instagram-backed Threads account for advertising.
        
        This allows running Threads ads without needing a full Threads account.
        The account is linked to an Instagram account for ad permissions.
        
        Args:
            arguments: {
                "business_id": "123",
                "instagram_user_id": "456"
            }
        
        Returns:
            ToolResult with new threads_user_id
        """
        try:
            business_id = arguments.get("business_id")
            instagram_user_id = arguments.get("instagram_user_id")
            
            if not business_id or not instagram_user_id:
                return ToolResult(
                    ok=False,
                    error={"message": "business_id and instagram_user_id are required"}
                )
            
            logger.info(f"Creating Instagram-backed Threads for IG user: {instagram_user_id}")
            
            response = await self.client.post(
                f"{business_id}/instagram_backed_threads_accounts",
                body={"instagram_user_id": instagram_user_id}
            )
            
            return ToolResult(
                ok=True,
                data={
                    "threads_user_id": response.get("id"),
                    "instagram_user_id": instagram_user_id,
                    "account_type": "INSTAGRAM_BACKED",
                    "message": "Instagram-backed Threads account created successfully",
                    "capabilities": [
                        "Run ads on Threads stream",
                        "Use Threads placements in ad targeting",
                        "No full Threads account required"
                    ]
                }
            )
            
        except MetaApiError as exc:
            logger.error("create_instagram_backed_threads_failed", **exc.to_dict())
            return ToolResult(ok=False, error=exc.to_dict())
        except Exception as exc:
            logger.error(f"create_instagram_backed_threads_error: {exc}")
            return ToolResult(ok=False, error={"message": str(exc)})
    
    async def create_threads_ad_set(
        self, 
        arguments: Dict[str, Any]
    ) -> ToolResult:
        """
        Create an ad set targeting Threads stream placement.
        
        Threads ads appear in the threads_stream placement and can be
        combined with Instagram stream for cross-platform reach.
        
        Args:
            arguments: {
                "ad_account_id": "act_123",
                "campaign_id": "456",
                "name": "Threads Ad Set",
                "daily_budget": 10000,
                "optimization_goal": "LINK_CLICKS",
                "targeting_countries": ["US"],
                "include_instagram": true  # Also target Instagram stream
            }
        
        Returns:
            ToolResult with ad set details
        """
        try:
            ad_account_id = arguments.get("ad_account_id")
            campaign_id = arguments.get("campaign_id")
            name = arguments.get("name")
            daily_budget = arguments.get("daily_budget")
            
            if not all([ad_account_id, campaign_id, name, daily_budget]):
                return ToolResult(
                    ok=False,
                    error={"message": "ad_account_id, campaign_id, name, and daily_budget are required"}
                )
            
            logger.info(f"Creating Threads ad set: {name}")
            
            # Build targeting with Threads positions
            include_instagram = arguments.get("include_instagram", True)
            
            publisher_platforms = ["threads"]
            positions = {
                "threads_positions": ["threads_stream"]
            }
            
            if include_instagram:
                publisher_platforms.append("instagram")
                positions["instagram_positions"] = ["stream"]
            
            targeting = {
                "geo_locations": {
                    "countries": arguments.get("targeting_countries", ["US"])
                },
                "publisher_platforms": publisher_platforms,
                **positions
            }
            
            # Add optional targeting
            if "age_min" in arguments:
                targeting["age_min"] = arguments["age_min"]
            if "age_max" in arguments:
                targeting["age_max"] = arguments["age_max"]
            if "user_os" in arguments:
                targeting["user_os"] = arguments["user_os"]
            
            adset_data = {
                "name": name,
                "campaign_id": campaign_id,
                "daily_budget": daily_budget,
                "billing_event": "IMPRESSIONS",
                "optimization_goal": arguments.get("optimization_goal", "LINK_CLICKS"),
                "bid_strategy": arguments.get("bid_strategy", "LOWEST_COST_WITHOUT_CAP"),
                "targeting": targeting,
                "status": "PAUSED"
            }
            
            if "bid_amount" in arguments:
                adset_data["bid_amount"] = arguments["bid_amount"]
            
            response = await self.client.post(
                f"{ad_account_id}/adsets",
                body=adset_data
            )
            
            return ToolResult(
                ok=True,
                data={
                    "adset_id": response.get("id"),
                    "name": name,
                    "placements": {
                        "threads": ["threads_stream"],
                        "instagram": ["stream"] if include_instagram else []
                    },
                    "message": "Threads ad set created successfully",
                    "status": "PAUSED",
                    "next_steps": [
                        "Create ad creative with threads_user_id",
                        "Create ad linking creative to this ad set",
                        "Activate ad set when ready"
                    ]
                }
            )
            
        except MetaApiError as exc:
            logger.error("create_threads_ad_set_failed", **exc.to_dict())
            return ToolResult(ok=False, error=exc.to_dict())
        except Exception as exc:
            logger.error(f"create_threads_ad_set_error: {exc}")
            return ToolResult(ok=False, error={"message": str(exc)})
    
    async def create_threads_creative(
        self, 
        arguments: Dict[str, Any]
    ) -> ToolResult:
        """
        Create an ad creative for Threads with threads_user_id.
        
        The creative requires both instagram_user_id and threads_user_id.
        The instagram_user_id must be associated with the threads_user_id.
        
        Args:
            arguments: {
                "ad_account_id": "act_123",
                "name": "Threads Creative",
                "threads_user_id": "789",
                "instagram_user_id": "456",
                "page_id": "321",
                "message": "Check out our new product!",
                "link": "https://example.com",
                "image_hash": "abc123",  # or video_id
                "call_to_action": "LEARN_MORE"
            }
        
        Returns:
            ToolResult with creative ID
        """
        try:
            ad_account_id = arguments.get("ad_account_id")
            threads_user_id = arguments.get("threads_user_id")
            instagram_user_id = arguments.get("instagram_user_id")
            page_id = arguments.get("page_id")
            
            if not all([ad_account_id, threads_user_id, instagram_user_id, page_id]):
                return ToolResult(
                    ok=False,
                    error={"message": "ad_account_id, threads_user_id, instagram_user_id, and page_id are required"}
                )
            
            # v24.0 Threads validation: Caption length limit
            message = arguments.get("message", "")
            if len(message) > 500:
                return ToolResult(
                    ok=False,
                    error={
                        "message": f"Threads caption exceeds 500 character limit ({len(message)} chars)",
                        "validation_error": "THREADS_CAPTION_TOO_LONG",
                        "limit": 500,
                        "current_length": len(message)
                    }
                )
            
            # v24.0 Threads validation: Image dimension check
            image_width = arguments.get("image_width")
            if image_width is not None and image_width < 500:
                return ToolResult(
                    ok=False,
                    error={
                        "message": f"Threads image width must be at least 500px (got {image_width}px)",
                        "validation_error": "THREADS_IMAGE_TOO_SMALL",
                        "min_width": 500,
                        "current_width": image_width
                    }
                )
            
            logger.info(f"Creating Threads creative for threads_user: {threads_user_id}")
            
            # Build link_data
            link_data = {
                "link": arguments.get("link", "https://example.com"),
                "message": message,
                "call_to_action": {
                    "type": arguments.get("call_to_action", "LEARN_MORE"),
                    "value": {}
                }
            }
            
            # Add image or video
            if "image_hash" in arguments:
                link_data["image_hash"] = arguments["image_hash"]
            elif "video_id" in arguments:
                link_data["video_id"] = arguments["video_id"]
            
            # Build creative with object_story_spec containing threads_user_id
            creative_data = {
                "name": arguments.get("name", "Threads Creative"),
                "object_story_spec": {
                    "page_id": page_id,
                    "instagram_user_id": instagram_user_id,
                    "threads_user_id": threads_user_id,
                    "link_data": link_data
                }
            }
            
            response = await self.client.post(
                f"{ad_account_id}/adcreatives",
                body=creative_data
            )
            
            return ToolResult(
                ok=True,
                data={
                    "creative_id": response.get("id"),
                    "threads_user_id": threads_user_id,
                    "instagram_user_id": instagram_user_id,
                    "message": "Threads creative created successfully",
                    "placement_support": ["threads_stream", "instagram_stream"],
                    "next_steps": [
                        "Create ad using this creative",
                        "Link to ad set with threads_positions targeting"
                    ]
                }
            )
            
        except MetaApiError as exc:
            logger.error("create_threads_creative_failed", **exc.to_dict())
            return ToolResult(ok=False, error=exc.to_dict())
        except Exception as exc:
            logger.error(f"create_threads_creative_error: {exc}")
            return ToolResult(ok=False, error={"message": str(exc)})
    
    async def create_threads_ad(
        self, 
        arguments: Dict[str, Any]
    ) -> ToolResult:
        """
        Create a complete Threads ad linking creative to ad set.
        
        Args:
            arguments: {
                "ad_account_id": "act_123",
                "name": "Threads Ad",
                "adset_id": "456",
                "creative_id": "789"
            }
        
        Returns:
            ToolResult with ad ID
        """
        try:
            ad_account_id = arguments.get("ad_account_id")
            adset_id = arguments.get("adset_id")
            creative_id = arguments.get("creative_id")
            name = arguments.get("name", "Threads Ad")
            
            if not all([ad_account_id, adset_id, creative_id]):
                return ToolResult(
                    ok=False,
                    error={"message": "ad_account_id, adset_id, and creative_id are required"}
                )
            
            logger.info(f"Creating Threads ad: {name}")
            
            ad_data = {
                "name": name,
                "adset_id": adset_id,
                "creative": {"creative_id": creative_id},
                "status": "PAUSED"
            }
            
            response = await self.client.post(
                f"{ad_account_id}/ads",
                body=ad_data
            )
            
            return ToolResult(
                ok=True,
                data={
                    "ad_id": response.get("id"),
                    "adset_id": adset_id,
                    "creative_id": creative_id,
                    "status": "PAUSED",
                    "message": "Threads ad created successfully",
                    "next_steps": [
                        "Review ad in Ads Manager",
                        "Set ad to ACTIVE when ready to launch",
                        "Monitor Threads-specific metrics"
                    ]
                }
            )
            
        except MetaApiError as exc:
            logger.error("create_threads_ad_failed", **exc.to_dict())
            return ToolResult(ok=False, error=exc.to_dict())
        except Exception as exc:
            logger.error(f"create_threads_ad_error: {exc}")
            return ToolResult(ok=False, error={"message": str(exc)})
    
    async def get_threads_insights(
        self, 
        arguments: Dict[str, Any]
    ) -> ToolResult:
        """
        Get performance insights specifically for Threads placements.
        
        Breaks down metrics by threads_stream vs other placements.
        
        Args:
            arguments: {
                "object_id": "campaign_id or adset_id or ad_id",
                "level": "campaign|adset|ad",
                "time_range": {"since": "2024-01-01", "until": "2024-01-31"}
            }
        
        Returns:
            ToolResult with Threads-specific insights
        """
        try:
            object_id = arguments.get("object_id")
            level = arguments.get("level", "ad")
            time_range = arguments.get("time_range", {})
            
            if not object_id:
                return ToolResult(
                    ok=False,
                    error={"message": "object_id is required"}
                )
            
            logger.info(f"Getting Threads insights for {object_id}")
            
            params = {
                "fields": ",".join([
                    "impressions",
                    "clicks",
                    "spend",
                    "reach",
                    "ctr",
                    "cpm",
                    "actions",
                    "cost_per_action_type"
                ]),
                "breakdowns": "publisher_platform,platform_position",
                "level": level
            }
            
            if time_range:
                params["time_range"] = time_range
            
            response = await self.client.get(
                f"{object_id}/insights",
                params=params
            )
            
            # Filter for Threads data
            all_data = response.get("data", [])
            threads_data = [
                row for row in all_data 
                if row.get("publisher_platform") == "threads"
            ]
            
            return ToolResult(
                ok=True,
                data={
                    "object_id": object_id,
                    "level": level,
                    "threads_insights": threads_data,
                    "all_placements": all_data,
                    "threads_summary": {
                        "total_impressions": sum(int(row.get("impressions", 0)) for row in threads_data),
                        "total_clicks": sum(int(row.get("clicks", 0)) for row in threads_data),
                        "total_spend": sum(float(row.get("spend", 0)) for row in threads_data)
                    }
                }
            )
            
        except MetaApiError as exc:
            logger.error("get_threads_insights_failed", **exc.to_dict())
            return ToolResult(ok=False, error=exc.to_dict())
        except Exception as exc:
            logger.error(f"get_threads_insights_error: {exc}")
            return ToolResult(ok=False, error={"message": str(exc)})


_threads_ads_engine: Optional[ThreadsAdsEngine] = None


def get_threads_ads_engine() -> ThreadsAdsEngine:
    """Get the global ThreadsAdsEngine instance"""
    global _threads_ads_engine
    if _threads_ads_engine is None:
        from .meta_client import get_client
        _threads_ads_engine = ThreadsAdsEngine(get_client())
    return _threads_ads_engine
