from typing import Optional

from fastapi import APIRouter, Depends, HTTPException, BackgroundTasks
from sqlalchemy.ext.asyncio import AsyncSession
from sqlalchemy import select
from pydantic import BaseModel

from database import get_db, AsyncSessionLocal
from models.user import User
from models.supplier import Supplier
from models.invoice import Invoice
from auth import get_current_user, require_cap
from ocr.parser import identify_supplier

router = APIRouter()


async def rematch_unmatched_invoices(kitchen_id: int):
    """
    Re-run supplier matching for all invoices without a supplier.
    Called after supplier create/update to match previously unmatched invoices.
    """
    async with AsyncSessionLocal() as db:
        # Get all invoices without a supplier that have vendor_name from OCR
        result = await db.execute(
            select(Invoice).where(
                Invoice.kitchen_id == kitchen_id,
                Invoice.supplier_id == None,
                Invoice.vendor_name != None
            )
        )
        invoices = result.scalars().all()

        for invoice in invoices:
            if invoice.vendor_name:
                supplier_id, match_type = await identify_supplier(
                    invoice.vendor_name, kitchen_id, db
                )
                if supplier_id:
                    invoice.supplier_id = supplier_id
                    invoice.supplier_match_type = match_type

        await db.commit()


class SupplierCreate(BaseModel):
    name: str
    aliases: list[str] = []
    template_config: dict = {}
    identifier_config: dict = {}
    skip_dext: bool = False
    order_email: Optional[str] = None
    account_number: Optional[str] = None


class SupplierUpdate(BaseModel):
    name: Optional[str] = None
    aliases: Optional[list[str]] = None
    template_config: Optional[dict] = None
    identifier_config: Optional[dict] = None
    skip_dext: Optional[bool] = None
    order_email: Optional[str] = None
    account_number: Optional[str] = None


class SupplierResponse(BaseModel):
    id: int
    name: str
    aliases: list[str]
    template_config: dict
    identifier_config: dict
    skip_dext: bool
    order_email: Optional[str] = None
    account_number: Optional[str] = None
    created_at: str

    class Config:
        from_attributes = True


@router.post("/", response_model=SupplierResponse)
async def create_supplier(
    request: SupplierCreate,
    background_tasks: BackgroundTasks,
    current_user: User = Depends(get_current_user),
    db: AsyncSession = Depends(get_db)
):
    """Create a new supplier with extraction templates"""
    supplier = Supplier(
        kitchen_id=current_user.kitchen_id,
        name=request.name,
        aliases=request.aliases,
        template_config=request.template_config,
        identifier_config=request.identifier_config,
        skip_dext=request.skip_dext,
        order_email=request.order_email,
        account_number=request.account_number,
    )
    db.add(supplier)
    await db.commit()
    await db.refresh(supplier)

    # Rematch unmatched invoices in background
    background_tasks.add_task(rematch_unmatched_invoices, current_user.kitchen_id)

    return SupplierResponse(
        id=supplier.id,
        name=supplier.name,
        aliases=supplier.aliases or [],
        template_config=supplier.template_config,
        identifier_config=supplier.identifier_config,
        skip_dext=supplier.skip_dext,
        order_email=supplier.order_email,
        account_number=supplier.account_number,
        created_at=supplier.created_at.isoformat()
    )


@router.get("/", response_model=list[SupplierResponse])
async def list_suppliers(
    current_user: User = Depends(get_current_user),
    db: AsyncSession = Depends(get_db)
):
    """List all suppliers for the current kitchen"""
    result = await db.execute(
        select(Supplier)
        .where(Supplier.kitchen_id == current_user.kitchen_id)
        .order_by(Supplier.name)
    )
    suppliers = result.scalars().all()

    return [
        SupplierResponse(
            id=s.id,
            name=s.name,
            aliases=s.aliases or [],
            template_config=s.template_config,
            identifier_config=s.identifier_config,
            skip_dext=s.skip_dext,
            order_email=s.order_email,
            account_number=s.account_number,
            created_at=s.created_at.isoformat()
        )
        for s in suppliers
    ]


@router.get("/{supplier_id}", response_model=SupplierResponse)
async def get_supplier(
    supplier_id: int,
    current_user: User = Depends(get_current_user),
    db: AsyncSession = Depends(get_db)
):
    """Get a supplier by ID"""
    result = await db.execute(
        select(Supplier).where(
            Supplier.id == supplier_id,
            Supplier.kitchen_id == current_user.kitchen_id
        )
    )
    supplier = result.scalar_one_or_none()

    if not supplier:
        raise HTTPException(status_code=404, detail="Supplier not found")

    return SupplierResponse(
        id=supplier.id,
        name=supplier.name,
        aliases=supplier.aliases or [],
        template_config=supplier.template_config,
        identifier_config=supplier.identifier_config,
        skip_dext=supplier.skip_dext,
        order_email=supplier.order_email,
        account_number=supplier.account_number,
        created_at=supplier.created_at.isoformat()
    )


