"""Pydantic models for MoEngage Stats API request and response.

These models provide:
- Input validation for API requests
- Structured output for MCP tool responses
- Type safety throughout the application
"""

from datetime import date
from enum import Enum
from typing import Any

from pydantic import BaseModel, Field, field_validator


class AttributionType(str, Enum):
    """Attribution type for campaign statistics."""

    VIEW_THROUGH = "VIEW_THROUGH"
    CLICK_THROUGH = "CLICK_THROUGH"


class MetricType(str, Enum):
    """Metric aggregation type."""

    TOTAL = "TOTAL"
    UNIQUE = "UNIQUE"


class StatsRequest(BaseModel):
    """Request model for MoEngage Stats API.

    Attributes:
        request_id: Unique identifier for this request (for tracking)
        campaign_ids: List of campaign IDs to fetch stats for (max 50)
        start_date: Start date for the stats period (YYYY-MM-DD)
        end_date: End date for the stats period (YYYY-MM-DD)
        attribution_type: Attribution model to use
        metric_type: Whether to return total or unique metrics
    """

    request_id: str = Field(
        default="mcp-stats-request",
        description="Unique identifier for this request",
        max_length=100,
    )
    campaign_ids: list[str] = Field(
        ...,
        description="List of campaign IDs to fetch stats for",
        min_length=1,
        max_length=50,
    )
    start_date: date = Field(
        ...,
        description="Start date for the stats period (YYYY-MM-DD)",
    )
    end_date: date = Field(
        ...,
        description="End date for the stats period (YYYY-MM-DD)",
    )
    attribution_type: AttributionType = Field(
        default=AttributionType.VIEW_THROUGH,
        description="Attribution model to use",
    )
    metric_type: MetricType = Field(
        default=MetricType.TOTAL,
        description="Whether to return total or unique metrics",
    )

    @field_validator("end_date")
    @classmethod
    def validate_date_range(cls, v: date, info: Any) -> date:
        """Ensure end_date is not before start_date."""
        start = info.data.get("start_date")
        if start and v < start:
            raise ValueError("end_date must be on or after start_date")
        return v

    @field_validator("campaign_ids")
    @classmethod
    def validate_campaign_ids(cls, v: list[str]) -> list[str]:
        """Ensure campaign IDs are non-empty strings."""
        validated = []
        for cid in v:
            cid = cid.strip()
            if not cid:
                raise ValueError("Campaign IDs cannot be empty strings")
            validated.append(cid)
        return validated

    def to_api_request(self) -> dict[str, Any]:
        """Convert to MoEngage API request format."""
        return {
            "request_id": self.request_id,
            "campaign_ids": self.campaign_ids,
            "start_date": self.start_date.isoformat(),
            "end_date": self.end_date.isoformat(),
            "attribution_type": self.attribution_type.value,
            "metric_type": self.metric_type.value,
        }


class PerformanceStats(BaseModel):
    """Performance statistics for a campaign variation."""

    attempted: int = Field(default=0, description="Number of attempts")
    sent: int = Field(default=0, description="Number sent")
    failed: int = Field(default=0, description="Number failed")
    impression: int = Field(default=0, description="Number of impressions")
    click: int = Field(default=0, description="Number of clicks")
    ctr: float = Field(default=0.0, description="Click-through rate")
    delivery_rate: float = Field(default=0.0, description="Delivery rate")
    sent_rate: float = Field(default=0.0, description="Sent rate")
    failure_rate: float = Field(default=0.0, description="Failure rate")


class VariationStats(BaseModel):
    """Statistics for a campaign variation."""

    variation_id: str | None = Field(default=None, description="Variation identifier")
    variation_name: str | None = Field(default=None, description="Variation name")
    performance_stats: PerformanceStats = Field(
        default_factory=PerformanceStats,
        description="Performance metrics for this variation",
    )


