"""Import batch processor service."""
import asyncio
from typing import List, Dict, Any, Optional
from datetime import datetime
from sqlalchemy.ext.asyncio import AsyncSession
from sqlalchemy import select, update, insert, delete
from sqlalchemy.sql import func

from app.models.card_import_batch import CardImportBatch
from app.models.user_card_import_record import UserCardImportRecord
from app.models.user_data import UserCardCollection
from app.models.models import MtgonlineCard
from app.services.fuzzy_card_matcher import FuzzyCardMatcher


class ImportBatchProcessor:
    """Process card import batches."""

    @staticmethod
    async def create_batch(
        db: AsyncSession,
        user_id: int,
        filename: str,
        file_type: str,
        file_size: int,
        card_names: List[str]
    ) -> CardImportBatch:
        """Create a new import batch."""
        batch = CardImportBatch(
            user_id=user_id,
            filename=filename,
            file_type=file_type,
            file_size=file_size,
            status="pending",
            total_cards=len(card_names),
        )
        db.add(batch)
        await db.flush()
        return batch

    @staticmethod
    async def process_batch(
        db: AsyncSession,
        batch: CardImportBatch,
        card_names: List[str],
        threshold: float = FuzzyCardMatcher.MANUAL_REVIEW_THRESHOLD
    ) -> Dict[str, Any]:
        """
        Process an import batch.
        
        Args:
            db: Database session
            batch: Import batch to process
            card_names: List of card names from the file
            threshold: Minimum similarity threshold for matching
            
        Returns:
            Dictionary with processing results
        """
        # Update status to processing
        batch.status = "processing"
        await db.flush()

        try:
            # Fetch all cards from database
            stmt = select(MtgonlineCard)
            result = await db.execute(stmt)
            db_cards = result.scalars().all()

            # Build candidate list
            candidate_names = [card.name for card in db_cards if card.name]

            # Perform batch matching
            match_results = FuzzyCardMatcher.batch_match_with_database(
                card_names=card_names,
                db_session=db,
                mtgonline_card_model=MtgonlineCard,
                threshold=threshold
            )

            # Count matches
            matched_count = sum(1 for _, _, matched_name, _, _ in match_results if matched_name)
            unmatched_count = sum(1 for _, _, matched_name, _, _ in match_results if not matched_name)

            # Update batch
            batch.matched_cards = matched_count
            batch.unmatched_cards = unmatched_count
            batch.match_results = match_results
            batch.status = "completed"
            batch.updated_at = func.now()
            await db.flush()

            return {
                "batch_id": batch.id,
                "status": "completed",
                "total_cards": len(card_names),
                "matched_cards": matched_count,
                "unmatched_cards": unmatched_count,
                "match_results": match_results,
            }

        except Exception as e:
            batch.status = "failed"
            batch.error_message = str(e)
            batch.updated_at = func.now()
            await db.flush()

            return {
                "batch_id": batch.id,
                "status": "failed",
                "error": str(e),
            }

    @staticmethod
    async def get_batch_status(db: AsyncSession, batch_id: int) -> Optional[CardImportBatch]:
        """Get the status of an import batch."""
        stmt = select(CardImportBatch).where(CardImportBatch.id == batch_id)
        result = await db.execute(stmt)
        return result.scalar_one_or_none()

    @staticmethod
    async def get_batch_results(db: AsyncSession, batch_id: int) -> Optional[Dict[str, Any]]:
        """Get the match results for an import batch."""
        batch = await ImportBatchProcessor.get_batch_status(db, batch_id)
        if not batch:
            return None
        return batch.match_results

    @staticmethod
    async def confirm_batch(db: AsyncSession, batch_id: int, user_id: int) -> UserCardImportRecord:
        """
        Confirm an import batch.
        
        Args:
            db: Database session
            batch_id: ID of the batch to confirm
            user_id: ID of the user confirming
            
        Returns:
            UserCardImportRecord for the confirmed import
        """
        batch = await ImportBatchProcessor.get_batch_status(db, batch_id)
        if not batch:
            raise ValueError(f"Import batch {batch_id} not found")

        if batch.status != "completed":
            raise ValueError(f"Import batch {batch_id} is not completed (status: {batch.status})")

        # Check if already confirmed
        stmt = select(UserCardImportRecord).where(
            UserCardImportRecord.user_id == user_id,
            UserCardImportRecord.batch_id == batch_id,
        )
        result = await db.execute(stmt)
        existing = result.scalar_one_or_none()

        if existing:
            return existing

        # Create confirmation record
        record = UserCardImportRecord(
            user_id=user_id,
            batch_id=batch_id,
            is_confirmed=True,
        )
        db.add(record)
        await db.flush()

        return record

    @staticmethod
    async def get_user_imports(db: AsyncSession, user_id: int) -> List[CardImportBatch]:
        """Get all import batches for a user."""
        stmt = select(CardImportBatch).where(CardImportBatch.user_id == user_id).order_by(CardImportBatch.created_at.desc())
        result = await db.execute(stmt)
        return result.scalars().all()

    @staticmethod
    async def delete_batch(db: AsyncSession, batch_id: int, user_id: int) -> bool:
        """
        Delete an import batch.
        
        Args:
            db: Database session
            batch_id: ID of the batch to delete
            user_id: ID of the user deleting
            
        Returns:
            True if deleted successfully, False if not found
        """
        batch = await ImportBatchProcessor.get_batch_status(db, batch_id)
        if not batch or batch.user_id != user_id:
            return False

        # Delete confirmation records
        stmt = delete(UserCardImportRecord).where(UserCardImportRecord.batch_id == batch_id)
        await db.execute(stmt)

        # Delete batch
        stmt = delete(CardImportBatch).where(CardImportBatch.id == batch_id)
        await db.execute(stmt)
        await db.flush()

        return True
