"""Service for managing custom symbol dictionaries."""

from dataclasses import dataclass
from typing import Optional
from uuid import UUID

import structlog
from sqlalchemy import and_, delete, select
from sqlalchemy.ext.asyncio import AsyncSession

from src.db.models import Document, SymbolDictionary

logger = structlog.get_logger(__name__)

# Context value for custom symbols uploaded via API
CUSTOM_SYMBOL_CONTEXT = "api_upload"


@dataclass
class SymbolUploadResult:
    """Result of symbol upload operation.

    Attributes:
        document_id: UUID of the document.
        symbols_added: Number of new symbols added.
        symbols_updated: Number of existing symbols updated.
        reprocessing_triggered: Whether re-resolution was triggered.
    """

    document_id: UUID
    symbols_added: int
    symbols_updated: int
    reprocessing_triggered: bool

    def to_dict(self) -> dict:
        """Convert to dictionary for JSON serialization."""
        return {
            "document_id": str(self.document_id),
            "symbols_added": self.symbols_added,
            "symbols_updated": self.symbols_updated,
            "reprocessing_triggered": self.reprocessing_triggered,
        }


@dataclass
class SymbolDeleteResult:
    """Result of symbol deletion operation.

    Attributes:
        document_id: UUID of the document.
        symbol: The symbol that was deleted.
        deleted: Whether the symbol was found and deleted.
        reprocessing_triggered: Whether re-resolution was triggered.
    """

    document_id: UUID
    symbol: str
    deleted: bool
    reprocessing_triggered: bool

    def to_dict(self) -> dict:
        """Convert to dictionary for JSON serialization."""
        return {
            "document_id": str(self.document_id),
            "symbol": self.symbol,
            "deleted": self.deleted,
            "reprocessing_triggered": self.reprocessing_triggered,
        }


class SymbolService:
    """Service for managing custom symbol dictionaries.

    This service handles:
    - Uploading custom symbols for a document
    - Custom symbols override auto-detected symbols with same key
    - Deleting custom symbols
    - Triggering re-resolution when document is already processed
    """

    async def upload_custom_symbols(
        self,
        document_id: UUID,
        symbols: list[dict],
        session: AsyncSession,
    ) -> SymbolUploadResult:
        """Upload custom symbol definitions for a document.

        Custom symbols are marked with context="api_upload" to distinguish
        them from auto-detected symbols. If a custom symbol already exists,
        it will be updated.

        Args:
            document_id: UUID of the document to add symbols to.
            symbols: List of dicts with 'symbol' and 'meaning' keys.
            session: Async database session.

        Returns:
            SymbolUploadResult with upload statistics.
        """
        logger.info(
            "custom_symbol_upload_started",
            document_id=str(document_id),
            symbol_count=len(symbols),
        )

        symbols_added = 0
        symbols_updated = 0

        for symbol_data in symbols:
            symbol = symbol_data["symbol"]
            meaning = symbol_data["meaning"]

            # Check if custom symbol already exists for this document
            existing = await session.execute(
                select(SymbolDictionary).where(
                    and_(
                        SymbolDictionary.document_id == document_id,
                        SymbolDictionary.symbol == symbol,
                        SymbolDictionary.context == CUSTOM_SYMBOL_CONTEXT,
                    )
                )
            )
            existing_symbol = existing.scalar_one_or_none()

            if existing_symbol:
                # Update existing custom symbol
                existing_symbol.meaning = meaning
                symbols_updated += 1
                logger.debug(
                    "custom_symbol_updated",
                    document_id=str(document_id),
                    symbol=symbol,
                )
            else:
                # Create new custom symbol
                new_symbol = SymbolDictionary(
                    document_id=document_id,
                    symbol=symbol,
                    meaning=meaning,
                    context=CUSTOM_SYMBOL_CONTEXT,
                    sheet_name=None,  # Custom symbols are not from a specific sheet
                )
                session.add(new_symbol)
                symbols_added += 1
                logger.debug(
                    "custom_symbol_added",
                    document_id=str(document_id),
                    symbol=symbol,
                )

        await session.commit()

        logger.info(
            "custom_symbol_upload_completed",
            document_id=str(document_id),
            symbols_added=symbols_added,
            symbols_updated=symbols_updated,
        )

        return SymbolUploadResult(
            document_id=document_id,
            symbols_added=symbols_added,
            symbols_updated=symbols_updated,
            reprocessing_triggered=False,  # Will be set by caller if needed
        )

    async def delete_custom_symbol(
        self,
        document_id: UUID,
        symbol: str,
        session: AsyncSession,
    ) -> SymbolDeleteResult:
        """Delete a custom symbol from a document.

        Only custom symbols (context="api_upload") can be deleted.
        Auto-detected symbols cannot be deleted via this method.

        Args:
            document_id: UUID of the document.
            symbol: The symbol string to delete.
            session: Async database session.

        Returns:
            SymbolDeleteResult with deletion status.
        """
        logger.info(
            "custom_symbol_deletion_started",
            document_id=str(document_id),
            symbol=symbol,
        )

        # Delete the custom symbol
        result = await session.execute(
            delete(SymbolDictionary).where(
                and_(
                    SymbolDictionary.document_id == document_id,
                    SymbolDictionary.symbol == symbol,
                    SymbolDictionary.context == CUSTOM_SYMBOL_CONTEXT,
                )
            )
        )

        deleted = result.rowcount > 0
        await session.commit()

        if deleted:
            logger.info(
                "custom_symbol_deleted",
                document_id=str(document_id),
                symbol=symbol,
            )
        else:
            logger.warning(
                "custom_symbol_not_found",
                document_id=str(document_id),
                symbol=symbol,
            )

        return SymbolDeleteResult(
            document_id=document_id,
            symbol=symbol,
            deleted=deleted,
            reprocessing_triggered=False,  # Will be set by caller if needed
        )

    async def get_document_symbols(
        self,
        document_id: UUID,
        session: AsyncSession,
    ) -> list[dict]:
        """Get all symbols for a document.

        Returns both custom and auto-detected symbols.

        Args:
            document_id: UUID of the document.
            session: Async database session.

        Returns:
            List of symbol dicts with 'symbol', 'meaning', 'is_custom' keys.
        """
        result = await session.execute(
            select(SymbolDictionary)
            .where(SymbolDictionary.document_id == document_id)
            .order_by(SymbolDictionary.symbol)
        )
        symbols = result.scalars().all()

        return [
            {
                "symbol": s.symbol,
                "meaning": s.meaning,
                "is_custom": s.context == CUSTOM_SYMBOL_CONTEXT,
                "sheet_name": s.sheet_name,
            }
            for s in symbols
        ]

    async def check_document_needs_reprocessing(
        self,
        document_id: UUID,
        session: AsyncSession,
    ) -> bool:
        """Check if a document is already processed and needs re-resolution.

        Args:
            document_id: UUID of the document.
            session: Async database session.

        Returns:
            True if document status is "completed" (needs re-resolution).
        """
        result = await session.execute(
            select(Document.status).where(Document.id == document_id)
        )
        status = result.scalar_one_or_none()
        return status == "completed"

    async def get_document(
        self,
        document_id: UUID,
        session: AsyncSession,
    ) -> Optional[Document]:
        """Get a document by ID.

        Args:
            document_id: UUID of the document.
            session: Async database session.

        Returns:
            Document or None if not found.
        """
        result = await session.execute(
            select(Document).where(Document.id == document_id)
        )
        return result.scalar_one_or_none()


# Create singleton instance for dependency injection
symbol_service = SymbolService()
