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

from mcp import types as mcp_types

from ..errors import MetaApiError
from ..logging import get_logger
from ..meta_client import MetaClient
from ..schemas import (
    UploadCreativeAssetRequest,
    GetCreativeAssetsRequest,
    AdCopyAnalysisRequest,
    VisualElementScorerRequest,
    DCOAnalysisRequest,
    CreativeTestingStatsRequest,
    MessageMarketMatchRequest,
    EmotionalAppealRequest,
    CreativeBenchmarkRequest,
    CrossFormatComparisonRequest,
    GraphBodyEncoding,
    ToolResult
)
from .base import ToolHandler, _tool_schema

logger = get_logger(__name__)


def _default_time_range(days: int = 30) -> Dict[str, str]:
    today = datetime.utcnow().date()
    since = today - timedelta(days=days)
    return {"since": since.isoformat(), "until": today.isoformat()}


def _ensure_ad_ids(arguments: Dict[str, Any]) -> list[str]:
    ad_ids = arguments.get("ad_ids")
    if isinstance(ad_ids, list):
        normalized = [str(ad_id) for ad_id in ad_ids if ad_id]
        if normalized:
            return normalized
    ad_id = arguments.get("ad_id")
    if ad_id:
        return [str(ad_id)]
    return []


def _ensure_single_ad_id(arguments: Dict[str, Any]) -> str | None:
    ad_id = arguments.get("ad_id")
    if ad_id:
        return str(ad_id)
    ad_ids = _ensure_ad_ids(arguments)
    return ad_ids[0] if ad_ids else None


def _to_payload(data: Any) -> Any:
    if hasattr(data, "model_dump"):
        return data.model_dump(mode="json")
    return data

async def upload_creative_asset_handler(client: MetaClient, arguments: Dict[str, Any]) -> ToolResult:
    request = UploadCreativeAssetRequest.model_validate(arguments)
    try:
        payload = {
            "type": request.asset_type.value,
        }
        if request.name:
            payload["name"] = request.name
        if request.description:
            payload["description"] = request.description
            
        response = await client.post(
            f"{request.ad_account_id}/adimages",
            body=payload,
            files=request.files,
            encoding=GraphBodyEncoding.FORM,
        )
        return ToolResult(ok=True, data=response)
    except MetaApiError as exc:
        logger.error("tool_upload_creative_asset_failed", **exc.to_dict())
        return ToolResult(ok=False, error=exc.to_dict())

async def get_creative_assets_handler(client: MetaClient, arguments: Dict[str, Any]) -> ToolResult:
    request = GetCreativeAssetsRequest.model_validate(arguments)
    try:
        params = {
            "fields": ",".join(request.fields),
            "limit": request.limit,
        }
        if request.asset_type:
            params["type"] = request.asset_type.value
        if request.after:
            params["after"] = request.after
            
        response = await client.get(
            f"{request.ad_account_id}/adimages",
            params=params,
        )
        return ToolResult(ok=True, data=response)
    except MetaApiError as exc:
        logger.error("tool_get_creative_assets_failed", **exc.to_dict())
        return ToolResult(ok=False, error=exc.to_dict())

# --- Creative Intelligence Handlers ---

async def analyze_ad_copy_performance_handler(client: MetaClient, arguments: Dict[str, Any]) -> ToolResult:
    try:
        from ..creative_intelligence import CreativeIntelligenceEngine
        engine = CreativeIntelligenceEngine(client)
        payload = dict(arguments)
        payload.setdefault("time_range", _default_time_range())
        payload["ad_ids"] = _ensure_ad_ids(payload)
        if not payload["ad_ids"]:
            return ToolResult(ok=False, error={"message": "ad_ids (or ad_id) is required"})
        request = AdCopyAnalysisRequest.model_validate(payload)
        data = await engine.analyze_ad_copy_performance(request)
        return ToolResult(ok=True, data=_to_payload(data))
    except Exception as exc:
        logger.error("tool_analyze_ad_copy_performance_failed", error=str(exc))
        return ToolResult(ok=False, error={"message": str(exc)})

async def score_visual_elements_handler(client: MetaClient, arguments: Dict[str, Any]) -> ToolResult:
    try:
        from ..creative_intelligence import CreativeIntelligenceEngine
        engine = CreativeIntelligenceEngine(client)
        payload = dict(arguments)
        payload.setdefault("time_range", _default_time_range())
        payload["ad_id"] = _ensure_single_ad_id(payload)
        if not payload["ad_id"]:
            return ToolResult(ok=False, error={"message": "ad_id (or ad_ids) is required"})
        request = VisualElementScorerRequest.model_validate(payload)
        data = await engine.score_visual_elements(request)
        return ToolResult(ok=True, data=_to_payload(data))
    except Exception as exc:
        logger.error("tool_score_visual_elements_failed", error=str(exc))
        return ToolResult(ok=False, error={"message": str(exc)})

