"""AR/AP API — Phase 5.5."""
from __future__ import annotations

from datetime import date
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.receivable_payable import CustomerAccount, ReceivableInvoice, SupplierAccount
from app.models.types import InvoiceStatus, SettlementStatus
from app.repositories.base import TenantBaseRepository
from app.services.aging_engine import AgingEngine
from app.services.settlement_engine import SettlementEngine, SettlementRepository
from shared.security import CurrentUser

router = APIRouter()


class CustomerCreate(BaseModel):
    code: str | None = None
    name: str
    credit_limit: Decimal | None = None


class CustomerRead(BaseModel):
    id: UUID
    tenant_id: UUID
    code: str
    name: str
    outstanding_balance: Decimal
    is_active: bool

    model_config = {"from_attributes": True}


class ReceivableInvoiceCreate(BaseModel):
    customer_id: UUID
    invoice_number: str | None = None
    invoice_date: date
    due_date: date | None = None
    total_amount: Decimal


class SettlementCreate(BaseModel):
    settlement_number: str | None = None
    settlement_date: date
    settlement_type: str
    party_type: str
    party_id: UUID
    total_amount: Decimal
    allocations: list[dict]


class CustomerRepo(TenantBaseRepository[CustomerAccount]):
    model = CustomerAccount


class SupplierRepo(TenantBaseRepository[SupplierAccount]):
    model = SupplierAccount


class ReceivableRepo(TenantBaseRepository[ReceivableInvoice]):
    model = ReceivableInvoice


@router.post("/customers", response_model=CustomerRead, status_code=status.HTTP_201_CREATED)
async def create_customer(
    body: CustomerCreate,
    tenant_id: UUID = Depends(require_tenant),
    db: AsyncSession = Depends(get_db),
    _user: CurrentUser = Depends(get_current_user),
):
    from app.services.document_number_service import DocumentNumberService

    repo = CustomerRepo(db)
    code = await DocumentNumberService(db).allocate_if_blank(tenant_id, "customer", body.code)
    entity = CustomerAccount(
        tenant_id=tenant_id,
        code=code,
        name=body.name,
        credit_limit=body.credit_limit,
    )
    await repo.add(entity)
    await db.commit()
    await db.refresh(entity)
    return entity


@router.get("/customers", response_model=list[CustomerRead])
async def list_customers(
    q: str | None = None,
    tenant_id: UUID = Depends(require_tenant),
    db: AsyncSession = Depends(get_db),
    _user: CurrentUser = Depends(get_current_user),
):
    repo = CustomerRepo(db)
    rows = await repo.list_by_tenant(tenant_id, limit=200)
    if q:
        needle = q.strip().lower()
        rows = [r for r in rows if needle in (r.code or "").lower() or needle in (r.name or "").lower()]
    return rows


@router.patch("/customers/{customer_id}", response_model=CustomerRead)
async def update_customer(
    customer_id: UUID,
    body: dict,
    tenant_id: UUID = Depends(require_tenant),
    db: AsyncSession = Depends(get_db),
    _user: CurrentUser = Depends(get_current_user),
):
    from shared.exceptions import NotFoundError

    repo = CustomerRepo(db)
    entity = await repo.get(tenant_id, customer_id)
    if entity is None:
        raise NotFoundError("مشتری یافت نشد", error_code="customer_not_found")
    if "name" in body and body["name"] is not None:
        entity.name = body["name"]
    if "credit_limit" in body:
        entity.credit_limit = body["credit_limit"]
    if "is_active" in body and body["is_active"] is not None:
        entity.is_active = bool(body["is_active"])
    await db.commit()
    await db.refresh(entity)
    return entity


@router.get("/customers/{customer_id}", response_model=CustomerRead)
async def get_customer(
    customer_id: UUID,
    tenant_id: UUID = Depends(require_tenant),
    db: AsyncSession = Depends(get_db),
    _user: CurrentUser = Depends(get_current_user),
):
    from shared.exceptions import NotFoundError

    entity = await CustomerRepo(db).get(tenant_id, customer_id)
    if entity is None:
        raise NotFoundError("مشتری یافت نشد", error_code="customer_not_found")
    return entity


@router.get("/suppliers")
async def list_suppliers(
    q: str | None = None,
    tenant_id: UUID = Depends(require_tenant),
    db: AsyncSession = Depends(get_db),
    _user: CurrentUser = Depends(get_current_user),
):
    suppliers = await SupplierRepo(db).list_by_tenant(tenant_id, limit=200)
    if q:
        needle = q.strip().lower()
        suppliers = [
            s for s in suppliers if needle in (s.code or "").lower() or needle in (s.name or "").lower()
        ]
    return [
        {
            "id": str(s.id),
            "code": s.code,
            "name": s.name,
            "outstanding_balance": str(s.outstanding_balance),
            "is_active": s.is_active,
        }
        for s in suppliers
    ]


@router.post("/suppliers", status_code=status.HTTP_201_CREATED)
async def create_supplier(
    body: CustomerCreate,
    tenant_id: UUID = Depends(require_tenant),
    db: AsyncSession = Depends(get_db),
    _user: CurrentUser = Depends(get_current_user),
):
    from app.services.document_number_service import DocumentNumberService

    repo = SupplierRepo(db)
    code = await DocumentNumberService(db).allocate_if_blank(tenant_id, "supplier", body.code)
    entity = SupplierAccount(tenant_id=tenant_id, code=code, name=body.name)
    await repo.add(entity)
    await db.commit()
    return {"id": str(entity.id), "code": entity.code, "name": entity.name}


