import datetime as dt
import uuid

from fastapi import APIRouter, Depends, HTTPException
from pydantic import BaseModel
from sqlalchemy import select
from sqlalchemy.orm import Session

from flyway.api.deps import current_actor, get_db
from flyway.api.lists import IdsIn, Page, like, nz_range, page_params, paginate
from flyway.api.views import item_out, transfer_detail, transfer_row
from flyway.domain.actor import Actor
from flyway.models import Client, ProductBatch, Transfer, TransferItem
from flyway.services.load_in import LoadInHeader, LoadInLine, delete_load_ins, save_load_in, set_load_in_draft
from flyway.services.load_out import lines_for_all_remaining

router = APIRouter(prefix="/api/load-ins")


class LineIn(BaseModel):
    product_id: uuid.UUID
    batch_number: str
    quantity: int
    unit: int = 1
    used_by_date: dt.date | None = None
    reference: str | None = None
    id: uuid.UUID | None = None


class LoadInIn(BaseModel):
    client_id: uuid.UUID
    code: str
    date: dt.datetime
    from_location: str | None = None
    from_rmp: str | None = None
    ed_identification: str | None = None
    export_to: str | None = None
    transport_company: str | None = None
    vehicle_registration: str | None = None
    condition_of_vehicle: str | None = None
    non_conforming_product: bool = False
    notes: str | None = None
    lines: list[LineIn]


def _load_in(db: Session, transfer_id: uuid.UUID) -> Transfer:
    t = db.get(Transfer, transfer_id)
    if t is None or t.is_load_out or t.is_local:
        raise HTTPException(status_code=404)
    return t


def _save(db: Session, actor: Actor, body: LoadInIn, transfer_id: uuid.UUID | None = None) -> dict:
    header = LoadInHeader(**body.model_dump(exclude={"lines"}))
    t = save_load_in(db, actor, header, [LoadInLine(**line.model_dump()) for line in body.lines], transfer_id)
    db.commit()
    return transfer_detail(db, t)


def batch_filter(q, batch: str):
    """单据中任一明细的批号包含关键字。"""
    ids = select(TransferItem.transfer_id).join(ProductBatch).where(like(ProductBatch.number, batch))
    return q.where(Transfer.id.in_(ids))


@router.get("")
def list_load_ins(keyword: str | None = None, client_id: uuid.UUID | None = None, from_date: dt.date | None = None,
                  to_date: dt.date | None = None, from_rmp: str | None = None, batch: str | None = None,
                  is_draft: bool | None = None, page: Page = Depends(page_params), db: Session = Depends(get_db)):
    q = select(Transfer).join(Client).where(Transfer.is_load_out.is_(False), Transfer.is_local.is_(False))
    if keyword:
        q = q.where(like(Transfer.code, keyword))
    if client_id:
        q = q.where(Transfer.client_id == client_id)
    start, end = nz_range(from_date, to_date)
    if start:
        q = q.where(Transfer.date >= start)
    if end:
        q = q.where(Transfer.date < end)
    if from_rmp:
        q = q.where(like(Transfer.from_rmp, from_rmp))
    if batch:
        q = batch_filter(q, batch)
    if is_draft is not None:
        q = q.where(Transfer.is_draft.is_(is_draft))
    rows, total = paginate(db, q, page, {"code": Transfer.code, "date": Transfer.date, "client": Client.name}, "-date")
    return {"items": [transfer_row(t) for t in rows], "total": total}


@router.get("/{transfer_id}")
def get_load_in(transfer_id: uuid.UUID, db: Session = Depends(get_db)):
    return transfer_detail(db, _load_in(db, transfer_id))


@router.post("")
def create(body: LoadInIn, db: Session = Depends(get_db), actor: Actor = Depends(current_actor)):
    return _save(db, actor, body)


@router.put("/{transfer_id}")
def update(transfer_id: uuid.UUID, body: LoadInIn, db: Session = Depends(get_db),
           actor: Actor = Depends(current_actor)):
    return _save(db, actor, body, transfer_id)


@router.post("/{transfer_id}/complete")
def complete(transfer_id: uuid.UUID, db: Session = Depends(get_db), actor: Actor = Depends(current_actor)):
    t = set_load_in_draft(db, actor, transfer_id, False)
    db.commit()
    return transfer_detail(db, t)


@router.post("/delete", status_code=204)
def remove(body: IdsIn, db: Session = Depends(get_db), actor: Actor = Depends(current_actor)):
    delete_load_ins(db, actor, body.ids)
    db.commit()


@router.get("/{transfer_id}/remaining")
def remaining(transfer_id: uuid.UUID, db: Session = Depends(get_db)):
    """一键带出剩余（文档 10.4-2）：返回建议的出库行，remainder 为旧算法遗漏的余数。"""
    out = []
    for pick in lines_for_all_remaining(db, transfer_id):
        src = item_out(db.get(TransferItem, pick.line.source_item_id))
        out.append({"source_item_id": pick.line.source_item_id, "quantity": pick.line.quantity,
                    "unit": pick.line.unit, "remainder": pick.remainder, "source_code": src["source_code"],
                    **{k: src[k] for k in ("product_title", "batch_number", "used_by_date", "units",
                                           "remaining_stock")}})
    return out