async def analyze_dco_performance_handler(client: MetaClient, arguments: Dict[str, Any]) -> ToolResult:
    try:
        from ..creative_intelligence import CreativeIntelligenceEngine
        engine = CreativeIntelligenceEngine(client)
        payload = dict(arguments)
        payload.setdefault("time_range", _default_time_range())
        payload["ad_id"] = _ensure_single_ad_id(payload)
        if not payload["ad_id"]:
            return ToolResult(ok=False, error={"message": "ad_id (or ad_ids) is required"})
        request = DCOAnalysisRequest.model_validate(payload)
        data = await engine.analyze_dco_performance(request)
        return ToolResult(ok=True, data=_to_payload(data))
    except Exception as exc:
        logger.error("tool_analyze_dco_performance_failed", error=str(exc))
        return ToolResult(ok=False, error={"message": str(exc)})

async def analyze_creative_testing_stats_handler(client: MetaClient, arguments: Dict[str, Any]) -> ToolResult:
    try:
        from ..creative_intelligence import CreativeIntelligenceEngine
        engine = CreativeIntelligenceEngine(client)
        payload = dict(arguments)
        payload.setdefault("time_range", _default_time_range())
        payload["ad_ids"] = _ensure_ad_ids(payload)
        if not payload["ad_ids"]:
            return ToolResult(ok=False, error={"message": "ad_ids (or ad_id) is required"})
        request = CreativeTestingStatsRequest.model_validate(payload)
        data = await engine.analyze_creative_testing_stats(request)
        return ToolResult(ok=True, data=_to_payload(data))
    except Exception as exc:
        logger.error("tool_analyze_creative_testing_stats_failed", error=str(exc))
        return ToolResult(ok=False, error={"message": str(exc)})

async def score_message_market_match_handler(client: MetaClient, arguments: Dict[str, Any]) -> ToolResult:
    try:
        from ..creative_intelligence import CreativeIntelligenceEngine
        engine = CreativeIntelligenceEngine(client)
        payload = dict(arguments)
        payload.setdefault("time_range", _default_time_range())
        payload["ad_id"] = _ensure_single_ad_id(payload)
        if not payload["ad_id"]:
            return ToolResult(ok=False, error={"message": "ad_id (or ad_ids) is required"})
        request = MessageMarketMatchRequest.model_validate(payload)
        data = await engine.score_message_market_match(request)
        return ToolResult(ok=True, data=_to_payload(data))
    except Exception as exc:
        logger.error("tool_score_message_market_match_failed", error=str(exc))
        return ToolResult(ok=False, error={"message": str(exc)})

async def analyze_emotional_appeal_handler(client: MetaClient, arguments: Dict[str, Any]) -> ToolResult:
    try:
        from ..creative_intelligence import CreativeIntelligenceEngine
        engine = CreativeIntelligenceEngine(client)
        payload = dict(arguments)
        payload.setdefault("time_range", _default_time_range())
        payload["ad_id"] = _ensure_single_ad_id(payload)
        if not payload["ad_id"]:
            return ToolResult(ok=False, error={"message": "ad_id (or ad_ids) is required"})
        request = EmotionalAppealRequest.model_validate(payload)
        data = await engine.analyze_emotional_appeal(request)
        return ToolResult(ok=True, data=_to_payload(data))
    except Exception as exc:
        logger.error("tool_analyze_emotional_appeal_failed", error=str(exc))
        return ToolResult(ok=False, error={"message": str(exc)})

async def benchmark_creative_against_market_handler(client: MetaClient, arguments: Dict[str, Any]) -> ToolResult:
    try:
        from ..creative_intelligence import CreativeIntelligenceEngine
        engine = CreativeIntelligenceEngine(client)
        payload = dict(arguments)
        payload.setdefault("time_range", _default_time_range())
        payload["ad_id"] = _ensure_single_ad_id(payload)
        if not payload["ad_id"]:
            return ToolResult(ok=False, error={"message": "ad_id (or ad_ids) is required"})
        request = CreativeBenchmarkRequest.model_validate(payload)
        data = await engine.benchmark_creative_against_market(request)
        return ToolResult(ok=True, data=_to_payload(data))
    except Exception as exc:
        logger.error("tool_benchmark_creative_against_market_failed", error=str(exc))
        return ToolResult(ok=False, error={"message": str(exc)})

