import datetime as dt
import uuid

from fastapi import APIRouter, Depends, Response
from pydantic import BaseModel
from sqlalchemy import func, or_, select
from sqlalchemy.orm import Session

from flyway.api.deps import current_actor, get_db, require_admin_actor
from flyway.api.lists import IdsIn, Page, like, nz_range, page_params, paginate
from flyway.api.views import batch_out, client_out, product_out, supplier_out
from flyway.domain.actor import Actor
from flyway.models import Client, Product, ProductBatch, Supplier, Transfer, TransferItem
from flyway.services.master import (
    ClientData, delete_batches, delete_clients, delete_products, delete_suppliers, save_batch, save_client,
    save_product, save_supplier,
)
from flyway.services.stock import stock
from flyway.services.reports import client_stock_by_product

router = APIRouter(prefix="/api")
admin_router = APIRouter(prefix="/api", dependencies=[Depends(require_admin_actor)])


class ClientIn(BaseModel):
    name: str
    email: str = ""
    mobile_phone: str = ""
    phone: str | None = None
    phone1: str | None = None


class ProductIn(BaseModel):
    title: str
    default_unit: int = 1
    available_unit: str | None = None


class BatchIn(BaseModel):
    product_id: uuid.UUID
    number: str
    date: dt.date
    used_by_date: dt.date | None = None


class SupplierIn(BaseModel):
    name: str
    rmp_number: str


# ---- 客户 ----
@router.get("/clients")
def list_clients(keyword: str | None = None, page: Page = Depends(page_params), db: Session = Depends(get_db)):
    q = select(Client)
    if keyword:
        q = q.where(or_(like(Client.name, keyword), like(Client.email, keyword), like(Client.mobile_phone, keyword)))
    rows, total = paginate(db, q, page, {"name": Client.name, "email": Client.email}, "name")
    return {"items": [client_out(c) for c in rows], "total": total}


@router.get("/clients/{client_id}")
def get_client(client_id: uuid.UUID, db: Session = Depends(get_db)):
    c = db.get(Client, client_id)
    if c is None:
        return Response(status_code=404)
    rows = client_stock_by_product(db, client_id)
    return {**client_out(c), "products": [
        {"product_id": r["product"].id, "product_title": r["product"].title,
         "default_unit": r["product"].default_unit, "stock": r["stock"], "batches": len(r["batches"])}
        for r in rows], "stock": [
        {"product_id": r["product"].id, "product_title": r["product"].title, "batch_number": number,
         "quantity": quantity} for r in rows for number, quantity in r["batches"].items()]}



@router.post("/clients")
def create_client(body: ClientIn, db: Session = Depends(get_db), actor: Actor = Depends(current_actor)):
    c = save_client(db, actor, ClientData(**body.model_dump()))
    db.commit()
    return client_out(c)


@router.put("/clients/{client_id}")
def update_client(client_id: uuid.UUID, body: ClientIn, db: Session = Depends(get_db),
                  actor: Actor = Depends(current_actor)):
    c = save_client(db, actor, ClientData(**body.model_dump()), client_id)
    db.commit()
    return client_out(c)


@router.post("/clients/delete", status_code=204)
def remove_clients(body: IdsIn, db: Session = Depends(get_db), actor: Actor = Depends(current_actor)):
    delete_clients(db, actor, body.ids)
    db.commit()


# ---- 产品 ----
@router.get("/products")
def list_products(keyword: str | None = None, page: Page = Depends(page_params), db: Session = Depends(get_db)):
    q = select(Product)
    if keyword:
        q = q.where(like(Product.title, keyword))
    stock_total = (select(func.coalesce(func.sum(TransferItem.remaining_stock), 0))
                   .join(Transfer).join(ProductBatch).where(ProductBatch.product_id == Product.id,
                       Transfer.is_load_out.is_(False), Transfer.is_draft.is_(False)).correlate(Product).scalar_subquery())
    batch_count = select(func.count()).where(ProductBatch.product_id == Product.id).correlate(Product).scalar_subquery()
    rows, total = paginate(db, q, page, {"title": Product.title, "default_unit": Product.default_unit,
                                       "stock": stock_total, "batch_count": batch_count}, "title")
    # ponytail: 每行一次库存汇总查询（每页最多 100 行），慢了再改成一次分组查询
    items = [{**product_out(p), "stock": stock(db, product_id=p.id),
              "batch_count": db.scalar(select(func.count()).where(ProductBatch.product_id == p.id))} for p in rows]
    return {"items": items, "total": total}


@router.get("/products/{product_id}")
def get_product(product_id: uuid.UUID, db: Session = Depends(get_db)):
    p = db.get(Product, product_id)
    return product_out(p) if p else Response(status_code=404)


@router.post("/products")
def create_product(body: ProductIn, db: Session = Depends(get_db), actor: Actor = Depends(current_actor)):
    p = save_product(db, actor, body.title, body.default_unit, body.available_unit)
    db.commit()
    return product_out(p)


