from typing import Any, Dict, Tuple
from functools import partial

from mcp import types as mcp_types

from ..errors import MetaApiError
from ..logging import get_logger
from ..meta_client import MetaClient
from ..schemas import (
    CreateCustomAudienceRequest,
    CreateLookalikeAudienceRequest,
    GetAudienceInsightsRequest,
    CheckFlaggedAudienceRequest,
    ValidateLookalikeSpecRequest,
    GraphBodyEncoding,
    ToolResult
)
from .base import ToolHandler, _tool_schema

logger = get_logger(__name__)

async def create_custom_audience_handler(client: MetaClient, arguments: Dict[str, Any]) -> ToolResult:
    request = CreateCustomAudienceRequest.model_validate(arguments)
    try:
        payload = {
            "name": request.name,
            "description": request.description,
            "subtype": request.subtype.value,
            "customer_file_source": request.customer_file_source,
            "retention_days": request.retention_days,
            "prefill": request.prefill,
            "is_eligible_for_sac_campaigns": request.is_eligible_for_sac_campaigns,
        }
        if request.rule:
            payload["rule"] = request.rule
            
        response = await client.post(
            f"{request.ad_account_id}/customaudiences",
            body=payload,
            encoding=GraphBodyEncoding.FORM,
        )
        return ToolResult(ok=True, data=response)
    except MetaApiError as exc:
        logger.error("tool_create_custom_audience_failed", **exc.to_dict())
        return ToolResult(ok=False, error=exc.to_dict())

async def create_lookalike_audience_handler(client: MetaClient, arguments: Dict[str, Any]) -> ToolResult:
    request = CreateLookalikeAudienceRequest.model_validate(arguments)
    try:
        payload = {
            "name": request.name,
            "description": request.description,
            "origin_audience_id": request.origin_audience_id,
            "ratio": request.ratio,
            "country": request.country,
        }
        
        response = await client.post(
            f"{request.ad_account_id}/customaudiences",
            body=payload,
            encoding=GraphBodyEncoding.FORM,
        )
        return ToolResult(ok=True, data=response)
    except MetaApiError as exc:
        logger.error("tool_create_lookalike_audience_failed", **exc.to_dict())
        return ToolResult(ok=False, error=exc.to_dict())

async def get_audience_insights_handler(client: MetaClient, arguments: Dict[str, Any]) -> ToolResult:
    request = GetAudienceInsightsRequest.model_validate(arguments)
    try:
        params = {
            "fields": ",".join(request.fields),
        }
        
        response = await client.get(
            f"{request.audience_id}/insights",
            params=params,
        )
        return ToolResult(ok=True, data=response)
    except MetaApiError as exc:
        logger.error("tool_get_audience_insights_failed", **exc.to_dict())
        return ToolResult(ok=False, error=exc.to_dict())


async def check_flagged_audience_handler(client: MetaClient, arguments: Dict[str, Any]) -> ToolResult:
    """v24.0+ Check if a custom audience is flagged before update operations.
    
    Flagged audiences will fail on update in v24.0+. Use this tool to check
    status before attempting any modifications.
    """
    request = CheckFlaggedAudienceRequest.model_validate(arguments)
    try:
        # Fetch audience with flagged status fields
        params = {
            "fields": ",".join(request.fields),
        }
        
        response = await client.get(
            request.audience_id,
            params=params,
        )
        
        # Determine if audience is flagged
        is_flagged = response.get("is_flagged", False)
        operation_status = response.get("operation_status", {})
        
        # Build resolution steps if flagged
        resolution_steps = None
        if is_flagged:
            resolution_steps = [
                "1. Go to Audiences in Meta Ads Manager",
                "2. Find the flagged audience",
                "3. Review the flagged reason",
                "4. Follow the resolution steps at: https://www.facebook.com/business/help/1055828013359808",
                "5. Once resolved, retry the update operation"
            ]
        
        result = {
            "audience_id": request.audience_id,
            "name": response.get("name", "Unknown"),
            "is_flagged": is_flagged,
            "flagged_reason": response.get("flagged_reason"),
            "operation_status": operation_status,
            "can_update": not is_flagged,
            "resolution_steps": resolution_steps,
            "v24_note": "v24.0+: Updating flagged custom audiences will fail. Resolve flagging before update."
        }
        
        return ToolResult(ok=True, data=result)
    except MetaApiError as exc:
        logger.error("tool_check_flagged_audience_failed", **exc.to_dict())
        return ToolResult(ok=False, error=exc.to_dict())


