from datetime import date, datetime
from typing import List, Optional, Dict, Any
from uuid import uuid4

from fastapi import APIRouter, Depends, HTTPException, Query
from sqlalchemy import and_, func
from sqlalchemy.orm import Session

from app.core.database import get_db
from app.core.dependencies import get_current_company_id
from app.models.branch import Branch
from app.models.inventory import SerialNumber, Product, ProductBatchRegistry, ProductTraceabilityLog, InvoiceItemSerial
from app.models.sales import Invoice, InvoiceItem, Customer
from pydantic import BaseModel

router = APIRouter()


# ==================== SCHEMAS ====================

class BatchRegistryCreate(BaseModel):
    batch_number: str
    product_id: str
    manufacturing_date: datetime
    manufacture_site: Optional[str] = None
    manufactured_quantity: float = 0
    expiry_date: Optional[date] = None
    storage_location: Optional[str] = None
    storage_temperature: Optional[str] = None
    storage_humidity_max: Optional[float] = None
    notes: Optional[str] = None


class BatchRegistryResponse(BaseModel):
    id: str
    batch_number: str
    product_id: str
    product_name: Optional[str] = None
    manufacturing_date: datetime
    manufacture_site: Optional[str]
    manufactured_quantity: float
    quality_status: str
    expiry_date: Optional[date]
    storage_location: Optional[str]
    storage_temperature: Optional[str]
    storage_humidity_max: Optional[float]
    notes: Optional[str]
    created_at: datetime
    updated_at: datetime

    class Config:
        from_attributes = True


class ProductTraceabilityResponse(BaseModel):
    batch_number: str
    serial_number: Optional[str]
    event_type: str
    event_date: datetime
    product_id: Optional[str]
    product_name: Optional[str]
    customer_id: Optional[str]
    customer_name: Optional[str]
    sale_id: Optional[str]
    invoice_id: Optional[str]
    invoice_number: Optional[str]
    location: Optional[str]
    quantity: Optional[float]
    notes: Optional[str]
    created_by: Optional[str]

    class Config:
        from_attributes = True


class CustomerPurchaseHistoryResponse(BaseModel):
    customer_id: str
    customer_name: str
    total_purchases: int
    purchases: List[Dict[str, Any]]


class BatchSummaryResponse(BaseModel):
    batch_number: str
    product_id: str
    product_name: Optional[str]
    manufacturing_date: datetime
    expiry_date: Optional[date]
    manufactured_quantity: float
    serials_allocated: int
    serials_sold: int
    serials_available: int
    quality_status: str
    manufacture_site: Optional[str]


class ExpiryAlertResponse(BaseModel):
    batch_number: str
    product_id: str
    product_name: Optional[str]
    expiry_date: date
    days_until_expiry: int
    manufactured_quantity: float
    serials_remaining: int
    status: str  # "critical" (<7 days), "warning" (<30 days), "ok"


# ==================== ENDPOINTS ====================

@router.post("/batch-registry", response_model=BatchRegistryResponse)
async def create_batch_registry(
    payload: BatchRegistryCreate,
    db: Session = Depends(get_db),
):
    """Register a new manufactured batch"""
    product = db.query(Product).filter(Product.id == payload.product_id).first()
    if not product:
        raise HTTPException(status_code=404, detail="Product not found")

    existing = db.query(ProductBatchRegistry).filter(
        ProductBatchRegistry.batch_number == payload.batch_number
    ).first()
    if existing:
        raise HTTPException(status_code=400, detail="Batch number already exists")

    batch = ProductBatchRegistry(
        batch_number=payload.batch_number,
        product_id=payload.product_id,
        manufacturing_date=payload.manufacturing_date,
        manufacture_site=payload.manufacture_site,
        manufactured_quantity=payload.manufactured_quantity,
        expiry_date=payload.expiry_date,
        storage_location=payload.storage_location,
        storage_temperature=payload.storage_temperature,
        storage_humidity_max=payload.storage_humidity_max,
        notes=payload.notes,
    )
    db.add(batch)
    db.commit()
    db.refresh(batch)

    return {
        **batch.__dict__,
        "product_name": product.name,
    }


