from __future__ import annotations
import uuid
from datetime import datetime
from typing import Optional
from fastapi import APIRouter, Depends, HTTPException
from sqlalchemy.orm import Session
from sqlalchemy import func
from pydantic import BaseModel
from app.database import get_db
from app.models.inventory import InventoryItem, InventoryTransaction
from app.models.accounting import InventoryLot
from app.services.auth import get_current_user
from app.services.inventory_costing import (
    inventory_in, inventory_out, inventory_valuation,
)

router = APIRouter(prefix="/inventory", tags=["inventory"])


class InventoryCreate(BaseModel):
    name: str
    category: str
    subcategory: Optional[str] = None
    unit: str = "piece"
    quantity: int = 0
    min_quantity: int = 5
    cost_price: float = 0.0
    sell_price: float = 0.0
    location: Optional[str] = None
    status: str = "in_stock"
    image_url: Optional[str] = None

class InventoryUpdate(BaseModel):
    name: Optional[str] = None
    category: Optional[str] = None
    subcategory: Optional[str] = None
    unit: Optional[str] = None
    quantity: Optional[int] = None
    min_quantity: Optional[int] = None
    cost_price: Optional[float] = None
    sell_price: Optional[float] = None
    location: Optional[str] = None
    status: Optional[str] = None
    image_url: Optional[str] = None

class InventoryOut(BaseModel):
    id: str
    code: str
    name: str
    category: str
    subcategory: Optional[str] = None
    unit: str
    quantity: int
    min_quantity: int
    cost_price: float
    sell_price: float
    location: Optional[str] = None
    status: str
    image_url: Optional[str] = None
    created_at: datetime
    updated_at: datetime
    model_config = {"from_attributes": True}

class TransactionCreate(BaseModel):
    item_id: str
    type: str
    quantity: int
    unit_cost: Optional[float] = None
    branch_id: Optional[str] = None
    reference_type: Optional[str] = None
    reference_id: Optional[str] = None
    notes: Optional[str] = None

class TransactionOut(BaseModel):
    id: str
    item_id: str
    type: str
    quantity: int
    unit_cost: Optional[float] = None
    value: Optional[float] = None
    branch_id: Optional[str] = None
    lot_id: Optional[str] = None
    journal_entry_id: Optional[str] = None
    reference_type: Optional[str] = None
    reference_id: Optional[str] = None
    notes: Optional[str] = None
    created_by: Optional[str] = None
    created_at: datetime
    model_config = {"from_attributes": True}


def _generate_item_code(db: Session) -> str:
    last = db.query(InventoryItem).filter(
        InventoryItem.code.like("INV-%")
    ).order_by(InventoryItem.code.desc()).first()
    if last:
        seq = int(last.code.split("-")[-1]) + 1
    else:
        seq = 1
    return f"INV-{seq:06d}"


@router.get("", response_model=list[InventoryOut])
def list_inventory(
    category: Optional[str] = None,
    search: Optional[str] = None,
    low_stock: Optional[bool] = None,
    db: Session = Depends(get_db),
    _=Depends(get_current_user),
):
    q = db.query(InventoryItem)
    if category:
        q = q.filter(InventoryItem.category == category)
    if search:
        q = q.filter(InventoryItem.name.ilike(f"%{search}%"))
    if low_stock:
        q = q.filter(InventoryItem.quantity <= InventoryItem.min_quantity)
    return q.order_by(InventoryItem.name).all()


@router.post("", response_model=InventoryOut, status_code=201)
def create_inventory_item(data: InventoryCreate, db: Session = Depends(get_db), current_user=Depends(get_current_user)):
    obj = InventoryItem(**data.model_dump(), code=_generate_item_code(db))
    db.add(obj)
    db.flush()
    if obj.quantity > 0 and obj.cost_price > 0:
        inventory_in(
            db, item_id=obj.id, quantity=obj.quantity, unit_cost=obj.cost_price,
            branch_id=obj.branch_id, reference_type="opening", reference_id=obj.id,
            created_by=current_user.id, notes="Opening stock",
            adjust_quantity=False,
        )
        if obj.quantity <= obj.min_quantity:
            obj.status = "low_stock"
    db.commit()
    db.refresh(obj)
    return obj


@router.get("/transactions", response_model=list[TransactionOut])
def list_transactions(
    item_id: Optional[str] = None,
    type: Optional[str] = None,
    reference_type: Optional[str] = None,
    reference_id: Optional[str] = None,
    db: Session = Depends(get_db),
    _=Depends(get_current_user),
):
    q = db.query(InventoryTransaction)
    if item_id:
        q = q.filter(InventoryTransaction.item_id == item_id)
    if type:
        q = q.filter(InventoryTransaction.type == type)
    if reference_type:
        q = q.filter(InventoryTransaction.reference_type == reference_type)
    if reference_id:
        q = q.filter(InventoryTransaction.reference_id == reference_id)
    return q.order_by(InventoryTransaction.created_at.desc()).all()


