"""
Card import router endpoints.

Provides endpoints for importing card collections, viewing import status,
and using imported cards for deckbuilding. This feature allows users to
upload their owned cards as a list, which then informs deckbuilding.
"""
import json
from fastapi import APIRouter, Depends, HTTPException, status
from sqlalchemy.ext.asyncio import AsyncSession
from sqlalchemy import select, func, update
from sqlalchemy.orm import selectinload
from typing import Optional, List

from app.core.database import get_db
from app.core.security import get_current_user
from app.models.models import User, MtgonlineCard
from app.models.user_card_import import UserCardImport
from app.schemas.card_import_schemas import (
    CardImportRequest,
    CardImportResponse,
    CardImportStatusResponse,
    CardMatchResult,
    CardImportSummary,
    MessageResponse,
)

router = APIRouter()


@router.get("/status", response_model=CardImportStatusResponse)
async def get_card_import_status(
    db: AsyncSession = Depends(get_db),
    current_user: dict = Depends(get_current_user),
):
    """
    Get current card import status for the current user.
    
    Returns whether a card import exists, the card count,
    and the names of all imported cards.
    """
    user_id = int(current_user["user_id"])

    stmt = select(UserCardImport).where(UserCardImport.user_id == user_id)
    result = await db.execute(stmt)
    card_import = result.scalar_one_or_none()

    if not card_import:
        return CardImportStatusResponse(has_import=False)

    # Parse card names from JSON
    try:
        card_names = json.loads(card_import.card_names_json)
    except (json.JSONDecodeError, TypeError):
        raise HTTPException(
            status_code=status.HTTP_500_INTERNAL_SERVER_ERROR,
            detail="Invalid card import data format"
        )

    return CardImportStatusResponse(
        has_import=True,
        card_count=len(card_names),
        card_names=card_names,
        last_imported=card_import.updated_at,
    )


@router.post("/", response_model=CardImportResponse)
async def import_cards(
    request: CardImportRequest,
    db: AsyncSession = Depends(get_db),
    current_user: dict = Depends(get_current_user),
):
    """
    Import a card collection for the current user.
    
    Accepts a list of card names and stores them as the user's
    owned card collection. This replaces any existing import.
    Uses fuzzy matching to find card IDs in the mtgonline_cards table.
    """
    user_id = int(current_user["user_id"])

    # Normalize card names: strip whitespace, title case
    normalized_names = [name.strip().title() for name in request.card_names]

    # Fetch all cards from the mtgonline_cards mirror
    stmt = select(MtgonlineCard).options(selectinload(MtgonlineCard.deck_cards))
    result = await db.execute(stmt)
    all_cards = result.scalars().all()

    # Build lookup dictionaries
    card_by_name = {}  # exact title case name -> MtgonlineCard
    card_by_lower = {}  # lowercase name -> MtgonlineCard (for fuzzy matching)
    
    for card in all_cards:
        if card.name:
            # Exact match (title case)
            card_by_name[card.name] = card
            # Lowercase for case-insensitive matching
            card_by_lower[card.name.lower()] = card

    # Match each imported card name to a database card
    matched_cards = []
    unmatched_cards = []
    matched_card_names = []

    for card_name in normalized_names:
        # Try exact match first
        if card_name in card_by_name:
            matched_cards.append(CardMatchResult(
                card_id=card_by_name[card_name].id,
                card_name=card_name,
                matched_name=card_by_name[card_name].name,
                match_type="exact",
                confidence=1.0,
            ))
            matched_card_names.append(card_by_name[card_name].name)
            continue

        # Try case-insensitive match
        if card_name.lower() in card_by_lower:
            matched_cards.append(CardMatchResult(
                card_id=card_by_lower[card_name.lower()].id,
                card_name=card_name,
                matched_name=card_by_lower[card_name.lower()].name,
                match_type="exact",
                confidence=0.95,
            ))
            matched_card_names.append(card_by_lower[card_name.lower()].name)
            continue

        # Try partial match (fuzzy)
        best_match = None
        best_confidence = 0.0
        for db_name, db_card in card_by_lower.items():
            # Simple partial match: check if one contains the other
            if card_name.lower() in db_name or db_name.lower() in card_name.lower():
                # Calculate confidence based on length similarity
                min_len = min(len(card_name), len(db_name))
                max_len = max(len(card_name), len(db_name))
                if max_len > 0:
                    confidence = min_len / max_len
                    if confidence > best_confidence:
                        best_confidence = confidence
                        best_match = db_card

        if best_match and best_confidence >= 0.6:  # 60% similarity threshold
            matched_cards.append(CardMatchResult(
                card_id=best_match.id,
                card_name=card_name,
                matched_name=best_match.name,
                match_type="partial",
                confidence=best_confidence,
            ))
            matched_card_names.append(best_match.name)
        else:
            unmatched_cards.append(card_name)

    # Create or update the card import
    card_names_json = json.dumps(matched_card_names)

    existing = select(UserCardImport).where(UserCardImport.user_id == user_id)
    existing_result = await db.execute(existing)
    existing_import = existing_result.scalar_one_or_none()

    if existing_import:
        # Update existing import
        stmt = (
            update(UserCardImport)
            .where(UserCardImport.id == existing_import.id)
            .values(
                card_names_json=card_names_json,
                updated_at=func.now(),
            )
        )
        await db.execute(stmt)
        await db.flush()
        
        # Fetch updated import
        stmt = select(UserCardImport).where(UserCardImport.id == existing_import.id)
        result = await db.execute(stmt)
        updated_import = result.scalar_one_or_none()
    else:
        # Create new import
        new_import = UserCardImport(
            user_id=user_id,
            card_names_json=card_names_json,
        )
        db.add(new_import)
        await db.flush()
        
        updated_import = new_import

    return CardImportResponse(
        message=f"Imported {len(matched_card_names)} cards successfully",
        card_count=len(matched_card_names),
        card_names=matched_card_names,
        imported_at=updated_import.updated_at,
    )