class CampaignStats(BaseModel):
    """Statistics for a single campaign."""

    campaign_id: str = Field(..., description="Campaign identifier")
    campaign_name: str | None = Field(default=None, description="Campaign name")
    campaign_type: str | None = Field(default=None, description="Campaign type")
    channel: str | None = Field(default=None, description="Campaign channel")
    all_variations: PerformanceStats = Field(
        default_factory=PerformanceStats,
        description="Aggregated stats across all variations",
    )
    variations: list[VariationStats] = Field(
        default_factory=list,
        description="Stats broken down by variation",
    )
    global_control_group: PerformanceStats | None = Field(
        default=None,
        description="Global control group stats",
    )
    campaign_control_group: PerformanceStats | None = Field(
        default=None,
        description="Campaign control group stats",
    )


class StatsResponse(BaseModel):
    """Response model for MoEngage Stats API.

    This is the structured output returned by the MCP tool.
    """

    request_id: str = Field(..., description="Request identifier for tracking")
    success: bool = Field(..., description="Whether the request was successful")
    campaigns: list[CampaignStats] = Field(
        default_factory=list,
        description="Stats for each requested campaign",
    )
    error_message: str | None = Field(
        default=None,
        description="Error message if request failed",
    )
    rate_limit_remaining: int | None = Field(
        default=None,
        description="Remaining API calls in the current minute",
    )


class ErrorResponse(BaseModel):
    """Structured error response for MCP tools."""

    error_code: str = Field(..., description="Error code for categorization")
    error_message: str = Field(..., description="Human-readable error message")
    details: dict[str, Any] | None = Field(
        default=None,
        description="Additional error details",
    )
    retry_after: int | None = Field(
        default=None,
        description="Seconds to wait before retrying (for rate limits)",
    )


# =============================================================================
# Campaign Search API Models (Email, Push, SMS Campaign Details)
# =============================================================================


class CampaignChannel(str, Enum):
    """Campaign channel types."""

    EMAIL = "EMAIL"
    PUSH = "PUSH"
    SMS = "SMS"
    IN_APP = "IN_APP"
    WHATSAPP = "WHATSAPP"
    CARDS = "CARDS"
    ONSITE = "ONSITE"


class CampaignStatus(str, Enum):
    """Campaign status types."""

    DRAFT = "DRAFT"
    SCHEDULED = "SCHEDULED"
    ACTIVE = "ACTIVE"
    PAUSED = "PAUSED"
    COMPLETED = "COMPLETED"
    STOPPED = "STOPPED"


class CampaignDeliveryType(str, Enum):
    """Campaign delivery types."""

    ONE_TIME = "ONE_TIME"
    PERIODIC = "PERIODIC"
    EVENT_TRIGGERED = "EVENT_TRIGGERED"
    BUSINESS_EVENT_TRIGGERED = "BUSINESS_EVENT_TRIGGERED"
    DEVICE_TRIGGERED = "DEVICE_TRIGGERED"
    LOCATION_TRIGGERED = "LOCATION_TRIGGERED"
    BROADCAST_LIVE_ACTIVITY = "BROADCAST_LIVE_ACTIVITY"


class CampaignSearchRequest(BaseModel):
    """Request model for Campaign Search API.

    Used for Get Email/Push/SMS Campaign Details APIs.
    """

    request_id: str = Field(
        default="mcp-campaign-search",
        description="Unique identifier for this request",
    )
    channels: list[CampaignChannel] | None = Field(
        default=None,
        description="List of channels to filter by (EMAIL, PUSH, SMS, etc.)",
    )
    campaign_id: str | None = Field(
        default=None,
        description="Optional campaign ID to fetch specific campaign",
    )
    campaign_name: str | None = Field(
        default=None,
        description="Optional campaign name to filter by",
    )
    campaign_status: list[CampaignStatus] | None = Field(
        default=None,
        description="Optional filter by campaign status",
    )
    delivery_type: list[CampaignDeliveryType] | None = Field(
        default=None,
        description="Optional filter by delivery type (ONE_TIME, PERIODIC, EVENT_TRIGGERED)",
    )
    tags: list[str] | None = Field(
        default=None,
        description="Optional filter by campaign tags",
    )
    created_by: list[str] | None = Field(
        default=None,
        description="Optional filter by creator email IDs",
    )
    include_child_campaigns: bool = Field(
        default=False,
        description="Include child campaigns (periodic children, flow campaign notes)",
    )
    include_archive_campaigns: bool = Field(
        default=False,
        description="Include archived campaigns",
    )
    limit: int = Field(
        default=10,
        ge=1,
        le=15,
        description="Number of campaigns to return per page (max 15)",
    )
    page: int = Field(
        default=1,
        ge=1,
        description="Page number for pagination",
    )

    def to_api_request(self) -> dict[str, Any]:
        """Convert to MoEngage API request format."""
        request: dict[str, Any] = {
            "request_id": self.request_id,
            "limit": self.limit,
            "page": self.page,
            "include_child_campaigns": self.include_child_campaigns,
            "include_archive_campaigns": self.include_archive_campaigns,
        }

        # Build campaign_fields object
        campaign_fields: dict[str, Any] = {}

        if self.channels:
            campaign_fields["channels"] = [c.value for c in self.channels]

        if self.campaign_id:
            campaign_fields["id"] = self.campaign_id

        if self.campaign_name:
            campaign_fields["name"] = self.campaign_name

        if self.campaign_status:
            campaign_fields["status"] = [s.value for s in self.campaign_status]

        if self.delivery_type:
            campaign_fields["delivery_type"] = [d.value for d in self.delivery_type]

        if self.tags:
            campaign_fields["tags"] = self.tags

        if self.created_by:
            campaign_fields["created_by"] = self.created_by

        if campaign_fields:
            request["campaign_fields"] = campaign_fields

        return request


