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

import json
from decimal import Decimal
from uuid import UUID

from sqlalchemy import select
from sqlalchemy.ext.asyncio import AsyncSession

from app.models.posting import Voucher, VoucherLine
from app.models.purchase_inventory import (
    InventoryAccountingConfiguration,
    InventoryCostAdjustment,
    InventoryPostingProfile,
    InventoryValuationHistory,
    PurchasePostingProfile,
)
from app.models.sales_accounting import AccountingPreview
from app.models.types import (
    AccountCategory,
    AccountType,
    InventoryDocumentType,
    InventoryValuationMethod,
    PurchaseDocumentType,
    VoucherStatus,
)
from app.repositories.base import TenantBaseRepository
from app.repositories.foundation import FiscalPeriodRepository
from app.repositories.posting import VoucherRepository
from app.services.posting_engine import PostingEngine
from shared.exceptions import NotFoundError


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


class InventoryProfileRepo(TenantBaseRepository[InventoryPostingProfile]):
    model = InventoryPostingProfile


class InventoryValuationHistoryRepo(TenantBaseRepository[InventoryValuationHistory]):
    model = InventoryValuationHistory


class InventoryCostAdjustmentRepo(TenantBaseRepository[InventoryCostAdjustment]):
    model = InventoryCostAdjustment


class InventoryValuationEngine:
    """Valuation calculations separated from Posting Engine."""

    def calculate_fifo(self, layers: list[tuple[Decimal, Decimal]], issue_qty: Decimal) -> Decimal:
        remaining = issue_qty
        total_cost = Decimal("0")
        for qty, cost in layers:
            if remaining <= 0:
                break
            take = min(remaining, qty)
            total_cost += take * cost
            remaining -= take
        return total_cost

    def calculate_weighted_average(self, total_qty: Decimal, total_value: Decimal, new_qty: Decimal, new_cost: Decimal) -> Decimal:
        if total_qty + new_qty == 0:
            return Decimal("0")
        return (total_value + new_qty * new_cost) / (total_qty + new_qty)

    def calculate_moving_average(self, current_avg: Decimal, current_qty: Decimal, new_qty: Decimal, new_cost: Decimal) -> Decimal:
        return self.calculate_weighted_average(current_qty, current_avg * current_qty, new_qty, new_cost)