@router.get("/receivable-invoices")
async def list_receivable_invoices(
    customer_id: UUID | None = None,
    tenant_id: UUID = Depends(require_tenant),
    db: AsyncSession = Depends(get_db),
    _user: CurrentUser = Depends(get_current_user),
):
    invoices = await ReceivableRepo(db).list_by_tenant(tenant_id, limit=200)
    rows = invoices
    if customer_id is not None:
        rows = [i for i in invoices if i.customer_id == customer_id]
    return [
        {
            "id": str(i.id),
            "customer_id": str(i.customer_id),
            "invoice_number": i.invoice_number,
            "invoice_date": i.invoice_date.isoformat(),
            "due_date": i.due_date.isoformat() if i.due_date else None,
            "total_amount": str(i.total_amount),
            "remaining_amount": str(i.remaining_amount),
            "status": i.status.value,
        }
        for i in rows
    ]


@router.post("/receivable-invoices", status_code=status.HTTP_201_CREATED)
async def create_receivable_invoice(
    body: ReceivableInvoiceCreate,
    tenant_id: UUID = Depends(require_tenant),
    db: AsyncSession = Depends(get_db),
    _user: CurrentUser = Depends(get_current_user),
):
    from app.services.document_number_service import DocumentNumberService

    repo = ReceivableRepo(db)
    invoice_number = await DocumentNumberService(db).allocate_if_blank(
        tenant_id, "ar_invoice", body.invoice_number
    )
    entity = ReceivableInvoice(
        tenant_id=tenant_id,
        customer_id=body.customer_id,
        invoice_number=invoice_number,
        invoice_date=body.invoice_date,
        due_date=body.due_date,
        total_amount=body.total_amount,
        paid_amount=Decimal("0"),
        remaining_amount=body.total_amount,
        status=InvoiceStatus.ISSUED,
    )
    await repo.add(entity)
    await db.commit()
    return {"id": str(entity.id), "invoice_number": entity.invoice_number}


@router.get("/settlements")
async def list_settlements(
    party_type: str | None = None,
    tenant_id: UUID = Depends(require_tenant),
    db: AsyncSession = Depends(get_db),
    _user: CurrentUser = Depends(get_current_user),
):
    from app.models.receivable_payable import Settlement
    from sqlalchemy import select

    stmt = select(Settlement).where(Settlement.tenant_id == tenant_id)
    if party_type:
        stmt = stmt.where(Settlement.party_type == party_type)
    stmt = stmt.order_by(Settlement.settlement_date.desc()).limit(200)
    rows = list((await db.execute(stmt)).scalars().all())
    return [
        {
            "id": str(s.id),
            "settlement_number": s.settlement_number,
            "settlement_date": s.settlement_date.isoformat(),
            "settlement_type": s.settlement_type,
            "party_type": s.party_type,
            "party_id": str(s.party_id),
            "total_amount": str(s.total_amount),
            "status": s.status.value,
        }
        for s in rows
    ]


@router.post("/settlements")
async def create_settlement(
    body: SettlementCreate,
    tenant_id: UUID = Depends(require_tenant),
    db: AsyncSession = Depends(get_db),
    _user: CurrentUser = Depends(get_current_user),
):
    from app.models.receivable_payable import Settlement
    from app.services.document_number_service import DocumentNumberService

    settlement_number = await DocumentNumberService(db).allocate_if_blank(
        tenant_id, "settlement", body.settlement_number
    )
    settlement = Settlement(
        tenant_id=tenant_id,
        settlement_number=settlement_number,
        settlement_date=body.settlement_date,
        settlement_type=body.settlement_type,
        party_type=body.party_type,
        party_id=body.party_id,
        total_amount=body.total_amount,
        status=SettlementStatus.DRAFT,
    )
    repo = SettlementRepository(db)
    await repo.add(settlement)

    engine = SettlementEngine(db)
    if body.party_type == "customer":
        settlement = await engine.allocate_receivable(tenant_id, settlement, body.allocations)
    else:
        settlement = await engine.allocate_payable(tenant_id, settlement, body.allocations)
    await db.commit()
    return {
        "id": str(settlement.id),
        "settlement_number": settlement.settlement_number,
        "status": settlement.status.value,
    }


@router.get("/customers/{customer_id}/aging")
async def get_customer_aging(
    customer_id: UUID,
    tenant_id: UUID = Depends(require_tenant),
    db: AsyncSession = Depends(get_db),
    _user: CurrentUser = Depends(get_current_user),
):
    engine = AgingEngine(db)
    snapshot = await engine.calculate_customer_aging(tenant_id, customer_id)
    await db.commit()
    return {
        "current": str(snapshot.current_amount),
        "days_1_30": str(snapshot.days_1_30),
        "days_31_60": str(snapshot.days_31_60),
        "days_61_90": str(snapshot.days_61_90),
        "days_90_plus": str(snapshot.days_90_plus),
        "total": str(snapshot.total_outstanding),
    }