class BasicCampaignDetails(BaseModel):
    """Basic details of a campaign."""

    name: str | None = Field(default=None, description="Campaign name")
    content_type: str | None = Field(default=None, description="Content type")
    team: str | None = Field(default=None, description="Team name")
    tags: list[str] = Field(default_factory=list, description="Campaign tags")
    subscription_category: str | None = Field(
        default=None, description="Subscription category"
    )


class CampaignContentVariation(BaseModel):
    """Campaign content variation details."""

    variation_id: str | None = Field(default=None, description="Variation ID")
    variation_name: str | None = Field(default=None, description="Variation name")
    subject: str | None = Field(default=None, description="Email subject (for EMAIL)")
    preview_text: str | None = Field(default=None, description="Preview text")
    from_name: str | None = Field(default=None, description="From name")
    from_email: str | None = Field(default=None, description="From email address")
    reply_to: str | None = Field(default=None, description="Reply-to email")
    title: str | None = Field(default=None, description="Push title (for PUSH)")
    message: str | None = Field(default=None, description="Message content")


class CampaignDetails(BaseModel):
    """Full campaign details from Campaign Search API."""

    campaign_id: str = Field(..., description="Campaign identifier")
    channel: str | None = Field(default=None, description="Campaign channel")
    platform: str | None = Field(default=None, description="Target platform")
    created_by: str | None = Field(default=None, description="Creator email")
    created_at: str | None = Field(default=None, description="Creation timestamp")
    updated_at: str | None = Field(default=None, description="Last update timestamp")
    campaign_delivery_type: str | None = Field(
        default=None, description="Delivery type (ONE_TIME, PERIODIC, etc.)"
    )
    campaign_status: str | None = Field(default=None, description="Current status")
    campaign_start_time: str | None = Field(default=None, description="Start time")
    campaign_end_time: str | None = Field(default=None, description="End time")
    basic_details: BasicCampaignDetails = Field(
        default_factory=BasicCampaignDetails,
        description="Basic campaign information",
    )
    variations: list[CampaignContentVariation] = Field(
        default_factory=list,
        description="Content variations",
    )
    utm_params: dict[str, str] = Field(
        default_factory=dict,
        description="UTM parameters",
    )
    connector_type: str | None = Field(default=None, description="Connector type")
    connector_name: str | None = Field(default=None, description="Connector name")


class CampaignSearchResponse(BaseModel):
    """Response model for Campaign Search API."""

    request_id: str = Field(..., description="Request identifier")
    success: bool = Field(..., description="Whether the request was successful")
    total_campaigns: int = Field(default=0, description="Total number of campaigns")
    current_page: int = Field(default=1, description="Current page number")
    total_pages: int = Field(default=1, description="Total number of pages")
    campaigns: list[CampaignDetails] = Field(
        default_factory=list,
        description="List of campaign details",
    )
    error_message: str | None = Field(
        default=None,
        description="Error message if request failed",
    )
