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

from app.db.session import get_db
from app.models.operations import PurchaseOrderItem, PurchaseReceiptItem, StockLot
from app.models.planning import MaterialDemand
from app.schemas.operations import QualityInspectionAction, QualityInspectionRead
from app.services.auth import AuthContext, require_authenticated_user
from app.services.document_archives import DOCUMENT_TYPE_QUALITY_INSPECTION, generate_document_archive
from app.services.operations import (
    get_quality_inspections_query,
    recalc_purchase_order_item_received_weight,
    sync_purchase_order_status,
    sync_purchase_receipt_status,
    to_decimal,
    upsert_stock_balance,
)

router = APIRouter(dependencies=[Depends(require_authenticated_user)])


def _auth_context_user_id(context: AuthContext | object) -> int | None:
    user = getattr(context, "user", None)
    user_id = getattr(user, "id", None)
    return int(user_id) if user_id is not None else None


def _read_quality_inspection(db: Session, receipt_item_id: int) -> QualityInspectionRead:
    row = db.execute(get_quality_inspections_query(limit=1).where(PurchaseReceiptItem.id == receipt_item_id)).mappings().first()
    if not row:
        raise HTTPException(status_code=500, detail="质检结果保存后读取失败")
    return QualityInspectionRead.model_validate(dict(row))


def _read_quality_inspection_after_archive_generation(
    db: Session,
    receipt_item_id: int,
    *,
    created_by: int | None,
) -> QualityInspectionRead:
    try:
        archive_kwargs = {"created_by": created_by} if created_by is not None else {}
        archive_result = generate_document_archive(
            db,
            DOCUMENT_TYPE_QUALITY_INSPECTION,
            int(receipt_item_id),
            **archive_kwargs,
        )
        archive_fields = {
            "archive_status": archive_result.archive_status,
            "archive_version": archive_result.archive_version,
            "archive_error_message": archive_result.archive_error_message,
        }
    except Exception as exc:
        try:
            db.rollback()
        except Exception:
            pass
        archive_fields = {
            "archive_status": "归档失败",
            "archive_version": None,
            "archive_error_message": f"归档失败：{exc}",
        }
    return _read_quality_inspection(db, receipt_item_id).model_copy(update=archive_fields)


@router.get("/receipt-inspections", response_model=list[QualityInspectionRead])
def list_receipt_inspections(
    pending_only: bool = Query(default=False),
    limit: int = Query(default=100, ge=1, le=500),
    db: Session = Depends(get_db),
) -> list[QualityInspectionRead]:
    rows = db.execute(get_quality_inspections_query(limit=limit, pending_only=pending_only)).mappings().all()
    return [QualityInspectionRead.model_validate(dict(row)) for row in rows]