@router.get("/lots")
def list_lots(
    item_id: Optional[str] = None,
    active_only: bool = True,
    db: Session = Depends(get_db),
    _=Depends(get_current_user),
):
    q = db.query(InventoryLot)
    if item_id:
        q = q.filter(InventoryLot.item_id == item_id)
    if active_only:
        q = q.filter(InventoryLot.is_active == True, InventoryLot.remaining_qty > 0)  # noqa: E712
    lots = q.order_by(InventoryLot.received_date, InventoryLot.created_at).all()
    return [
        {
            "id": lot.id,
            "item_id": lot.item_id,
            "lot_number": lot.lot_number,
            "received_date": lot.received_date,
            "branch_id": lot.branch_id,
            "batch_id": lot.batch_id,
            "received_qty": float(lot.received_qty),
            "remaining_qty": float(lot.remaining_qty),
            "unit_cost": float(lot.unit_cost),
            "is_active": lot.is_active,
        }
        for lot in lots
    ]


@router.get("/valuation")
def get_inventory_valuation(
    branch_id: Optional[str] = None,
    db: Session = Depends(get_db),
    _=Depends(get_current_user),
):
    rows = inventory_valuation(db, branch_id=branch_id)
    total_value = sum(r["value"] for r in rows)
    return {"items": rows, "total_value": total_value, "items_count": len(rows)}


@router.get("/{item_id}", response_model=InventoryOut)
def get_inventory_item(item_id: str, db: Session = Depends(get_db), _=Depends(get_current_user)):
    obj = db.query(InventoryItem).filter(InventoryItem.id == item_id).first()
    if not obj:
        raise HTTPException(status_code=404, detail="Inventory item not found")
    return obj


@router.put("/{item_id}", response_model=InventoryOut)
def update_inventory_item(item_id: str, data: InventoryUpdate, db: Session = Depends(get_db), _=Depends(get_current_user)):
    obj = db.query(InventoryItem).filter(InventoryItem.id == item_id).first()
    if not obj:
        raise HTTPException(status_code=404, detail="Inventory item not found")
    for k, v in data.model_dump(exclude_unset=True).items():
        setattr(obj, k, v)
    db.commit()
    db.refresh(obj)
    return obj


@router.delete("/{item_id}")
def delete_inventory_item(item_id: str, db: Session = Depends(get_db), _=Depends(get_current_user)):
    obj = db.query(InventoryItem).filter(InventoryItem.id == item_id).first()
    if not obj:
        raise HTTPException(status_code=404, detail="Inventory item not found")
    db.delete(obj)
    db.commit()
    return {"detail": "Inventory item deleted"}


@router.post("/transactions", response_model=TransactionOut, status_code=201)
def create_transaction(data: TransactionCreate, db: Session = Depends(get_db), current_user=Depends(get_current_user)):
    item = db.query(InventoryItem).filter(InventoryItem.id == data.item_id).first()
    if not item:
        raise HTTPException(status_code=404, detail="Inventory item not found")

    if data.type == "out":
        if item.quantity < data.quantity:
            raise HTTPException(status_code=400, detail="Insufficient stock")
        inventory_out(
            db, item_id=item.id, quantity=data.quantity, branch_id=data.branch_id or item.branch_id,
            reference_type=data.reference_type or "manual", reference_id=data.reference_id,
            created_by=current_user.id, notes=data.notes,
        )
    elif data.type == "adjust":
        delta = data.quantity - item.quantity
        if delta > 0:
            inventory_in(
                db, item_id=item.id, quantity=delta, unit_cost=data.unit_cost or item.cost_price,
                branch_id=data.branch_id or item.branch_id,
                reference_type=data.reference_type or "adjust", reference_id=data.reference_id,
                created_by=current_user.id, notes=data.notes,
            )
        elif delta < 0:
            if item.quantity < -delta:
                raise HTTPException(status_code=400, detail="Adjustment exceeds on-hand stock")
            inventory_out(
                db, item_id=item.id, quantity=-delta, branch_id=data.branch_id or item.branch_id,
                reference_type=data.reference_type or "adjust", reference_id=data.reference_id,
                created_by=current_user.id, notes=data.notes,
            )
        item.quantity = data.quantity
        item.status = "low_stock" if item.quantity <= item.min_quantity else "in_stock"
        if delta == 0:
            db.add(InventoryTransaction(
                item_id=item.id, type="adjust", quantity=item.quantity,
                unit_cost=data.unit_cost or item.cost_price, branch_id=data.branch_id or item.branch_id,
                reference_type=data.reference_type or "adjust", reference_id=data.reference_id,
                notes=data.notes, created_by=current_user.id,
            ))
    else:
        inventory_in(
            db, item_id=item.id, quantity=data.quantity, unit_cost=data.unit_cost or item.cost_price,
            branch_id=data.branch_id or item.branch_id,
            reference_type=data.reference_type or "manual", reference_id=data.reference_id,
            created_by=current_user.id, notes=data.notes,
        )

    db.flush()
    q = db.query(InventoryTransaction).filter(InventoryTransaction.item_id == item.id)
    if data.reference_type:
        q = q.filter(InventoryTransaction.reference_type == data.reference_type)
    if data.reference_id:
        q = q.filter(InventoryTransaction.reference_id == data.reference_id)
    obj = q.order_by(InventoryTransaction.created_at.desc()).first()
    db.commit()
    if not obj:
        raise HTTPException(status_code=500, detail="Transaction was not recorded")
    return obj
