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, aliased

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.routes_load_ins import batch_filter
from flyway.api.views import transfer_detail, transfer_row
from flyway.domain.actor import Actor
from flyway.models import Client, Transfer
from flyway.services.transfer import TransferHeader, TransferLine, delete_local_transfers, save_local_transfer

router = APIRouter(prefix="/api/transfers")


class LineIn(BaseModel):
    source_item_id: uuid.UUID
    quantity: int
    unit: int = 1
    reference: str | None = None
    id: uuid.UUID | None = None


class TransferIn(BaseModel):
    from_client_id: uuid.UUID
    to_client_id: uuid.UUID
    code: str
    date: dt.datetime
    notes: str | None = None
    load_in_ids: list[uuid.UUID] = []
    lines: list[LineIn]


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


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


@router.get("")
def list_transfers(keyword: str | None = None, batch: str | None = None, from_date: dt.date | None = None,
                   to_date: dt.date | None = None, from_client_id: uuid.UUID | None = None,
                   to_client_id: uuid.UUID | None = None, page: Page = Depends(page_params),
                   db: Session = Depends(get_db)):
    inn = aliased(Transfer)
    q = select(Transfer).join(Client).where(Transfer.is_load_out.is_(True), Transfer.is_local.is_(True))
    if keyword:
        q = q.where(like(Transfer.code, keyword))
    if batch:
        q = batch_filter(q, batch)
    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_client_id:
        q = q.where(Transfer.client_id == from_client_id)
    if to_client_id:
        q = q.where(Transfer.id.in_(select(inn.transfer_from_id).where(inn.client_id == to_client_id)))
    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_transfer(transfer_id: uuid.UUID, db: Session = Depends(get_db)):
    return transfer_detail(db, _transfer(db, transfer_id))


@router.post("")
def create(body: TransferIn, 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: TransferIn, db: Session = Depends(get_db),
           actor: Actor = Depends(current_actor)):
    return _save(db, actor, body, transfer_id)


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