@router.put("/products/{product_id}")
def update_product(product_id: uuid.UUID, body: ProductIn, db: Session = Depends(get_db),
                   actor: Actor = Depends(current_actor)):
    p = save_product(db, actor, body.title, body.default_unit, body.available_unit, product_id)
    db.commit()
    return product_out(p)


@router.post("/products/delete", status_code=204)
def remove_products(body: IdsIn, db: Session = Depends(get_db), actor: Actor = Depends(current_actor)):
    delete_products(db, actor, body.ids)
    db.commit()


# ---- 批次 ----
@router.get("/batches")
def list_batches(keyword: str | None = None, product_id: uuid.UUID | None = None, from_date: dt.date | None = None,
                 to_date: dt.date | None = None, batch: str | None = None, page: Page = Depends(page_params), db: Session = Depends(get_db)):
    q = select(ProductBatch).join(Product)
    if keyword:
        q = q.where(or_(like(ProductBatch.number, keyword), like(Product.title, keyword)))
    if batch:
        q = q.where(func.lower(func.rtrim(ProductBatch.number)) == batch.rstrip(' ').lower())
    if product_id:
        q = q.where(ProductBatch.product_id == product_id)
    if from_date:
        q = q.where(ProductBatch.date >= from_date)
    if to_date:
        q = q.where(ProductBatch.date <= to_date)
    stock_total = (select(func.coalesce(func.sum(TransferItem.remaining_stock), 0)).join(Transfer)
                   .where(TransferItem.batch_id == ProductBatch.id, Transfer.is_load_out.is_(False),
                          Transfer.is_draft.is_(False)).correlate(ProductBatch).scalar_subquery())
    sorts = {"stock": stock_total, "number": ProductBatch.number, "date": ProductBatch.date, "used_by_date": ProductBatch.used_by_date,
             "product": Product.title}
    rows, total = paginate(db, q, page, sorts, "number")
    return {"items": [batch_out(bt, stock(db, batch_id=bt.id)) for bt in rows], "total": total}


@router.get("/batches/{batch_id}")
def get_batch(batch_id: uuid.UUID, db: Session = Depends(get_db)):
    bt = db.get(ProductBatch, batch_id)
    return batch_out(bt, stock(db, batch_id=bt.id)) if bt else Response(status_code=404)


@router.post("/batches")
def create_batch(body: BatchIn, db: Session = Depends(get_db), actor: Actor = Depends(current_actor)):
    bt = save_batch(db, actor, body.product_id, body.number, body.date, body.used_by_date)
    db.commit()
    return batch_out(bt, stock(db, batch_id=bt.id))


@router.put("/batches/{batch_id}")
def update_batch(batch_id: uuid.UUID, body: BatchIn, db: Session = Depends(get_db),
                 actor: Actor = Depends(current_actor)):
    bt = save_batch(db, actor, body.product_id, body.number, body.date, body.used_by_date, batch_id)
    db.commit()
    return batch_out(bt, stock(db, batch_id=bt.id))


@router.post("/batches/delete", status_code=204)
def remove_batches(body: IdsIn, db: Session = Depends(get_db), actor: Actor = Depends(current_actor)):
    delete_batches(db, actor, body.ids)
    db.commit()


# ---- 供应商（仅管理员）----
@admin_router.get("/suppliers")
def list_suppliers(keyword: str | None = None, page: Page = Depends(page_params), db: Session = Depends(get_db)):
    q = select(Supplier)
    if keyword:
        q = q.where(or_(like(Supplier.name, keyword), like(Supplier.rmp_number, keyword)))
    rows, total = paginate(db, q, page, {"name": Supplier.name, "rmp_number": Supplier.rmp_number}, "rmp_number")
    return {"items": [supplier_out(s) for s in rows], "total": total}


@admin_router.post("/suppliers")
def create_supplier(body: SupplierIn, db: Session = Depends(get_db), actor: Actor = Depends(current_actor)):
    s = save_supplier(db, actor, body.name, body.rmp_number)
    db.commit()
    return supplier_out(s)


@admin_router.get("/suppliers/{supplier_id}")
def get_supplier(supplier_id: uuid.UUID, db: Session = Depends(get_db)):
    supplier = db.get(Supplier, supplier_id)
    return supplier_out(supplier) if supplier else Response(status_code=404)


@admin_router.put("/suppliers/{supplier_id}")
def update_supplier(supplier_id: uuid.UUID, body: SupplierIn, db: Session = Depends(get_db),
                    actor: Actor = Depends(current_actor)):
    s = save_supplier(db, actor, body.name, body.rmp_number, supplier_id)
    db.commit()
    return supplier_out(s)


@admin_router.post("/suppliers/delete", status_code=204)
def remove_suppliers(body: IdsIn, db: Session = Depends(get_db), actor: Actor = Depends(current_actor)):
    delete_suppliers(db, actor, body.ids)
    db.commit()