@router.post("/receipt-inspections/{receipt_item_id}", response_model=QualityInspectionRead)
def inspect_receipt_item(
    receipt_item_id: int,
    payload: QualityInspectionAction,
    context: AuthContext = Depends(require_authenticated_user),
    db: Session = Depends(get_db),
) -> QualityInspectionRead:
    receipt_item = db.get(PurchaseReceiptItem, receipt_item_id)
    if not receipt_item:
        raise HTTPException(status_code=404, detail="到货明细不存在")

    lot = db.scalar(
        select(StockLot).where(
            StockLot.source_doc_type == "PURCHASE_RECEIPT",
            StockLot.source_doc_id == receipt_item.receipt_id,
            StockLot.source_line_id == receipt_item.id,
        )
    )
    merged_into_existing_lot = False
    if not lot and receipt_item.merge_to_lot_id:
        lot = db.get(StockLot, receipt_item.merge_to_lot_id)
        merged_into_existing_lot = True
    if not lot:
        raise HTTPException(status_code=404, detail="对应批次不存在")

    po_item = db.get(PurchaseOrderItem, receipt_item.purchase_order_item_id)
    if not po_item:
        raise HTTPException(status_code=404, detail="采购订单明细不存在")

    result = payload.result.upper()
    if result not in {"PASS", "REJECT"}:
        raise HTTPException(status_code=400, detail="质检结果只支持 PASS 或 REJECT")

    if result == "PASS":
        receipt_qty = to_decimal(receipt_item.received_qty)
        receipt_weight = to_decimal(receipt_item.received_weight_kg)
        receipt_item.accepted_qty = receipt_qty
        receipt_item.accepted_weight_kg = receipt_weight
        receipt_item.status = "PASSED"
        lot.quality_status = "PASS"
        lot.status = "AVAILABLE"
        if merged_into_existing_lot:
            lot.locked_qty = max(to_decimal(0), to_decimal(lot.locked_qty) - receipt_qty)
            lot.locked_weight_kg = max(
                to_decimal(0),
                to_decimal(lot.locked_weight_kg) - receipt_weight,
            )
        else:
            lot.locked_qty = to_decimal(0)
            lot.locked_weight_kg = to_decimal(0)
        upsert_stock_balance(
            db,
            item_id=lot.item_id,
            warehouse_id=lot.warehouse_id,
            location_id=lot.location_id,
            qty_delta=to_decimal(0),
            weight_delta=to_decimal(0),
            available_qty_delta=receipt_qty,
            available_weight_delta=receipt_weight,
            unit_cost=to_decimal(lot.unit_cost, "0.0001"),
        )
    else:
        receipt_item.accepted_qty = to_decimal(0)
        receipt_item.accepted_weight_kg = to_decimal(0)
        receipt_item.status = "REJECTED"
        if merged_into_existing_lot:
            reject_qty = to_decimal(receipt_item.received_qty)
            reject_weight = to_decimal(receipt_item.received_weight_kg)
            lot.inbound_qty = max(to_decimal(0), to_decimal(lot.inbound_qty) - reject_qty)
            lot.inbound_weight_kg = max(to_decimal(0), to_decimal(lot.inbound_weight_kg) - reject_weight)
            lot.remaining_qty = max(to_decimal(0), to_decimal(lot.remaining_qty) - reject_qty)
            lot.remaining_weight_kg = max(to_decimal(0), to_decimal(lot.remaining_weight_kg) - reject_weight)
            lot.locked_qty = max(to_decimal(0), to_decimal(lot.locked_qty) - reject_qty)
            lot.locked_weight_kg = max(to_decimal(0), to_decimal(lot.locked_weight_kg) - reject_weight)
            if lot.remaining_weight_kg <= 0 and lot.remaining_qty <= 0:
                lot.status = "DEPLETED"
            upsert_stock_balance(
                db,
                item_id=lot.item_id,
                warehouse_id=lot.warehouse_id,
                location_id=lot.location_id,
                qty_delta=-reject_qty,
                weight_delta=-reject_weight,
                available_qty_delta=to_decimal(0),
                available_weight_delta=to_decimal(0),
                unit_cost=to_decimal(lot.unit_cost, "0.0001"),
            )
        else:
            lot.quality_status = "REJECT"
            lot.status = "REJECTED"
            lot.locked_qty = to_decimal(lot.remaining_qty)
            lot.locked_weight_kg = to_decimal(lot.remaining_weight_kg)

    receipt_item.remark = payload.remark or receipt_item.remark
    lot.remark = context.employee.employee_name
    db.add(receipt_item)
    db.add(lot)
    db.flush()
    po_item = recalc_purchase_order_item_received_weight(db, po_item.id) or po_item
    if po_item.source_demand_id and po_item.status == "CLOSED":
        demand = db.get(MaterialDemand, po_item.source_demand_id)
        if demand:
            demand.status = "RECEIVED"
            db.add(demand)
    sync_purchase_receipt_status(db, receipt_item.receipt_id)
    sync_purchase_order_status(db, po_item.purchase_order_id)
    db.commit()

    return _read_quality_inspection_after_archive_generation(
        db,
        receipt_item_id,
        created_by=_auth_context_user_id(context),
    )
