from pydantic import BaseModel, Field, field_validator
from typing import Optional, List, Literal, Union
from datetime import datetime


class User(BaseModel):
    """User information for forecasts and comments"""

    id: Optional[Union[str, int]] = None
    name: str

    @field_validator("id")
    @classmethod
    def convert_id_to_string(cls, v):
        """Convert integer IDs to strings"""
        return str(v) if v is not None else v


class Tag(BaseModel):
    """Tag model for question categorization"""

    id: Optional[Union[str, int]] = None
    name: str

    @field_validator("id")
    @classmethod
    def convert_id_to_string(cls, v):
        """Convert integer IDs to strings"""
        return str(v) if v is not None else v


class Forecast(BaseModel):
    """Forecast made on a question"""

    id: Optional[Union[str, int]] = None
    forecast: float = Field(ge=0, le=1, description="Forecast value between 0 and 1")
    user: User
    created_at: Optional[datetime] = Field(None, alias="createdAt")
    option_id: Optional[Union[str, int]] = Field(
        None, alias="optionId", description="For multi-choice questions"
    )

    @field_validator("id", "option_id")
    @classmethod
    def convert_id_to_string(cls, v):
        """Convert integer IDs to strings"""
        return str(v) if v is not None else v

    class Config:
        populate_by_name = True
        by_alias = True  # Use aliases when serializing


class Comment(BaseModel):
    """Comment on a question"""

    id: Optional[Union[str, int]] = None
    comment: str
    user: User
    created_at: Optional[datetime] = Field(None, alias="createdAt")

    @field_validator("id")
    @classmethod
    def convert_id_to_string(cls, v):
        """Convert integer IDs to strings"""
        return str(v) if v is not None else v

    class Config:
        populate_by_name = True
        by_alias = True  # Use aliases when serializing


class Question(BaseModel):
    """Fatebook question model with optional fields for detailed responses"""

    # Core fields (id is optional since getQuestion doesn't return it)
    id: Optional[str] = None
    title: str
    type: Literal["BINARY", "NUMERIC", "MULTIPLE_CHOICE"] = "BINARY"
    resolved: bool = False

    # Timestamps
    created_at: datetime = Field(alias="createdAt")
    resolve_by: datetime = Field(alias="resolveBy")
    resolved_at: Optional[datetime] = Field(None, alias="resolvedAt")

    # Resolution information
    resolution: Optional[Literal["YES", "NO", "AMBIGUOUS"]] = None

    # Additional content (typically in detailed view)
    notes: Optional[str] = None

    # Related data (typically in detailed view)
    forecasts: Optional[List[Forecast]] = Field(
        default=None, description="List of forecasts on this question"
    )
    tags: Optional[List[Tag]] = Field(default=None, description="Tags associated with the question")
    comments: Optional[List[Comment]] = Field(default=None, description="Comments on the question")

    # Visibility settings (typically in detailed view)
    shared_publicly: Optional[bool] = Field(None, alias="sharedPublicly")
    unlisted: Optional[bool] = None
    hide_forecasts_until: Optional[datetime] = Field(None, alias="hideForecastsUntil")
    share_with_lists: Optional[List[str]] = Field(None, alias="shareWithLists")
    share_with_email: Optional[List[str]] = Field(None, alias="shareWithEmail")

    # Additional fields from getQuestion endpoint
    your_latest_prediction: Optional[str] = Field(None, alias="yourLatestPrediction")
    question_scores: Optional[List] = Field(None, alias="questionScores")

    class Config:
        populate_by_name = True
        by_alias = True  # Use aliases when serializing

    # Computed properties
    @property
    def forecast_count(self) -> int:
        """Number of forecasts on this question"""
        return len(self.forecasts) if self.forecasts else 0

    @property
    def status_text(self) -> str:
        """Human-readable status string"""
        if self.resolved:
            return f"✅ RESOLVED ({self.resolution})"
        return "⏳ OPEN"

    def format_short(self) -> str:
        """Format for list display"""
        tags_text = ", ".join([tag.name for tag in self.tags]) if self.tags else ""
        tags_display = f" | Tags: {tags_text}" if tags_text else ""
        forecast_text = (
            f" | {self.forecast_count} forecast{'s' if self.forecast_count != 1 else ''}"
        )

        id_text = f" | ID: {self.id}" if self.id else ""
        return f"**{self.title}**\n{self.status_text}{id_text}{forecast_text}{tags_display}"

    def format_detailed(self) -> str:
        """Format for detailed single question display"""
        lines = [
            f"**{self.title}**",
        ]
        if self.id:
            lines.append(f"ID: {self.id}")
        lines.extend(
            [
                f"Type: {self.type}",
                f"Created: {self.created_at.isoformat()}",
                f"Resolve By: {self.resolve_by.isoformat()}",
            ]
        )

        status = "✅ Resolved" if self.resolved else "⏳ Open"
        if self.resolved and self.resolved_at:
            status += f" as {self.resolution} on {self.resolved_at.isoformat()}"
        lines.append(f"Status: {status}")

        if self.notes:
            lines.append(f"Notes: {self.notes}")

        if self.forecasts:
            lines.append(f"Forecasts ({len(self.forecasts)}):")
            for forecast in self.forecasts:
                lines.append(f"  • {forecast.user.name}: {forecast.forecast:.0%}")

        if self.tags:
            tag_names = [tag.name for tag in self.tags]
            lines.append(f"Tags: {', '.join(tag_names)}")

        if self.comments:
            lines.append(f"Comments ({len(self.comments)}):")
            for comment in self.comments:
                lines.append(f"  • {comment.user.name}: {comment.comment}")

        visibility = []
        if self.shared_publicly:
            visibility.append("Public")
        if self.unlisted:
            visibility.append("Unlisted")
        if visibility:
            lines.append(f"Visibility: {', '.join(visibility)}")

        return "\n".join(lines)


class QuestionsResponse(BaseModel):
    """Response from getQuestions endpoint"""

    items: List[Question]
    cursor: Optional[str] = None


class QuestionsList(BaseModel):
    """List of questions for MCP responses - matches expected MCP schema"""

    result: List[Question]

    class Config:
        populate_by_name = True
        by_alias = True


class QuestionReference(BaseModel):
    """Minimal question reference with id and title"""

    id: str
    title: str
