from abc import ABC, abstractmethod
from decimal import Decimal

from app.domain.entities.purchase import (
    GoodsReceipt,
    PurchaseOrder,
    PurchasePayment,
    Vendor,
    VendorBill,
)
from app.domain.enums import GrnStatus, PurchaseOrderStatus, PurchasePaymentStatus, VendorBillStatus


class PurchaseRepository(ABC):
    # ---- Vendors ----
    @abstractmethod
    async def create_vendor(self, vendor: Vendor) -> Vendor:
        pass

    @abstractmethod
    async def get_vendor(self, vendor_id: str, company_id: str) -> Vendor | None:
        pass

    @abstractmethod
    async def get_vendor_by_code(self, company_id: str, code: str) -> Vendor | None:
        pass

    @abstractmethod
    async def get_next_vendor_code(self, company_id: str) -> str:
        pass

    @abstractmethod
    async def list_vendors(
        self, company_id: str, is_active: bool | None = None, skip: int = 0, limit: int = 100
    ) -> list[Vendor]:
        pass

    @abstractmethod
    async def count_vendors(self, company_id: str, is_active: bool | None = None) -> int:
        pass

    @abstractmethod
    async def update_vendor(self, vendor_id: str, vendor: Vendor) -> Vendor | None:
        pass

    @abstractmethod
    async def delete_vendor(self, vendor_id: str) -> bool:
        pass

    # ---- Purchase orders ----
    @abstractmethod
    async def create_purchase_order(self, order: PurchaseOrder) -> PurchaseOrder:
        pass

    @abstractmethod
    async def get_purchase_order(self, order_id: str, company_id: str) -> PurchaseOrder | None:
        pass

    @abstractmethod
    async def list_purchase_orders(
        self,
        company_id: str,
        status: PurchaseOrderStatus | None = None,
        vendor_id: str | None = None,
        skip: int = 0,
        limit: int = 100,
    ) -> list[PurchaseOrder]:
        pass

    @abstractmethod
    async def count_purchase_orders(
        self,
        company_id: str,
        status: PurchaseOrderStatus | None = None,
        vendor_id: str | None = None,
    ) -> int:
        pass

    @abstractmethod
    async def update_purchase_order(
        self, order_id: str, order: PurchaseOrder, *, replace_lines: bool = False
    ) -> PurchaseOrder | None:
        pass

    @abstractmethod
    async def update_po_line_received_quantity(
        self, line_id: str, received_quantity: Decimal
    ) -> None:
        pass

    @abstractmethod
    async def get_next_po_number(self, company_id: str) -> str:
        pass

    # ---- Goods receipts ----
    @abstractmethod
    async def create_goods_receipt(self, receipt: GoodsReceipt) -> GoodsReceipt:
        pass

    @abstractmethod
    async def get_goods_receipt(self, receipt_id: str, company_id: str) -> GoodsReceipt | None:
        pass

    @abstractmethod
    async def list_goods_receipts(
        self,
        company_id: str,
        status: GrnStatus | None = None,
        purchase_order_id: str | None = None,
        skip: int = 0,
        limit: int = 100,
    ) -> list[GoodsReceipt]:
        pass

    @abstractmethod
    async def count_goods_receipts(
        self,
        company_id: str,
        status: GrnStatus | None = None,
        purchase_order_id: str | None = None,
    ) -> int:
        pass

    @abstractmethod
    async def update_goods_receipt(
        self, receipt_id: str, receipt: GoodsReceipt
    ) -> GoodsReceipt | None:
        pass

    @abstractmethod
    async def get_next_grn_number(self, company_id: str) -> str:
        pass

    # ---- Vendor bills ----
    @abstractmethod
    async def create_vendor_bill(self, bill: VendorBill) -> VendorBill:
        pass

    @abstractmethod
    async def get_vendor_bill(self, bill_id: str, company_id: str) -> VendorBill | None:
        pass

    @abstractmethod
    async def list_vendor_bills(
        self,
        company_id: str,
        status: VendorBillStatus | None = None,
        vendor_id: str | None = None,
        skip: int = 0,
        limit: int = 100,
    ) -> list[VendorBill]:
        pass

    @abstractmethod
    async def count_vendor_bills(
        self,
        company_id: str,
        status: VendorBillStatus | None = None,
        vendor_id: str | None = None,
    ) -> int:
        pass

    @abstractmethod
    async def update_vendor_bill(self, bill_id: str, bill: VendorBill) -> VendorBill | None:
        pass

    @abstractmethod
    async def get_next_bill_number(self, company_id: str) -> str:
        pass

    # ---- Payments ----
    @abstractmethod
    async def create_payment(self, payment: PurchasePayment) -> PurchasePayment:
        pass

    @abstractmethod
    async def get_payment(self, payment_id: str, company_id: str) -> PurchasePayment | None:
        pass

    @abstractmethod
    async def list_payments(
        self,
        company_id: str,
        status: PurchasePaymentStatus | None = None,
        vendor_id: str | None = None,
        skip: int = 0,
        limit: int = 100,
    ) -> list[PurchasePayment]:
        pass

    @abstractmethod
    async def count_payments(
        self,
        company_id: str,
        status: PurchasePaymentStatus | None = None,
        vendor_id: str | None = None,
    ) -> int:
        pass

    @abstractmethod
    async def update_payment(
        self, payment_id: str, payment: PurchasePayment
    ) -> PurchasePayment | None:
        pass

    @abstractmethod
    async def get_next_payment_number(self, company_id: str) -> str:
        pass
