"""
Base card class for all news-related content.
Following LDR's pattern from BaseSearchStrategy.
"""

from abc import ABC, abstractmethod
from dataclasses import dataclass, field
from datetime import datetime
from typing import Dict, List, Optional, Any

from .utils import generate_card_id, utc_now
# Storage will be imported when needed to avoid circular import


@dataclass
class CardSource:
    """Tracks the origin of each card."""

    type: str  # "news_item", "user_search", "subscription", "news_research"
    source_id: Optional[str] = None
    created_from: str = ""
    metadata: Dict[str, Any] = field(default_factory=dict)


@dataclass
class CardVersion:
    """Represents a version of research/content for a card."""

    version_id: str
    created_at: datetime
    content: Dict[str, Any]  # The actual research results
    query_used: str
    search_strategy: Optional[str] = None

    def __post_init__(self):
        if not self.version_id:
            self.version_id = generate_card_id()


@dataclass
class BaseCard(ABC):
    """
    Abstract base class for all card types.
    Following LDR's pattern of base classes with common functionality.
    """

    # Required fields
    topic: str
    source: CardSource
    user_id: str

    # Optional fields with defaults
    card_id: Optional[str] = None
    parent_card_id: Optional[str] = None
    metadata: Dict[str, Any] = field(default_factory=dict)

    # Automatically generated fields
    id: str = field(init=False)
    created_at: datetime = field(init=False)
    updated_at: datetime = field(init=False)
    versions: List[CardVersion] = field(default_factory=list, init=False)
    interaction: Dict[str, Any] = field(init=False)

    # Storage and callback fields
    storage: Optional[Any] = field(default=None, init=False)
    progress_callback: Optional[Any] = field(default=None, init=False)

    def __post_init__(self):
        """Initialize generated fields after dataclass initialization."""
        self.id = self.card_id or generate_card_id()
        self.created_at = utc_now()
        self.updated_at = utc_now()
        self.interaction = {
            "votes_up": 0,
            "votes_down": 0,
            "views": 0,
            "shares": 0,
        }

    def set_progress_callback(self, callback) -> None:
        """Set a callback function to receive progress updates."""
        self.progress_callback = callback

    def _update_progress(
        self,
        message: str,
        progress_percent: Optional[int] = None,
        metadata: Optional[dict] = None,
    ) -> None:
        """Send a progress update via the callback if available."""
        if self.progress_callback:
            self.progress_callback(message, progress_percent, metadata or {})

    def save(self) -> str:
        """Save card to database"""
        if self.storage is None:
            raise RuntimeError("Storage must be set before calling save()")
        card_data = {
            "id": self.id,
            "user_id": self.user_id,
            "topic": self.topic,
            "card_type": self.get_card_type(),
            "source_type": self.source.type,
            "source_id": self.source.source_id,
            "created_from": self.source.created_from,
            "parent_card_id": self.parent_card_id,
            "metadata": self.metadata,
        }
        result: str = self.storage.create(card_data)
        return result

    def add_version(
        self, research_results: Dict[str, Any], query: str, strategy: str
    ) -> str:
        """Add a new version with research results"""
        version_data = {
            "search_query": query,
            "research_result": research_results,
            "headline": self._extract_headline(research_results),
            "summary": self._extract_summary(research_results),
            "findings": research_results.get("findings", []),
            "sources": research_results.get("sources", []),
            "impact_score": self._calculate_impact(research_results),
            "topics": self._extract_topics(research_results),
            "entities": self._extract_entities(research_results),
            "strategy": strategy,
        }

        if self.storage is None:
            raise RuntimeError(
                "Storage must be set before calling add_version()"
            )
        version_id: str = self.storage.add_version(self.id, version_data)

        # Add to local versions list
        version = CardVersion(
            version_id=version_id,
            created_at=utc_now(),
            content=research_results,
            query_used=query,
            search_strategy=strategy,
        )
        self.versions.append(version)
        self.updated_at = utc_now()

        return version_id

    def get_latest_version(self) -> Optional[CardVersion]:
        """Get the most recent version of this card."""
        if not self.versions:
            return None
        return max(self.versions, key=lambda v: v.created_at)

    def to_base_dict(self) -> Dict[str, Any]:
        """Convert base card attributes to dictionary."""
        return {
            "id": self.id,
            "topic": self.topic,
            "user_id": self.user_id,
            "created_at": self.created_at.isoformat()
            if self.created_at
            else None,
            "updated_at": self.updated_at.isoformat()
            if self.updated_at
            else None,
            "source": {
                "type": self.source.type,
                "source_id": self.source.source_id,
                "created_from": self.source.created_from,
                "metadata": self.source.metadata,
            },
            "versions_count": len(self.versions),
            "parent_card_id": self.parent_card_id,
            "metadata": self.metadata,
            "interaction": self.interaction,
            "card_type": self.get_card_type(),
        }

    @abstractmethod
    def get_card_type(self) -> str:
        """Return the card type (news, research, update, overview)"""
        pass

    @abstractmethod
    def to_dict(self) -> Dict[str, Any]:
        """
        Convert card to dictionary representation.
        Must be implemented by subclasses.
        """
        pass

    # Helper methods for extracting data from research results
    def _extract_headline(self, result: Dict[str, Any]) -> str:
        """Extract headline from research result"""
        # Try different possible fields
        return str(
            result.get("headline")
            or result.get("title")
            or result.get("query", "")[:100]
        )

    def _extract_summary(self, result: Dict[str, Any]) -> str:
        """Extract summary from research result"""
        return str(
            result.get("summary")
            or result.get("current_knowledge")
            or result.get("formatted_findings", "")[:500]
        )

    def _calculate_impact(self, result: Dict[str, Any]) -> int:
        """Calculate impact score (1-10)"""
        # Simple heuristic based on findings count and sources
        findings_count = len(result.get("findings", []))
        sources_count = len(result.get("sources", []))

        score = min(10, 5 + (findings_count // 5) + (sources_count // 3))
        return max(1, score)

    def _extract_topics(self, result: Dict[str, Any]) -> List[str]:
        """Extract topics from research result"""
        # Could be enhanced with NLP
        topics: List[str] = result.get("topics", [])
        if not topics and "query" in result:
            # Simple keyword extraction from query
            words = result["query"].lower().split()
            topics = [w for w in words if len(w) > 4][:5]
        return topics

    def _extract_entities(self, result: Dict[str, Any]) -> Dict[str, List[str]]:
        """Extract entities from research result"""
        # Placeholder - would use NER in production
        entities: Dict[str, List[str]] = result.get(
            "entities", {"people": [], "places": [], "organizations": []}
        )
        return entities


@dataclass
class NewsCard(BaseCard):
    """Card representing a news item with potential for research."""

    # News-specific fields with defaults
    headline: str = ""
    summary: str = ""
    category: str = "General"
    impact_score: int = 5
    entities: Dict[str, List[str]] = field(
        default_factory=lambda: {
            "people": [],
            "places": [],
            "organizations": [],
        }
    )
    topics_extracted: List[str] = field(default_factory=list)
    is_developing: bool = False
    time_ago: str = "recent"
    source_url: str = ""
    analysis: str = ""
    surprising_element: Optional[str] = None

    def __post_init__(self):
        """Initialize parent and set headline default."""
        super().__post_init__()
        if not self.headline:
            self.headline = self.topic

    def get_card_type(self) -> str:
        """Return the card type"""
        return "news"

    def to_dict(self) -> Dict[str, Any]:
        data = self.to_base_dict()
        data.update(
            {
                "headline": self.headline,
                "summary": self.summary,
                "category": self.category,
                "impact_score": self.impact_score,
                "entities": self.entities,
                "topics_extracted": self.topics_extracted,
                "is_developing": self.is_developing,
                "time_ago": self.time_ago,
            }
        )
        return data


@dataclass
class ResearchCard(BaseCard):
    """Card representing deeper research on a topic."""

    # Research-specific fields
    research_depth: str = "quick"  # "quick", "detailed", "report"
    key_findings: List[str] = field(default_factory=list)
    sources_count: int = 0

    def get_card_type(self) -> str:
        """Return the card type"""
        return "research"

    def to_dict(self) -> Dict[str, Any]:
        data = self.to_base_dict()
        data.update(
            {
                "research_depth": self.research_depth,
                "key_findings": self.key_findings,
                "sources_count": self.sources_count,
            }
        )
        return data


@dataclass
class UpdateCard(BaseCard):
    """Card representing updates or notifications."""

    # Update-specific fields
    update_type: str = "new_stories"  # "new_stories", "breaking", "follow_up"
    count: int = 0
    preview_items: List[Any] = field(default_factory=list)
    since: datetime = field(init=False)

    def __post_init__(self):
        """Initialize parent and set since timestamp."""
        super().__post_init__()
        self.since = utc_now()

    def get_card_type(self) -> str:
        """Return the card type"""
        return "update"

    def to_dict(self) -> Dict[str, Any]:
        data = self.to_base_dict()
        data.update(
            {
                "update_type": self.update_type,
                "count": self.count,
                "preview_items": self.preview_items,
                "since": self.since.isoformat(),
            }
        )
        return data


@dataclass
class OverviewCard(BaseCard):
    """Special card type for dashboard/overview display."""

    # Override topic default for overview cards
    topic: str = field(default="News Overview", init=False)

    # Overview-specific fields
    stats: Dict[str, Any] = field(
        default_factory=lambda: {
            "total_new": 0,
            "breaking": 0,
            "relevant": 0,
            "categories": {},
        }
    )
    summary: str = ""
    top_stories: List[Any] = field(default_factory=list)
    trend_analysis: str = ""

    def get_card_type(self) -> str:
        """Return the card type"""
        return "overview"

    def to_dict(self) -> Dict[str, Any]:
        data = self.to_base_dict()
        data.update(
            {
                "stats": self.stats,
                "summary": self.summary,
                "top_stories": self.top_stories,
                "trend_analysis": self.trend_analysis,
            }
        )
        return data