@router.delete("/", response_model=MessageResponse)
async def delete_card_import(
    db: AsyncSession = Depends(get_db),
    current_user: dict = Depends(get_current_user),
):
    """Delete the current user's card import."""
    user_id = int(current_user["user_id"])

    stmt = select(UserCardImport).where(UserCardImport.user_id == user_id)
    result = await db.execute(stmt)
    card_import = result.scalar_one_or_none()

    if not card_import:
        return MessageResponse(message="No card import found to delete")

    # Delete the import (cascade will handle related data if any)
    await db.delete(card_import)
    await db.flush()

    return MessageResponse(message="Card import deleted successfully")


@router.get("/summary", response_model=CardImportSummary)
async def get_card_import_summary(
    db: AsyncSession = Depends(get_db),
    current_user: dict = Depends(get_current_user),
):
    """
    Get a summary of the card import including match results.
    
    Returns the full match results with confidence scores and
    lists of unmatched cards for review.
    """
    user_id = int(current_user["user_id"])

    stmt = select(UserCardImport).where(UserCardImport.user_id == user_id)
    result = await db.execute(stmt)
    card_import = result.scalar_one_or_none()

    if not card_import:
        return CardImportSummary(
            total_cards=0,
            matched_cards=[],
            unmatched_cards=[],
        )

    try:
        card_names = json.loads(card_import.card_names_json)
    except (json.JSONDecodeError, TypeError):
        raise HTTPException(
            status_code=status.HTTP_500_INTERNAL_SERVER_ERROR,
            detail="Invalid card import data format"
        )

    # Fetch all cards for matching
    stmt = select(MtgonlineCard)
    result = await db.execute(stmt)
    all_cards = result.scalars().all()

    # Build lookup
    card_by_lower = {card.name.lower(): card for card in all_cards if card.name}

    # Match cards
    matched_cards = []
    unmatched_cards = []

    for card_name in card_names:
        normalized = card_name.strip().title()
        
        if normalized.lower() in card_by_lower:
            db_card = card_by_lower[normalized.lower()]
            matched_cards.append(CardMatchResult(
                card_id=db_card.id,
                card_name=normalized,
                matched_name=db_card.name,
                match_type="exact",
                confidence=1.0,
            ))
        else:
            unmatched_cards.append(card_name)

    return CardImportSummary(
        total_cards=len(card_names),
        matched_cards=matched_cards,
        unmatched_cards=unmatched_cards,
        import_id=card_import.id,
    )
