"""
SQLAlchemy storage implementation for news cards.
"""

from typing import List, Optional, Dict, Any
from sqlalchemy.orm import Session
from sqlalchemy import desc
from loguru import logger

from .storage import CardStorage
from ...database.models.news import NewsCard
from .base_card import CardVersion


class SQLCardStorage(CardStorage):
    """SQLAlchemy implementation of card storage"""

    def __init__(self, session: Session):
        """Initialize with a database session from the user's encrypted database"""
        if not session:
            raise ValueError("Session is required for SQLCardStorage")
        self._session = session

    @property
    def session(self):
        """Get database session"""
        return self._session

    def create(self, data: Dict[str, Any]) -> str:
        """Create a new card"""
        card_id = data.get("id") or self.generate_id()

        # Extract source info if it's nested
        source_info = data.get("source", {})
        if isinstance(source_info, dict):
            source_type = source_info.get("type")
            source_id = source_info.get("source_id")
            created_from = source_info.get("created_from")
        else:
            source_type = data.get("source_type")
            source_id = data.get("source_id")
            created_from = data.get("created_from")

        with self.session as session:
            card = NewsCard(
                id=card_id,
                user_id=data["user_id"],
                topic=data["topic"],
                card_type=data.get("card_type", data.get("type", "news")),
                source_type=source_type,
                source_id=source_id,
                created_from=created_from,
                parent_card_id=data.get("parent_card_id"),
            )

            session.add(card)
            session.commit()

            logger.info(f"Created card {card_id} for user {data['user_id']}")
            return card_id

    def get(self, id: str) -> Optional[Dict[str, Any]]:
        """Get a card by ID"""
        with self.session as session:
            card = session.query(NewsCard).filter_by(id=id).first()
            return card.to_dict() if card else None

    def update(self, id: str, data: Dict[str, Any]) -> bool:
        """Update a card"""
        with self.session as session:
            card = session.query(NewsCard).filter_by(id=id).first()
            if not card:
                return False

            # Update allowed fields
            updateable_fields = ["is_archived", "is_pinned", "last_viewed"]
            for field in updateable_fields:
                if field in data:
                    setattr(card, field, data[field])

            session.commit()
            return True

    def delete(self, id: str) -> bool:
        """Delete a card (and all its versions due to cascade)"""
        with self.session as session:
            card = session.query(NewsCard).filter_by(id=id).first()
            if not card:
                return False

            session.delete(card)
            session.commit()
            return True

    def list(
        self,
        filters: Optional[Dict[str, Any]] = None,
        limit: int = 100,
        offset: int = 0,
    ) -> List[Dict[str, Any]]:
        """List cards with optional filtering"""
        with self.session as session:
            query = session.query(NewsCard)

            if filters:
                if "user_id" in filters:
                    query = query.filter_by(user_id=filters["user_id"])
                if "card_type" in filters:
                    query = query.filter_by(card_type=filters["card_type"])
                if "is_archived" in filters:
                    query = query.filter_by(is_archived=filters["is_archived"])
                if "is_pinned" in filters:
                    query = query.filter_by(is_pinned=filters["is_pinned"])

            # Order by creation date (newest first)
            query = query.order_by(desc(NewsCard.created_at))

            cards = query.limit(limit).offset(offset).all()
            return [card.to_dict() for card in cards]

    def get_by_user(
        self, user_id: str, limit: int = 50, offset: int = 0
    ) -> List[Dict[str, Any]]:
        """Get cards for a specific user"""
        filters = {"user_id": user_id, "is_archived": False}
        return self.list(filters, limit, offset)

    def get_latest_version(self, card_id: str) -> Optional[Dict[str, Any]]:
        """Get the latest version of a card"""
        with self.session as session:
            version = (
                session.query(CardVersion)
                .filter_by(card_id=card_id)
                .order_by(desc(CardVersion.version_number))
                .first()
            )

            return version.to_dict() if version else None

    def add_version(self, card_id: str, version_data: Dict[str, Any]) -> str:
        """Add a new version to a card"""
        version_id = version_data.get("id") or self.generate_id()

        with self.session as session:
            # Get the card and current version count
            card = session.query(NewsCard).filter_by(id=card_id).first()
            if not card:
                raise ValueError(f"Card {card_id} not found")

            # Get next version number
            current_max = (
                session.query(CardVersion).filter_by(card_id=card_id).count()
            )
            version_number = current_max + 1

            # Create new version
            version = CardVersion(
                id=version_id,
                card_id=card_id,
                version_number=version_number,
                search_query=version_data.get("search_query"),
                research_result=version_data.get("research_result"),
                headline=version_data.get("headline"),
                summary=version_data.get("summary"),
                findings=version_data.get("findings"),
                sources=version_data.get("sources"),
                impact_score=version_data.get("impact_score"),
                topics=version_data.get("topics"),
                entities=version_data.get("entities"),
                embedding=version_data.get("embedding"),
            )

            session.add(version)

            # Update card's latest info
            card.latest_version_id = version_id
            card.latest_headline = version_data.get("headline")
            card.latest_summary = version_data.get("summary")
            card.latest_impact_score = version_data.get("impact_score")

            session.commit()

            logger.info(f"Added version {version_number} to card {card_id}")
            return version_id

    def update_latest_info(
        self, card_id: str, version_data: Dict[str, Any]
    ) -> bool:
        """Update the denormalized latest version info on the card"""
        with self.session as session:
            card = session.query(NewsCard).filter_by(id=card_id).first()
            if not card:
                return False

            card.latest_version_id = version_data.get("id")
            card.latest_headline = version_data.get("headline")
            card.latest_summary = version_data.get("summary")
            card.latest_impact_score = version_data.get("impact_score")

            session.commit()
            return True

    def archive_card(self, card_id: str) -> bool:
        """Archive a card"""
        return self.update(card_id, {"is_archived": True})

    def pin_card(self, card_id: str, pinned: bool = True) -> bool:
        """Pin or unpin a card"""
        return self.update(card_id, {"is_pinned": pinned})
