"""Phase 5.7 — Purchase & Inventory accounting API."""
from __future__ import annotations

from decimal import Decimal
from uuid import UUID

from fastapi import APIRouter, Depends, status
from pydantic import BaseModel
from sqlalchemy.ext.asyncio import AsyncSession

from app.api.deps import get_db, require_tenant
from app.core.security import get_current_user
from app.models.purchase_inventory import InventoryPostingProfile, PurchasePostingProfile
from app.models.types import InventoryValuationMethod
from app.repositories.base import TenantBaseRepository
from app.services.purchase_inventory_service import PurchaseInventoryAccountingService
from shared.security import CurrentUser

router = APIRouter()


class PurchaseProfileCreate(BaseModel):
    name: str
    inventory_account_id: UUID | None = None
    grni_account_id: UUID | None = None
    liability_account_id: UUID | None = None
    expense_account_id: UUID | None = None
    is_default: bool = False


class PurchaseProfileUpdate(BaseModel):
    name: str | None = None
    inventory_account_id: UUID | None = None
    grni_account_id: UUID | None = None
    liability_account_id: UUID | None = None
    expense_account_id: UUID | None = None
    is_default: bool | None = None
    is_active: bool | None = None


class PurchaseProfileRepo(TenantBaseRepository[PurchasePostingProfile]):
    model = PurchasePostingProfile


@router.post("/posting-profiles", status_code=status.HTTP_201_CREATED)
async def create_purchase_profile(
    body: PurchaseProfileCreate,
    tenant_id: UUID = Depends(require_tenant),
    db: AsyncSession = Depends(get_db),
    _user: CurrentUser = Depends(get_current_user),
):
    repo = PurchaseProfileRepo(db)
    data = body.model_dump()
    if data.get("is_default"):
        rows = await repo.list_by_tenant(tenant_id, limit=200)
        for r in rows:
            r.is_default = False
    elif not await repo.list_by_tenant(tenant_id, limit=1):
        data["is_default"] = True
    entity = PurchasePostingProfile(tenant_id=tenant_id, **data)
    await repo.add(entity)
    await db.commit()
    return {"id": str(entity.id), "name": entity.name}


@router.get("/posting-profiles")
async def list_purchase_profiles(
    tenant_id: UUID = Depends(require_tenant),
    db: AsyncSession = Depends(get_db),
    _user: CurrentUser = Depends(get_current_user),
):
    rows = await PurchaseProfileRepo(db).list_by_tenant(tenant_id, limit=100)
    return [
        {
            "id": str(r.id),
            "name": r.name,
            "is_default": r.is_default,
            "is_active": r.is_active,
            "inventory_account_id": str(r.inventory_account_id) if r.inventory_account_id else None,
            "grni_account_id": str(r.grni_account_id) if r.grni_account_id else None,
            "liability_account_id": str(r.liability_account_id) if r.liability_account_id else None,
            "expense_account_id": str(r.expense_account_id) if r.expense_account_id else None,
        }
        for r in rows
    ]


@router.post("/posting-profiles/ensure-default")
async def ensure_default_purchase_profile(
    tenant_id: UUID = Depends(require_tenant),
    db: AsyncSession = Depends(get_db),
    _user: CurrentUser = Depends(get_current_user),
):
    svc = PurchaseInventoryAccountingService(db)
    profile = await svc.ensure_default_purchase_profile(tenant_id)
    await db.commit()
    return {
        "id": str(profile.id),
        "name": profile.name,
        "is_default": profile.is_default,
        "inventory_account_id": str(profile.inventory_account_id) if profile.inventory_account_id else None,
        "grni_account_id": str(profile.grni_account_id) if profile.grni_account_id else None,
        "liability_account_id": str(profile.liability_account_id) if profile.liability_account_id else None,
        "expense_account_id": str(profile.expense_account_id) if profile.expense_account_id else None,
    }


@router.patch("/posting-profiles/{profile_id}")
async def update_purchase_profile(
    profile_id: UUID,
    body: PurchaseProfileUpdate,
    tenant_id: UUID = Depends(require_tenant),
    db: AsyncSession = Depends(get_db),
    _user: CurrentUser = Depends(get_current_user),
):
    from shared.exceptions import NotFoundError

    repo = PurchaseProfileRepo(db)
    entity = await repo.get(tenant_id, profile_id)
    if entity is None:
        raise NotFoundError("پروفایل خرید یافت نشد", error_code="profile_not_found")
    data = body.model_dump(exclude_unset=True)
    if data.get("is_default"):
        rows = await repo.list_by_tenant(tenant_id, limit=200)
        for r in rows:
            if r.id != entity.id:
                r.is_default = False
    for k, v in data.items():
        setattr(entity, k, v)
    await db.commit()
    return {"id": str(entity.id), "name": entity.name, "is_default": entity.is_default}


class GoodsReceiptRequest(BaseModel):
    source_document_id: str
    amount: Decimal
    profile_id: UUID | None = None
    credit_mode: str = "supplier"  # supplier | grni
    credit_account_id: UUID | None = None
    warehouse_name: str | None = None
    source_note: str | None = None
    party_name: str | None = None


class ValuationRequest(BaseModel):
    item_id: UUID
    quantity: Decimal
    unit_cost: Decimal
    method: InventoryValuationMethod = InventoryValuationMethod.WEIGHTED_AVERAGE
    warehouse_id: UUID | None = None