@router.get("/batch-registry/{batch_number}", response_model=BatchRegistryResponse)
async def get_batch_registry(batch_number: str, db: Session = Depends(get_db)):
    """Get batch registry details"""
    batch = db.query(ProductBatchRegistry).filter(
        ProductBatchRegistry.batch_number == batch_number
    ).first()
    if not batch:
        raise HTTPException(status_code=404, detail="Batch not found")

    product = db.query(Product).filter(Product.id == batch.product_id).first()

    return {
        **batch.__dict__,
        "product_name": product.name if product else None,
    }


@router.get("/batch-summary", response_model=List[BatchSummaryResponse])
async def get_batch_summary(
    db: Session = Depends(get_db),
    product_id: Optional[str] = Query(None),
    batch_number: Optional[str] = Query(None),
    expiry_from: Optional[date] = Query(None),
    expiry_to: Optional[date] = Query(None),
    company_id: Optional[str] = Depends(get_current_company_id),
):
    """Get summary of batches with serial allocation counts"""
    query = db.query(ProductBatchRegistry)

    if company_id and not product_id:
        branch_ids_sq = db.query(Branch.id).filter(Branch.company_id == company_id)
        prod_ids_sq = db.query(Product.id).filter(Product.branch_id.in_(branch_ids_sq))
        query = query.filter(ProductBatchRegistry.product_id.in_(prod_ids_sq))

    if product_id:
        query = query.filter(ProductBatchRegistry.product_id == product_id)
    if batch_number:
        query = query.filter(ProductBatchRegistry.batch_number == batch_number)
    if expiry_from:
        query = query.filter(ProductBatchRegistry.expiry_date >= expiry_from)
    if expiry_to:
        query = query.filter(ProductBatchRegistry.expiry_date <= expiry_to)

    batches = query.all()
    results = []

    for batch in batches:
        product = db.query(Product).filter(Product.id == batch.product_id).first()

        # Count serials
        total_serials = db.query(func.count(SerialNumber.id)).filter(
            SerialNumber.batch_number == batch.batch_number
        ).scalar() or 0

        sold_serials = db.query(func.count(SerialNumber.id)).filter(
            and_(
                SerialNumber.batch_number == batch.batch_number,
                SerialNumber.status == "sold"
            )
        ).scalar() or 0

        results.append({
            "batch_number": batch.batch_number,
            "product_id": batch.product_id,
            "product_name": product.name if product else None,
            "manufacturing_date": batch.manufacturing_date,
            "expiry_date": batch.expiry_date,
            "manufactured_quantity": float(batch.manufactured_quantity),
            "serials_allocated": total_serials,
            "serials_sold": sold_serials,
            "serials_available": total_serials - sold_serials,
            "quality_status": batch.quality_status,
            "manufacture_site": batch.manufacture_site,
        })

    return results


@router.get("/traceability/{batch_number}", response_model=List[ProductTraceabilityResponse])
async def get_batch_traceability(
    batch_number: str,
    db: Session = Depends(get_db),
    event_type: Optional[str] = Query(None),
    company_id: Optional[str] = Depends(get_current_company_id),
):
    """Get complete traceability trail for a batch (from manufacture to customer)"""
    query = db.query(ProductTraceabilityLog).filter(
        ProductTraceabilityLog.batch_number == batch_number
    )
    if company_id:
        branch_ids_sq = db.query(Branch.id).filter(Branch.company_id == company_id)
        prod_ids_sq = db.query(Product.id).filter(Product.branch_id.in_(branch_ids_sq))
        query = query.filter(ProductTraceabilityLog.product_id.in_(prod_ids_sq))

    if event_type:
        query = query.filter(ProductTraceabilityLog.event_type == event_type)

    logs = query.order_by(ProductTraceabilityLog.event_date).all()

    results = []
    for log in logs:
        product = db.query(Product).filter(Product.id == log.product_id).first()
        customer = db.query(Customer).filter(Customer.id == log.customer_id).first()
        invoice = db.query(Invoice).filter(Invoice.id == log.invoice_id).first()

        results.append({
            "batch_number": log.batch_number,
            "serial_number": log.serial_number,
            "event_type": log.event_type,
            "event_date": log.event_date,
            "product_id": log.product_id,
            "product_name": product.name if product else None,
            "customer_id": log.customer_id,
            "customer_name": customer.name if customer else None,
            "sale_id": log.sale_id,
            "invoice_id": log.invoice_id,
            "invoice_number": invoice.invoice_number if invoice else None,
            "location": log.location,
            "quantity": float(log.quantity) if log.quantity else None,
            "notes": log.notes,
            "created_by": log.created_by,
        })

    return results