async def validate_lookalike_spec_handler(client: MetaClient, arguments: Dict[str, Any]) -> ToolResult:
    """v24.0+ Validate lookalike_spec before creating lookalike audience.
    
    v24.0 enforces stricter type validation on lookalike_spec fields.
    Use this tool to validate the spec before creation.
    """
    request = ValidateLookalikeSpecRequest.model_validate(arguments)
    try:
        errors = []
        warnings = []
        origin_audience_valid = True
        
        # Check if origin audience exists and is not flagged
        try:
            origin_response = await client.get(
                request.lookalike_spec.origin_audience_id,
                params={"fields": "id,name,is_flagged,approximate_count"},
            )
            
            if origin_response.get("is_flagged", False):
                errors.append(f"Origin audience {request.lookalike_spec.origin_audience_id} is flagged")
                origin_audience_valid = False
                
            approx_count = origin_response.get("approximate_count", 0)
            if approx_count < 100:
                warnings.append(f"Origin audience has low count ({approx_count}). Recommend 1000+ for better results.")
                
        except MetaApiError as e:
            errors.append(f"Could not validate origin audience: {str(e)}")
            origin_audience_valid = False
        
        # Validate ratio
        if request.lookalike_spec.ratio < 0.01 or request.lookalike_spec.ratio > 0.20:
            errors.append(f"Ratio {request.lookalike_spec.ratio} out of range. Must be 0.01-0.20 (1-20%)")
        
        # Validate type
        valid_types = ["similarity", "reach"]
        if request.lookalike_spec.type not in valid_types:
            errors.append(f"Invalid type '{request.lookalike_spec.type}'. Must be one of: {valid_types}")
        
        result = {
            "is_valid": len(errors) == 0,
            "errors": errors,
            "warnings": warnings,
            "origin_audience_valid": origin_audience_valid,
            "v24_note": "v24.0+: lookalike_spec field requires valid subfields. Invalid specs will fail."
        }
        
        return ToolResult(ok=True, data=result)
    except Exception as exc:
        logger.error("tool_validate_lookalike_spec_failed", error=str(exc))
        return ToolResult(ok=False, error={"message": str(exc)})


def get_tools(client: MetaClient) -> Dict[str, Tuple[mcp_types.Tool, ToolHandler]]:
    return {
        "meta.create_custom_audience": (
            mcp_types.Tool(
                name="meta.create_custom_audience",
                description="Create custom audiences from website visitors, app users, customer lists, or engagement. Supports website retargeting, CRM uploads, and engagement-based segmentation for precise targeting.",
                inputSchema=_tool_schema(CreateCustomAudienceRequest),
            ),
            partial(create_custom_audience_handler, client),
        ),
        "meta.create_lookalike_audience": (
            mcp_types.Tool(
                name="meta.create_lookalike_audience",
                description="Create lookalike audiences from custom audience seeds. Expand reach by targeting users similar to your best customers. Supports multiple countries. Use for prospecting and scaling proven segments.",
                inputSchema=_tool_schema(CreateLookalikeAudienceRequest),
            ),
            partial(create_lookalike_audience_handler, client),
        ),
        "meta.get_audience_insights": (
            mcp_types.Tool(
                name="meta.get_audience_insights",
                description="Get demographics, interests, and behaviors for custom audiences. Analyze audience size, overlap, and composition. Use for audience planning and targeting refinement.",
                inputSchema=_tool_schema(GetAudienceInsightsRequest),
            ),
            partial(get_audience_insights_handler, client),
        ),
        "meta.audience.check_flagged": (
            mcp_types.Tool(
                name="meta.audience.check_flagged",
                description="[v24.0+] Check if custom audience is flagged before updating. Returns flagged status and resolution steps. Required in v24.0 as flagged audiences fail on update.",
                inputSchema=_tool_schema(CheckFlaggedAudienceRequest),
            ),
            partial(check_flagged_audience_handler, client),
        ),
        "meta.lookalike.validate_spec": (
            mcp_types.Tool(
                name="meta.lookalike.validate_spec",
                description="[v24.0+] Validate lookalike_spec before creating lookalike audience. Checks origin audience health, ratio bounds, and country support. Prevents v24.0 validation errors.",
                inputSchema=_tool_schema(ValidateLookalikeSpecRequest),
            ),
            partial(validate_lookalike_spec_handler, client),
        ),
    }