@router.post("/preview/goods-receipt")
async def preview_goods_receipt(
    body: GoodsReceiptRequest,
    tenant_id: UUID = Depends(require_tenant),
    db: AsyncSession = Depends(get_db),
    _user: CurrentUser = Depends(get_current_user),
):
    svc = PurchaseInventoryAccountingService(db)
    preview = await svc.preview_goods_receipt(tenant_id, body.amount, body.profile_id)
    await db.commit()
    return {"is_balanced": preview.is_balanced, "total_debit": str(preview.total_debit)}


@router.post("/post/goods-receipt")
async def post_goods_receipt(
    body: GoodsReceiptRequest,
    tenant_id: UUID = Depends(require_tenant),
    db: AsyncSession = Depends(get_db),
    user: CurrentUser = Depends(get_current_user),
):
    svc = PurchaseInventoryAccountingService(db)
    voucher = await svc.post_goods_receipt(
        tenant_id,
        source_document_id=body.source_document_id,
        amount=body.amount,
        actor_user_id=user.user_id,
        profile_id=body.profile_id,
        credit_mode=body.credit_mode,
        credit_account_id=body.credit_account_id,
        warehouse_name=body.warehouse_name,
        source_note=body.source_note,
        party_name=body.party_name,
    )
    await db.commit()
    return {"voucher_id": str(voucher.id), "voucher_number": voucher.voucher_number, "status": voucher.status.value}


@router.post("/preview/purchase-invoice")
async def preview_purchase_invoice(
    body: GoodsReceiptRequest,
    tenant_id: UUID = Depends(require_tenant),
    db: AsyncSession = Depends(get_db),
    _user: CurrentUser = Depends(get_current_user),
):
    svc = PurchaseInventoryAccountingService(db)
    preview = await svc.preview_purchase_invoice(tenant_id, body.amount, body.profile_id)
    await db.commit()
    return {
        "is_balanced": preview.is_balanced,
        "total_debit": str(preview.total_debit),
        "total_credit": str(preview.total_credit),
    }


@router.post("/post/purchase-invoice")
async def post_purchase_invoice(
    body: GoodsReceiptRequest,
    tenant_id: UUID = Depends(require_tenant),
    db: AsyncSession = Depends(get_db),
    user: CurrentUser = Depends(get_current_user),
):
    svc = PurchaseInventoryAccountingService(db)
    voucher = await svc.post_purchase_invoice(
        tenant_id,
        source_document_id=body.source_document_id,
        amount=body.amount,
        actor_user_id=user.user_id,
        profile_id=body.profile_id,
    )
    await db.commit()
    return {
        "voucher_id": str(voucher.id),
        "voucher_number": voucher.voucher_number,
        "status": voucher.status.value,
    }


class PurchaseReturnRequest(BaseModel):
    source_document_id: str
    amount: Decimal
    profile_id: UUID | None = None
    source_invoice_id: str | None = None
    source_invoice_number: str | None = None
    party_id: UUID | None = None
    party_name: str | None = None
    lines_summary: str | None = None


@router.post("/post/purchase-return")
async def post_purchase_return(
    body: PurchaseReturnRequest,
    tenant_id: UUID = Depends(require_tenant),
    db: AsyncSession = Depends(get_db),
    user: CurrentUser = Depends(get_current_user),
):
    svc = PurchaseInventoryAccountingService(db)
    voucher = await svc.post_purchase_return(
        tenant_id,
        source_document_id=body.source_document_id,
        amount=body.amount,
        actor_user_id=user.user_id,
        profile_id=body.profile_id,
        source_invoice_id=body.source_invoice_id,
        source_invoice_number=body.source_invoice_number,
        party_id=body.party_id,
        party_name=body.party_name,
        lines_summary=body.lines_summary,
    )
    await db.commit()
    return {
        "voucher_id": str(voucher.id),
        "voucher_number": voucher.voucher_number,
        "status": voucher.status.value,
    }


class InventoryIssueRequest(BaseModel):
    source_document_id: str
    amount: Decimal
    reason: str = "expense"  # expense | charity | consumption | barter
    debit_account_id: UUID
    credit_account_id: UUID | None = None
    profile_id: UUID | None = None
    barter_debit_account_id: UUID | None = None
    barter_amount: Decimal | None = None


@router.post("/post/inventory-issue")
async def post_inventory_issue(
    body: InventoryIssueRequest,
    tenant_id: UUID = Depends(require_tenant),
    db: AsyncSession = Depends(get_db),
    user: CurrentUser = Depends(get_current_user),
):
    svc = PurchaseInventoryAccountingService(db)
    voucher = await svc.post_inventory_issue(
        tenant_id,
        source_document_id=body.source_document_id,
        amount=body.amount,
        reason=body.reason,
        debit_account_id=body.debit_account_id,
        actor_user_id=user.user_id,
        profile_id=body.profile_id,
        credit_account_id=body.credit_account_id,
        barter_debit_account_id=body.barter_debit_account_id,
        barter_amount=body.barter_amount,
    )
    await db.commit()
    return {
        "voucher_id": str(voucher.id),
        "voucher_number": voucher.voucher_number,
        "status": voucher.status.value,
    }


@router.post("/valuation")
async def record_valuation(
    body: ValuationRequest,
    tenant_id: UUID = Depends(require_tenant),
    db: AsyncSession = Depends(get_db),
    _user: CurrentUser = Depends(get_current_user),
):
    svc = PurchaseInventoryAccountingService(db)
    record = await svc.record_valuation(
        tenant_id, body.item_id, body.quantity, body.unit_cost, body.method, body.warehouse_id
    )
    await db.commit()
    return {"id": str(record.id), "total_value": str(record.total_value)}