@router.get("/customer-purchases/{customer_id}", response_model=CustomerPurchaseHistoryResponse)
async def get_customer_purchase_history(
    customer_id: str,
    db: Session = Depends(get_db),
    from_date: Optional[date] = Query(None),
    to_date: Optional[date] = Query(None),
    company_id: Optional[str] = Depends(get_current_company_id),
):
    """Get all products purchased by a customer with batch and serial details"""
    customer = db.query(Customer).filter(Customer.id == customer_id).first()
    if not customer:
        raise HTTPException(status_code=404, detail="Customer not found")

    # Query invoices for this customer (scoped to company if known)
    invoice_query = db.query(Invoice).filter(Invoice.customer_id == customer_id)
    if company_id:
        branch_ids_sq = db.query(Branch.id).filter(Branch.company_id == company_id)
        invoice_query = invoice_query.filter(Invoice.branch_id.in_(branch_ids_sq))
    if from_date:
        invoice_query = invoice_query.filter(Invoice.date >= from_date)
    if to_date:
        invoice_query = invoice_query.filter(Invoice.date <= to_date)

    invoices = invoice_query.all()

    purchases = []
    for invoice in invoices:
        items = db.query(InvoiceItem).filter(InvoiceItem.invoice_id == invoice.id).all()
        for item in items:
            product = db.query(Product).filter(Product.id == item.product_id).first()

            # Get serials for this item
            serials = []
            item_serials = db.query(InvoiceItemSerial).filter(
                InvoiceItemSerial.invoice_item_id == item.id
            ).all()

            for item_serial in item_serials:
                serial = item_serial.serial_number
                serials.append({
                    "serial": serial.serial,
                    "batch_number": item_serial.batch_number or serial.batch_number,
                    "expiry_date": item_serial.expiry_date or serial.expiry_date,
                    "manufactured_date": serial.manufactured_date,
                })

            purchases.append({
                "invoice_id": invoice.id,
                "invoice_number": invoice.invoice_number,
                "invoice_date": invoice.date,
                "product_id": item.product_id,
                "product_name": product.name if product else None,
                "product_sku": product.sku if product else None,
                "quantity": float(item.quantity),
                "unit_price": float(item.price),
                "total": float(item.total),
                "batch_number": item.batch_number,
                "expiry_date": item.expiry_date,
                "serials": serials,
            })

    return {
        "customer_id": customer_id,
        "customer_name": customer.name,
        "total_purchases": len(purchases),
        "purchases": purchases,
    }