async def compare_cross_format_performance_handler(client: MetaClient, arguments: Dict[str, Any]) -> ToolResult:
    try:
        from ..creative_intelligence import CreativeIntelligenceEngine
        engine = CreativeIntelligenceEngine(client)
        payload = dict(arguments)
        payload.setdefault("time_range", _default_time_range())
        payload["ad_ids"] = _ensure_ad_ids(payload)
        if not payload["ad_ids"]:
            return ToolResult(ok=False, error={"message": "ad_ids (or ad_id) is required"})
        request = CrossFormatComparisonRequest.model_validate(payload)
        data = await engine.compare_cross_format_performance(request)
        return ToolResult(ok=True, data=_to_payload(data))
    except Exception as exc:
        logger.error("tool_compare_cross_format_performance_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.upload_creative_asset": (
            mcp_types.Tool(
                name="meta.upload_creative_asset",
                description="Upload images or videos to Meta Ads creative library. Supports JPG, PNG, MP4, and MOV formats. Returns asset hash for ad creation. Use for adding new creative assets.",
                inputSchema=_tool_schema(UploadCreativeAssetRequest),
            ),
            partial(upload_creative_asset_handler, client),
        ),
        "meta.get_creative_assets": (
            mcp_types.Tool(
                name="meta.get_creative_assets",
                description="List and search creative assets in the ad account library. Filter by type (image/video), name, and creation date. Use for asset management and creative inventory review.",
                inputSchema=_tool_schema(GetCreativeAssetsRequest),
            ),
            partial(get_creative_assets_handler, client),
        ),
        "meta.creative.analyze_copy": (
            mcp_types.Tool(
                name="meta.creative.analyze_copy",
                description="Analyze ad copy performance across headlines, body text, and CTAs. Identifies top-performing messaging patterns, word combinations, and emotional triggers. Use for copywriting optimization.",
                inputSchema={
                    "type": "object",
                    "properties": {
                        "ad_account_id": {"type": "string"},
                    },
                    "required": ["ad_account_id"],
                },
            ),
            partial(analyze_ad_copy_performance_handler, client),
        ),
        "meta.creative.score_visuals": (
            mcp_types.Tool(
                name="meta.creative.score_visuals",
                description="Score visual elements of ad creatives including images, colors, composition, and branding. AI-powered analysis of visual appeal and attention capture. Use for design optimization.",
                inputSchema={
                    "type": "object",
                    "properties": {
                        "ad_account_id": {"type": "string"},
                        "ad_id": {"type": "string"},
                    },
                    "required": ["ad_account_id"],
                },
            ),
            partial(score_visual_elements_handler, client),
        ),
        "meta.creative.analyze_dco": (
            mcp_types.Tool(
                name="meta.creative.analyze_dco",
                description="Analyze Dynamic Creative Optimization (DCO) performance. Compare element combinations, identify winning variants, and optimize asset rotation. Use for Advantage+ Creative insights.",
                inputSchema={
                    "type": "object",
                    "properties": {
                        "ad_account_id": {"type": "string"},
                    },
                    "required": ["ad_account_id"],
                },
            ),
            partial(analyze_dco_performance_handler, client),
        ),
        "meta.creative.analyze_testing": (
            mcp_types.Tool(
                name="meta.creative.analyze_testing",
                description="Analyze A/B creative testing results with statistical significance. Compare variant performance, calculate lift, and declare winners. Use for structured creative experiments.",
                inputSchema={
                    "type": "object",
                    "properties": {
                        "ad_account_id": {"type": "string"},
                        "test_name": {"type": "string"},
                    },
                    "required": ["ad_account_id"],
                },
            ),
            partial(analyze_creative_testing_stats_handler, client),
        ),
        "meta.creative.score_message_match": (
            mcp_types.Tool(
                name="meta.creative.score_message_match",
                description="Score message-market match between ad creative and target audience. Analyzes relevance, alignment with audience interests, and messaging resonance. Use for targeting-creative alignment.",
                inputSchema={
                    "type": "object",
                    "properties": {
                        "ad_account_id": {"type": "string"},
                        "ad_id": {"type": "string"},
                    },
                    "required": ["ad_account_id"],
                },
            ),
            partial(score_message_market_match_handler, client),
        ),
        "meta.creative.analyze_emotional_appeal": (
            mcp_types.Tool(
                name="meta.creative.analyze_emotional_appeal",
                description="Analyze emotional appeal and sentiment of ad creatives. Identifies fear, joy, urgency, trust, and other emotional triggers. Use for psychological messaging optimization.",
                inputSchema={
                    "type": "object",
                    "properties": {
                        "ad_account_id": {"type": "string"},
                        "ad_id": {"type": "string"},
                    },
                    "required": ["ad_account_id"],
                },
            ),
            partial(analyze_emotional_appeal_handler, client),
        ),
        "meta.creative.benchmark": (
            mcp_types.Tool(
                name="meta.creative.benchmark",
                description="Benchmark creative performance against industry standards. Compare CTR, engagement, and conversion rates to vertical averages. Use for competitive creative analysis.",
                inputSchema={
                    "type": "object",
                    "properties": {
                        "ad_account_id": {"type": "string"},
                        "industry": {"type": "string"},
                    },
                    "required": ["ad_account_id", "industry"],
                },
            ),
            partial(benchmark_creative_against_market_handler, client),
        ),
        "meta.creative.compare_formats": (
            mcp_types.Tool(
                name="meta.creative.compare_formats",
                description="Compare performance across creative formats: static images, carousels, videos, stories, and reels. Identifies best-performing format for each objective. Use for format strategy decisions.",
                inputSchema={
                    "type": "object",
                    "properties": {
                        "ad_account_id": {"type": "string"},
                    },
                    "required": ["ad_account_id"],
                },
            ),
            partial(compare_cross_format_performance_handler, client),
        ),
    }