class PurchaseInventoryAccountingService:
    """Purchase and inventory accounting via Posting Engine only."""

    def __init__(self, session: AsyncSession) -> None:
        self.session = session
        self.purchase_profile_repo = PurchaseProfileRepo(session)
        self.inventory_profile_repo = InventoryProfileRepo(session)
        self.voucher_repo = VoucherRepository(session)
        self.period_repo = FiscalPeriodRepository(session)
        self.posting_engine = PostingEngine(session)
        self.valuation_engine = InventoryValuationEngine()
        self.valuation_history_repo = InventoryValuationHistoryRepo(session)
        self.cost_adjustment_repo = InventoryCostAdjustmentRepo(session)

    async def preview_goods_receipt(
        self, tenant_id: UUID, amount: Decimal, profile_id: UUID | None = None
    ) -> AccountingPreview:
        profile = await self._resolve_purchase_profile(tenant_id, profile_id)
        lines = []
        if profile.inventory_account_id:
            lines.append({"account_id": str(profile.inventory_account_id), "debit": str(amount), "credit": "0"})
        if profile.grni_account_id:
            lines.append({"account_id": str(profile.grni_account_id), "debit": "0", "credit": str(amount)})
        total_debit = sum(Decimal(l["debit"]) for l in lines)
        total_credit = sum(Decimal(l["credit"]) for l in lines)
        preview = AccountingPreview(
            tenant_id=tenant_id,
            source_module="purchase",
            source_document_type=PurchaseDocumentType.GOODS_RECEIPT.value,
            source_document_id="preview",
            preview_data=json.dumps(lines),
            is_balanced=total_debit == total_credit,
            total_debit=total_debit,
            total_credit=total_credit,
        )
        self.session.add(preview)
        await self.session.flush()
        return preview

    async def post_goods_receipt(
        self,
        tenant_id: UUID,
        *,
        source_document_id: str,
        amount: Decimal,
        actor_user_id: str,
        profile_id: UUID | None = None,
        credit_mode: str = "supplier",
        credit_account_id: UUID | None = None,
        warehouse_name: str | None = None,
        source_note: str | None = None,
        party_name: str | None = None,
    ) -> Voucher:
        from datetime import date

        from app.services.document_number_service import DocumentNumberService
        from app.services.source_posting_guard import SourcePostingGuard
        from shared.exceptions import AppError

        guard = SourcePostingGuard(self.session)
        await guard.assert_can_post(
            tenant_id,
            source_module="purchase",
            source_document_type=PurchaseDocumentType.GOODS_RECEIPT.value,
            source_document_id=source_document_id,
        )

        profile = await self._resolve_purchase_profile(tenant_id, profile_id)
        period = await self.period_repo.get_current(tenant_id)
        if period is None:
            raise NotFoundError("دوره مالی جاری یافت نشد", error_code="period_not_found")

        if profile.inventory_account_id is None:
            raise AppError(
                "حساب موجودی در پروفایل خرید مشخص نیست",
                status_code=422,
                error_code="inventory_account_missing",
            )

        mode = (credit_mode or "grni").strip().lower()
        if credit_account_id is not None:
            credit_id = credit_account_id
            credit_label = "حساب انتخاب‌شده"
        elif mode == "supplier":
            credit_id = profile.liability_account_id
            credit_label = "تأمین‌کننده / حساب پرداختنی"
            if credit_id is None:
                raise AppError(
                    "حساب پرداختنی (بدهی تأمین‌کننده) در پروفایل خرید مشخص نیست",
                    status_code=422,
                    error_code="liability_account_missing",
                )
        else:
            credit_id = profile.grni_account_id or profile.liability_account_id
            credit_label = "GRNI" if profile.grni_account_id else "تأمین‌کننده / حساب پرداختنی"
            if credit_id is None:
                raise AppError(
                    "حساب GRNI یا حساب پرداختنی در پروفایل خرید مشخص نیست",
                    status_code=422,
                    error_code="credit_account_missing",
                )

        parts = [f"رسید کالا {source_document_id}"]
        if party_name:
            parts.append(f"از: {party_name}")
        if warehouse_name:
            parts.append(f"انبار: {warehouse_name}")
        parts.append(f"بستانکار: {credit_label}")
        if source_note:
            parts.append(source_note)
        description = " — ".join(parts)

        number = await DocumentNumberService(self.session).allocate(tenant_id, "voucher")
        voucher = Voucher(
            tenant_id=tenant_id,
            fiscal_period_id=period.id,
            voucher_number=number,
            voucher_date=date.today(),
            status=VoucherStatus.DRAFT,
            description=description,
            source_module="purchase",
            reference_number=source_document_id,
            created_by=actor_user_id,
        )
        await self.voucher_repo.add(voucher)

        self.session.add(
            VoucherLine(
                tenant_id=tenant_id,
                voucher_id=voucher.id,
                line_number=1,
                account_id=profile.inventory_account_id,
                debit=amount,
                credit=Decimal("0"),
                description=f"ورود به انبار{f' — {warehouse_name}' if warehouse_name else ''}",
            )
        )
        self.session.add(
            VoucherLine(
                tenant_id=tenant_id,
                voucher_id=voucher.id,
                line_number=2,
                account_id=credit_id,
                debit=Decimal("0"),
                credit=amount,
                description=f"بستانکار {credit_label}"
                + (f" — {party_name}" if party_name else ""),
            )
        )
        await self.session.flush()
        if await guard.should_post_immediately(tenant_id):
            voucher = await self.posting_engine.post_voucher(
                tenant_id, voucher.id, actor_user_id=actor_user_id, source_module="purchase"
            )
        await guard.mark_posted(
            tenant_id,
            voucher_id=voucher.id,
            source_module="purchase",
            source_document_type=PurchaseDocumentType.GOODS_RECEIPT.value,
            source_document_id=source_document_id,
        )
        return voucher

    async def preview_purchase_invoice(
        self, tenant_id: UUID, amount: Decimal, profile_id: UUID | None = None
    ) -> AccountingPreview:
        profile = await self._resolve_purchase_profile(tenant_id, profile_id)
        lines = []
        # Clear GRNI / expense and credit AP liability
        debit_account = profile.grni_account_id or profile.expense_account_id or profile.inventory_account_id
        credit_account = profile.liability_account_id
        if debit_account:
            lines.append({"account_id": str(debit_account), "debit": str(amount), "credit": "0"})
        if credit_account:
            lines.append({"account_id": str(credit_account), "debit": "0", "credit": str(amount)})
        total_debit = sum(Decimal(l["debit"]) for l in lines)
        total_credit = sum(Decimal(l["credit"]) for l in lines)
        preview = AccountingPreview(
            tenant_id=tenant_id,
            source_module="purchase",
            source_document_type=PurchaseDocumentType.SUPPLIER_INVOICE.value,
            source_document_id="preview",
            preview_data=json.dumps(lines),
            is_balanced=total_debit == total_credit and len(lines) >= 2,
            total_debit=total_debit,
            total_credit=total_credit,
        )
        self.session.add(preview)
        await self.session.flush()
        return preview

    async def post_purchase_invoice(
        self,
        tenant_id: UUID,
        *,
        source_document_id: str,
        amount: Decimal,
        actor_user_id: str,
        profile_id: UUID | None = None,
        party_id: UUID | None = None,
        party_name: str | None = None,
    ) -> Voucher:
        from datetime import date

        from app.services.document_number_service import DocumentNumberService
        from app.services.source_posting_guard import SourcePostingGuard
        from shared.exceptions import AppError

        guard = SourcePostingGuard(self.session)
        await guard.assert_can_post(
            tenant_id,
            source_module="purchase",
            source_document_type=PurchaseDocumentType.SUPPLIER_INVOICE.value,
            source_document_id=source_document_id,
        )

        profile = await self._resolve_purchase_profile(tenant_id, profile_id)
        period = await self.period_repo.get_current(tenant_id)
        if period is None:
            raise NotFoundError("دوره مالی جاری یافت نشد", error_code="period_not_found")

        debit_account = profile.grni_account_id or profile.expense_account_id or profile.inventory_account_id
        credit_account = profile.liability_account_id
        if not debit_account or not credit_account:
            raise AppError(
                "پروفایل خرید ناقص است: حساب بدهکار (GRNI/هزینه) و بستانکار (بدهی) لازم است",
                status_code=422,
                error_code="purchase_profile_incomplete",
            )

        number = await DocumentNumberService(self.session).allocate(tenant_id, "voucher")
        desc = f"فاکتور خرید {source_document_id}"
        if party_name:
            desc = f"فاکتور خرید — تأمین‌کننده: {party_name}"
        voucher = Voucher(
            tenant_id=tenant_id,
            fiscal_period_id=period.id,
            voucher_number=number,
            voucher_date=date.today(),
            status=VoucherStatus.DRAFT,
            description=desc,
            source_module="purchase",
            reference_number=source_document_id,
            created_by=actor_user_id,
        )
        await self.voucher_repo.add(voucher)
        self.session.add(
            VoucherLine(
                tenant_id=tenant_id,
                voucher_id=voucher.id,
                line_number=1,
                account_id=debit_account,
                debit=amount,
                credit=Decimal("0"),
            )
        )
        self.session.add(
            VoucherLine(
                tenant_id=tenant_id,
                voucher_id=voucher.id,
                line_number=2,
                account_id=credit_account,
                debit=Decimal("0"),
                credit=amount,
                description=f"بدهی تأمین‌کننده" + (f" — {party_name}" if party_name else ""),
            )
        )
        await self.session.flush()
        if await guard.should_post_immediately(tenant_id):
            voucher = await self.posting_engine.post_voucher(
                tenant_id, voucher.id, actor_user_id=actor_user_id, source_module="purchase"
            )
        await guard.mark_posted(
            tenant_id,
            voucher_id=voucher.id,
            source_module="purchase",
            source_document_type=PurchaseDocumentType.SUPPLIER_INVOICE.value,
            source_document_id=source_document_id,
        )
        if party_id is not None:
            await self._adjust_supplier_outstanding(tenant_id, party_id, amount)
        return voucher

    async def post_purchase_return(
        self,
        tenant_id: UUID,
        *,
        source_document_id: str,
        amount: Decimal,
        actor_user_id: str,
        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,
    ) -> Voucher:
        """Purchase return: Dr AP liability (reduce debt) / Cr inventory.

        Must be linked to a purchase invoice so party and items are traceable.
        """
        from datetime import date

        from app.services.document_number_service import DocumentNumberService
        from app.services.source_posting_guard import SourcePostingGuard
        from shared.exceptions import AppError

        doc_type_value = PurchaseDocumentType.PURCHASE_RETURN.value

        guard = SourcePostingGuard(self.session)
        await guard.assert_can_post(
            tenant_id,
            source_module="purchase",
            source_document_type=doc_type_value,
            source_document_id=source_document_id,
        )

        if amount <= 0:
            raise AppError("مبلغ مرجوعی باید بزرگ‌تر از صفر باشد", status_code=422, error_code="return_amount")

        profile = await self._resolve_purchase_profile(tenant_id, profile_id)
        period = await self.period_repo.get_current(tenant_id)
        if period is None:
            raise NotFoundError("دوره مالی جاری یافت نشد", error_code="period_not_found")

        debit_account = profile.liability_account_id
        credit_account = profile.inventory_account_id or profile.grni_account_id or profile.expense_account_id
        if not debit_account or not credit_account:
            raise AppError(
                "پروفایل خرید ناقص است: حساب بدهی و موجودی/GRNI برای مرجوعی لازم است",
                status_code=422,
                error_code="purchase_profile_incomplete",
            )

        parts = ["مرجوعی خرید"]
        if source_invoice_number:
            parts.append(f"فاکتور {source_invoice_number}")
        elif source_invoice_id:
            parts.append(f"فاکتور {source_invoice_id}")
        if party_name:
            parts.append(f"تأمین‌کننده: {party_name}")
        if lines_summary:
            parts.append(lines_summary)
        description = " — ".join(parts)

        number = await DocumentNumberService(self.session).allocate(tenant_id, "voucher")
        voucher = Voucher(
            tenant_id=tenant_id,
            fiscal_period_id=period.id,
            voucher_number=number,
            voucher_date=date.today(),
            status=VoucherStatus.DRAFT,
            description=description,
            source_module="purchase",
            reference_number=source_invoice_id or source_document_id,
            created_by=actor_user_id,
        )
        await self.voucher_repo.add(voucher)
        self.session.add(
            VoucherLine(
                tenant_id=tenant_id,
                voucher_id=voucher.id,
                line_number=1,
                account_id=debit_account,
                debit=amount,
                credit=Decimal("0"),
                description=f"کاهش بدهی تأمین‌کننده"
                + (f" — {party_name}" if party_name else ""),
            )
        )
        self.session.add(
            VoucherLine(
                tenant_id=tenant_id,
                voucher_id=voucher.id,
                line_number=2,
                account_id=credit_account,
                debit=Decimal("0"),
                credit=amount,
                description="خروج موجودی بابت مرجوعی"
                + (f" — {lines_summary}" if lines_summary else ""),
            )
        )
        await self.session.flush()
        if await guard.should_post_immediately(tenant_id):
            voucher = await self.posting_engine.post_voucher(
                tenant_id, voucher.id, actor_user_id=actor_user_id, source_module="purchase"
            )
        await guard.mark_posted(
            tenant_id,
            voucher_id=voucher.id,
            source_module="purchase",
            source_document_type=doc_type_value,
            source_document_id=source_document_id,
        )

        # Reduce supplier payable balance (our debt to them goes down)
        if party_id is not None:
            await self._adjust_supplier_outstanding(tenant_id, party_id, -amount)
        return voucher

    async def _adjust_supplier_outstanding(
        self, tenant_id: UUID, supplier_id: UUID, delta: Decimal
    ) -> None:
        from sqlalchemy import select

        from app.models.receivable_payable import SupplierAccount

        result = await self.session.execute(
            select(SupplierAccount).where(
                SupplierAccount.tenant_id == tenant_id,
                SupplierAccount.id == supplier_id,
            )
        )
        supplier = result.scalar_one_or_none()
        if supplier is None:
            return
        supplier.outstanding_balance = (supplier.outstanding_balance or Decimal("0")) + delta
        if supplier.outstanding_balance < 0:
            supplier.outstanding_balance = Decimal("0")

    async def post_inventory_issue(
        self,
        tenant_id: UUID,
        *,
        source_document_id: str,
        amount: Decimal,
        reason: str,
        debit_account_id: UUID,
        actor_user_id: str,
        profile_id: UUID | None = None,
        credit_account_id: UUID | None = None,
        barter_debit_account_id: UUID | None = None,
        barter_amount: Decimal | None = None,
    ):
        """Issue inventory to expense/charity/consumption/barter via Posting Engine.

        Standard: Dr expense (or barter inventory) / Cr inventory.
        Barter optional second pair when receiving another item of different value.
        """
        from datetime import date

        from app.services.document_number_service import DocumentNumberService
        from app.services.source_posting_guard import SourcePostingGuard
        from shared.exceptions import AppError

        guard = SourcePostingGuard(self.session)
        await guard.assert_can_post(
            tenant_id,
            source_module="inventory",
            source_document_type=f"issue_{reason}",
            source_document_id=source_document_id,
        )

        profile = await self._resolve_purchase_profile(tenant_id, profile_id)
        inventory_credit = credit_account_id or profile.inventory_account_id
        if inventory_credit is None:
            raise AppError(
                "حساب موجودی برای خروج کالا مشخص نیست (پروفایل خرید یا انتخاب دستی)",
                status_code=422,
                error_code="inventory_account_missing",
            )
        if amount <= 0:
            raise AppError("مبلغ خروج باید بزرگ‌تر از صفر باشد", status_code=422, error_code="issue_amount")

        period = await self.period_repo.get_current(tenant_id)
        if period is None:
            raise NotFoundError("دوره مالی جاری یافت نشد", error_code="period_not_found")

        reason_fa = {
            "expense": "هزینه",
            "charity": "خیرات/اهدایی",
            "consumption": "مصرف داخلی",
            "barter": "تهاتر/معاوضه",
        }.get(reason, reason)

        number = await DocumentNumberService(self.session).allocate(tenant_id, "voucher")
        voucher = Voucher(
            tenant_id=tenant_id,
            fiscal_period_id=period.id,
            voucher_number=number,
            voucher_date=date.today(),
            status=VoucherStatus.DRAFT,
            description=f"خروج موجودی — {reason_fa} — {source_document_id}",
            source_module="inventory",
            reference_number=source_document_id,
            created_by=actor_user_id,
        )
        await self.voucher_repo.add(voucher)

        if reason == "barter" and barter_debit_account_id and barter_amount and barter_amount > 0:
            # Give stock (Cr inventory), receive other stock/value (Dr), difference to expense/income account
            self.session.add(
                VoucherLine(
                    tenant_id=tenant_id,
                    voucher_id=voucher.id,
                    line_number=1,
                    account_id=inventory_credit,
                    debit=Decimal("0"),
                    credit=amount,
                    description=f"خروج کالا — {reason_fa}",
                )
            )
            self.session.add(
                VoucherLine(
                    tenant_id=tenant_id,
                    voucher_id=voucher.id,
                    line_number=2,
                    account_id=barter_debit_account_id,
                    debit=barter_amount,
                    credit=Decimal("0"),
                    description="کالای/ارزش دریافتی تهاتر",
                )
            )
            diff = amount - barter_amount
            if diff > 0:
                self.session.add(
                    VoucherLine(
                        tenant_id=tenant_id,
                        voucher_id=voucher.id,
                        line_number=3,
                        account_id=debit_account_id,
                        debit=diff,
                        credit=Decimal("0"),
                        description="مابه‌التفاوت تهاتر (هزینه)",
                    )
                )
            elif diff < 0:
                self.session.add(
                    VoucherLine(
                        tenant_id=tenant_id,
                        voucher_id=voucher.id,
                        line_number=3,
                        account_id=debit_account_id,
                        debit=Decimal("0"),
                        credit=-diff,
                        description="مابه‌التفاوت تهاتر (درآمد/تخفیف)",
                    )
                )
        else:
            # expense / charity / consumption: Dr target account, Cr inventory
            self.session.add(
                VoucherLine(
                    tenant_id=tenant_id,
                    voucher_id=voucher.id,
                    line_number=1,
                    account_id=debit_account_id,
                    debit=amount,
                    credit=Decimal("0"),
                    description=reason_fa,
                )
            )
            self.session.add(
                VoucherLine(
                    tenant_id=tenant_id,
                    voucher_id=voucher.id,
                    line_number=2,
                    account_id=inventory_credit,
                    debit=Decimal("0"),
                    credit=amount,
                    description=reason_fa,
                )
            )

        await self.session.flush()
        if await guard.should_post_immediately(tenant_id):
            voucher = await self.posting_engine.post_voucher(
                tenant_id, voucher.id, actor_user_id=actor_user_id, source_module="inventory"
            )
        await guard.mark_posted(
            tenant_id,
            voucher_id=voucher.id,
            source_module="inventory",
            source_document_type=f"issue_{reason}",
            source_document_id=source_document_id,
        )
        return voucher

    async def record_valuation(
        self,
        tenant_id: UUID,
        item_id: UUID,
        quantity: Decimal,
        unit_cost: Decimal,
        method: InventoryValuationMethod,
        warehouse_id: UUID | None = None,
    ) -> InventoryValuationHistory:
        from datetime import date

        record = InventoryValuationHistory(
            tenant_id=tenant_id,
            item_id=item_id,
            warehouse_id=warehouse_id,
            valuation_date=date.today(),
            method=method,
            quantity=quantity,
            unit_cost=unit_cost,
            total_value=quantity * unit_cost,
        )
        return await self.valuation_history_repo.add(record)

    async def _resolve_purchase_profile(
        self, tenant_id: UUID, profile_id: UUID | None
    ) -> PurchasePostingProfile:
        if profile_id:
            profile = await self.purchase_profile_repo.get(tenant_id, profile_id)
            if profile is None:
                raise NotFoundError("پروفایل خرید یافت نشد", error_code="profile_not_found")
            return profile
        stmt = select(PurchasePostingProfile).where(
            PurchasePostingProfile.tenant_id == tenant_id,
            PurchasePostingProfile.is_default.is_(True),
            PurchasePostingProfile.is_active.is_(True),
        )
        result = await self.session.execute(stmt)
        profile = result.scalar_one_or_none()
        if profile is not None:
            return profile

        # Any active profile → promote as default
        any_stmt = (
            select(PurchasePostingProfile)
            .where(
                PurchasePostingProfile.tenant_id == tenant_id,
                PurchasePostingProfile.is_active.is_(True),
            )
            .order_by(PurchasePostingProfile.created_at.asc())
            .limit(1)
        )
        any_result = await self.session.execute(any_stmt)
        existing = any_result.scalar_one_or_none()
        if existing is not None:
            existing.is_default = True
            await self.session.flush()
            return existing

        return await self.ensure_default_purchase_profile(tenant_id)

    async def ensure_default_purchase_profile(self, tenant_id: UUID) -> PurchasePostingProfile:
        """Create (or return) a default purchase posting profile with best-effort GL mapping."""
        stmt = select(PurchasePostingProfile).where(
            PurchasePostingProfile.tenant_id == tenant_id,
            PurchasePostingProfile.is_default.is_(True),
        )
        result = await self.session.execute(stmt)
        existing = result.scalar_one_or_none()
        if existing is not None:
            return existing

        inventory_id = await self._find_account_id(
            tenant_id,
            name_needles=("موجودی کالا", "موجودی", "انبار", "کالا"),
            account_types=(AccountType.ASSET,),
        )
        liability_id = await self._find_account_id(
            tenant_id,
            name_needles=("حساب‌های پرداختنی", "حسابهای پرداختنی", "پرداختنی", "بستانکاران"),
            account_types=(AccountType.LIABILITY,),
        )
        grni_id = await self._find_account_id(
            tenant_id,
            name_needles=("GRNI", "کالای در راه", "رسید در راه", "خرید در راه"),
            account_types=(AccountType.LIABILITY, AccountType.ASSET),
        )
        expense_id = await self._find_account_id(
            tenant_id,
            name_needles=("بهای تمام‌شده", "بهای تمام شده", "خرید کالا", "هزینه خرید"),
            account_types=(AccountType.EXPENSE,),
        )

        # Create missing core accounts on default chart when possible
        if inventory_id is None:
            inventory_id = await self._create_system_account(
                tenant_id,
                code="1190",
                name="موجودی کالا",
                account_type=AccountType.ASSET,
                account_category=AccountCategory.CURRENT_ASSET,
            )
        if liability_id is None:
            liability_id = await self._create_system_account(
                tenant_id,
                code="2101",
                name="حساب‌های پرداختنی",
                account_type=AccountType.LIABILITY,
                account_category=AccountCategory.CURRENT_LIABILITY,
            )
        if grni_id is None:
            grni_id = await self._create_system_account(
                tenant_id,
                code="2105",
                name="کالای در راه (GRNI)",
                account_type=AccountType.LIABILITY,
                account_category=AccountCategory.CURRENT_LIABILITY,
            )

        profile = PurchasePostingProfile(
            tenant_id=tenant_id,
            name="پروفایل پیش‌فرض خرید",
            inventory_account_id=inventory_id,
            grni_account_id=grni_id or liability_id,
            expense_account_id=expense_id,
            liability_account_id=liability_id,
            is_default=True,
            is_active=True,
        )
        await self.purchase_profile_repo.add(profile)
        await self.session.flush()
        return profile

    async def _find_account_id(
        self,
        tenant_id: UUID,
        *,
        name_needles: tuple[str, ...],
        account_types: tuple[AccountType, ...],
    ) -> UUID | None:
        from app.models.foundation import Account
        from app.models.types import AccountStatus

        stmt = select(Account).where(
            Account.tenant_id == tenant_id,
            Account.is_postable.is_(True),
            Account.is_group.is_(False),
            Account.status == AccountStatus.ACTIVE,
            Account.account_type.in_(account_types),
        )
        result = await self.session.execute(stmt)
        rows = list(result.scalars().all())
        lowered = [(a, (a.name or "").lower()) for a in rows]
        for needle in name_needles:
            n = needle.lower()
            for acc, name in lowered:
                if n in name:
                    return acc.id
        return rows[0].id if rows else None

    async def _create_system_account(
        self,
        tenant_id: UUID,
        *,
        code: str,
        name: str,
        account_type: AccountType,
        account_category: AccountCategory,
    ) -> UUID | None:
        from app.models.foundation import Account, ChartOfAccounts
        from app.models.types import AccountStatus

        chart_stmt = select(ChartOfAccounts).where(
            ChartOfAccounts.tenant_id == tenant_id,
            ChartOfAccounts.is_active.is_(True),
        ).order_by(ChartOfAccounts.is_default.desc()).limit(1)
        chart = (await self.session.execute(chart_stmt)).scalar_one_or_none()
        if chart is None:
            return None

        existing = (
            await self.session.execute(
                select(Account).where(Account.tenant_id == tenant_id, Account.code == code)
            )
        ).scalar_one_or_none()
        if existing is not None:
            return existing.id

        # Avoid unique name collisions by suffixing if needed
        final_code = code
        for i in range(0, 20):
            candidate = code if i == 0 else f"{code}-{i}"
            clash = (
                await self.session.execute(
                    select(Account.id).where(Account.tenant_id == tenant_id, Account.code == candidate)
                )
            ).scalar_one_or_none()
            if clash is None:
                final_code = candidate
                break

        acc = Account(
            tenant_id=tenant_id,
            chart_id=chart.id,
            code=final_code,
            name=name,
            account_type=account_type,
            account_category=account_category,
            level=3,
            is_group=False,
            is_postable=True,
            status=AccountStatus.ACTIVE,
            description="ایجاد خودکار برای پروفایل خرید",
        )
        self.session.add(acc)
        await self.session.flush()
        return acc.id