@router.get("/expiry-alerts", response_model=List[ExpiryAlertResponse])
async def get_expiry_alerts(
    db: Session = Depends(get_db),
    days_threshold: int = Query(30, ge=1, le=365),
    company_id: Optional[str] = Depends(get_current_company_id),
):
    """Get batches approaching expiry dates (critical < 7 days, warning < 30 days)"""
    today = date.today()
    query_date = date.fromordinal(today.toordinal() + days_threshold)

    _exp_q = db.query(ProductBatchRegistry).filter(
        and_(
            ProductBatchRegistry.expiry_date.isnot(None),
            ProductBatchRegistry.expiry_date <= query_date,
            ProductBatchRegistry.expiry_date >= today,
        )
    )
    if company_id:
        branch_ids_sq = db.query(Branch.id).filter(Branch.company_id == company_id)
        prod_ids_sq = db.query(Product.id).filter(Product.branch_id.in_(branch_ids_sq))
        _exp_q = _exp_q.filter(ProductBatchRegistry.product_id.in_(prod_ids_sq))
    batches = _exp_q.all()

    alerts = []
    for batch in batches:
        product = db.query(Product).filter(Product.id == batch.product_id).first()

        days_until = (batch.expiry_date - today).days

        # Count remaining serials
        remaining = db.query(func.count(SerialNumber.id)).filter(
            and_(
                SerialNumber.batch_number == batch.batch_number,
                SerialNumber.status.in_(["available", "reserved"])
            )
        ).scalar() or 0

        status = "critical" if days_until < 7 else "warning"

        alerts.append({
            "batch_number": batch.batch_number,
            "product_id": batch.product_id,
            "product_name": product.name if product else None,
            "expiry_date": batch.expiry_date,
            "days_until_expiry": days_until,
            "manufactured_quantity": float(batch.manufactured_quantity),
            "serials_remaining": remaining,
            "status": status,
        })

    return sorted(alerts, key=lambda x: x["days_until_expiry"])


@router.post("/log-event")
async def log_traceability_event(
    batch_number: str,
    event_type: str,
    db: Session = Depends(get_db),
    serial_number: Optional[str] = Query(None),
    product_id: Optional[str] = Query(None),
    customer_id: Optional[str] = Query(None),
    sale_id: Optional[str] = Query(None),
    invoice_id: Optional[str] = Query(None),
    location: Optional[str] = Query(None),
    quantity: Optional[float] = Query(None),
    notes: Optional[str] = Query(None),
    created_by: Optional[str] = Query(None),
):
    """Log a traceability event for a batch/product"""

    # Validate batch exists
    batch = db.query(ProductBatchRegistry).filter(
        ProductBatchRegistry.batch_number == batch_number
    ).first()
    if not batch and event_type == "manufactured":
        raise HTTPException(status_code=404, detail="Batch not found")

    log = ProductTraceabilityLog(
        batch_number=batch_number,
        serial_number=serial_number,
        event_type=event_type,
        product_id=product_id or (batch.product_id if batch else None),
        customer_id=customer_id,
        sale_id=sale_id,
        invoice_id=invoice_id,
        location=location,
        quantity=quantity,
        notes=notes,
        created_by=created_by,
    )
    db.add(log)
    db.commit()
    db.refresh(log)

    return {"message": "Event logged", "log_id": log.id}


@router.get("/product-recall/{batch_number}")
async def initiate_product_recall(
    batch_number: str,
    db: Session = Depends(get_db),
    recall_reason: str = Query(...),
):
    """Initiate product recall by batch - returns all customers who purchased this batch"""

    logs = db.query(ProductTraceabilityLog).filter(
        and_(
            ProductTraceabilityLog.batch_number == batch_number,
            ProductTraceabilityLog.event_type == "sold"
        )
    ).all()

    affected_customers = {}
    for log in logs:
        if log.customer_id:
            if log.customer_id not in affected_customers:
                customer = db.query(Customer).filter(Customer.id == log.customer_id).first()
                affected_customers[log.customer_id] = {
                    "customer_name": customer.name if customer else "Unknown",
                    "customer_contact": customer.contact_phone if customer else None,
                    "customer_email": customer.email if customer else None,
                    "serials": [],
                }
            affected_customers[log.customer_id]["serials"].append(log.serial_number)

    # Log the recall event
    recall_log = ProductTraceabilityLog(
        batch_number=batch_number,
        event_type="recalled",
        notes=f"Recall initiated: {recall_reason}",
    )
    db.add(recall_log)
    db.commit()

    return {
        "batch_number": batch_number,
        "recall_reason": recall_reason,
        "affected_customers_count": len(affected_customers),
        "affected_customers": affected_customers,
        "recall_initiated_at": datetime.utcnow(),
    }