@router.patch("/{supplier_id}", response_model=SupplierResponse)
async def update_supplier(
    supplier_id: int,
    update: SupplierUpdate,
    background_tasks: BackgroundTasks,
    current_user: User = Depends(get_current_user),
    db: AsyncSession = Depends(get_db)
):
    """Update a supplier's template configuration"""
    result = await db.execute(
        select(Supplier).where(
            Supplier.id == supplier_id,
            Supplier.kitchen_id == current_user.kitchen_id
        )
    )
    supplier = result.scalar_one_or_none()

    if not supplier:
        raise HTTPException(status_code=404, detail="Supplier not found")

    update_data = update.model_dump(exclude_unset=True)
    for field, value in update_data.items():
        setattr(supplier, field, value)

    await db.commit()
    await db.refresh(supplier)

    # Rematch unmatched invoices in background
    background_tasks.add_task(rematch_unmatched_invoices, current_user.kitchen_id)

    return SupplierResponse(
        id=supplier.id,
        name=supplier.name,
        aliases=supplier.aliases or [],
        template_config=supplier.template_config,
        identifier_config=supplier.identifier_config,
        skip_dext=supplier.skip_dext,
        order_email=supplier.order_email,
        account_number=supplier.account_number,
        created_at=supplier.created_at.isoformat()
    )


@router.delete("/{supplier_id}")
async def delete_supplier(
    supplier_id: int,
    current_user: User = Depends(get_current_user),
    db: AsyncSession = Depends(get_db)
):
    """Delete a supplier"""
    result = await db.execute(
        select(Supplier).where(
            Supplier.id == supplier_id,
            Supplier.kitchen_id == current_user.kitchen_id
        )
    )
    supplier = result.scalar_one_or_none()

    if not supplier:
        raise HTTPException(status_code=404, detail="Supplier not found")

    await db.delete(supplier)
    await db.commit()

    return {"message": "Supplier deleted"}


class AddAliasRequest(BaseModel):
    alias: str
    invoice_id: Optional[int] = None  # If provided, update this invoice's match type to 'exact'


@router.post("/{supplier_id}/aliases", response_model=SupplierResponse)
async def add_supplier_alias(
    supplier_id: int,
    request: AddAliasRequest,
    background_tasks: BackgroundTasks,
    current_user: User = Depends(get_current_user),
    db: AsyncSession = Depends(get_db)
):
    """Add an alias to a supplier for better matching"""
    result = await db.execute(
        select(Supplier).where(
            Supplier.id == supplier_id,
            Supplier.kitchen_id == current_user.kitchen_id
        )
    )
    supplier = result.scalar_one_or_none()

    if not supplier:
        raise HTTPException(status_code=404, detail="Supplier not found")

    alias = request.alias.strip()
    if not alias:
        raise HTTPException(status_code=400, detail="Alias cannot be empty")

    # Add alias if not already present
    # Create a new list to ensure SQLAlchemy detects the change (JSON columns don't detect in-place mutations)
    current_aliases = list(supplier.aliases or [])
    if alias not in current_aliases:
        current_aliases.append(alias)
        supplier.aliases = current_aliases

    # If invoice_id provided, update that invoice's match type to 'exact'
    if request.invoice_id:
        inv_result = await db.execute(
            select(Invoice).where(
                Invoice.id == request.invoice_id,
                Invoice.kitchen_id == current_user.kitchen_id
            )
        )
        invoice = inv_result.scalar_one_or_none()
        if invoice and invoice.supplier_match_type == 'fuzzy':
            invoice.supplier_match_type = 'exact'

    await db.commit()
    await db.refresh(supplier)

    # Rematch unmatched invoices in background
    background_tasks.add_task(rematch_unmatched_invoices, current_user.kitchen_id)

    return SupplierResponse(
        id=supplier.id,
        name=supplier.name,
        aliases=supplier.aliases or [],
        template_config=supplier.template_config,
        identifier_config=supplier.identifier_config,
        skip_dext=supplier.skip_dext,
        order_email=supplier.order_email,
        account_number=supplier.account_number,
        created_at=supplier.created_at.isoformat()
    )


@router.post("/rematch-fuzzy")
async def rematch_fuzzy_invoices(
    background_tasks: BackgroundTasks,
    current_user: User = Depends(get_current_user),
    db: AsyncSession = Depends(get_db)
):
    """
    Clear all fuzzy-matched invoices and re-run supplier matching.
    Use this after updating matching logic to fix incorrect fuzzy matches.
    """
    # Count fuzzy matches before clearing
    count_result = await db.execute(
        select(Invoice).where(
            Invoice.kitchen_id == current_user.kitchen_id,
            Invoice.supplier_match_type == "fuzzy"
        )
    )
    fuzzy_invoices = count_result.scalars().all()
    count = len(fuzzy_invoices)

    # Clear supplier assignment for all fuzzy matches
    for invoice in fuzzy_invoices:
        invoice.supplier_id = None
        invoice.supplier_match_type = None

    await db.commit()

    # Re-run matching in background
    background_tasks.add_task(rematch_unmatched_invoices, current_user.kitchen_id)

    return {"message": f"Cleared {count} fuzzy matches. Re-matching in background."}
