from abc import ABC, abstractmethod

from app.domain.entities.inventory import (
    BaseUnit,
    Brand,
    Department,
    DepartmentIssue,
    DepartmentIssueSummary,
    InventoryBalance,
    InventoryTransaction,
    Item,
    ItemCategory,
    ItemGroup,
    ItemTransaction,
    ItemType,
    Location,
    StockReport,
    StockTransfer,
    UnitType,
    Warehouse,
)


class InventoryRepository(ABC):
    # ---- Categories ----
    @abstractmethod
    async def create_category(self, category: ItemCategory) -> ItemCategory:
        pass

    @abstractmethod
    async def get_category(self, category_id: str, company_id: str) -> ItemCategory | None:
        pass

    @abstractmethod
    async def get_category_by_code(self, company_id: str, code: str) -> ItemCategory | None:
        pass

    @abstractmethod
    async def list_categories(
        self, company_id: str, is_active: bool | None = None, skip: int = 0, limit: int = 100
    ) -> list[ItemCategory]:
        pass

    @abstractmethod
    async def count_categories(self, company_id: str, is_active: bool | None = None) -> int:
        pass

    @abstractmethod
    async def update_category(self, category_id: str, category: ItemCategory) -> ItemCategory | None:
        pass

    @abstractmethod
    async def delete_category(self, category_id: str) -> bool:
        pass

    # ---- Base units ----
    @abstractmethod
    async def create_base_unit(self, base_unit: BaseUnit) -> BaseUnit:
        pass

    @abstractmethod
    async def get_base_unit(self, base_unit_id: str, company_id: str) -> BaseUnit | None:
        pass

    @abstractmethod
    async def get_base_unit_by_code(self, company_id: str, code: str) -> BaseUnit | None:
        pass

    @abstractmethod
    async def list_base_units(
        self, company_id: str, is_active: bool | None = None, skip: int = 0, limit: int = 100
    ) -> list[BaseUnit]:
        pass

    @abstractmethod
    async def count_base_units(self, company_id: str, is_active: bool | None = None) -> int:
        pass

    @abstractmethod
    async def update_base_unit(self, base_unit_id: str, base_unit: BaseUnit) -> BaseUnit | None:
        pass

    @abstractmethod
    async def delete_base_unit(self, base_unit_id: str) -> bool:
        pass

    # ---- Warehouses ----
    @abstractmethod
    async def create_warehouse(self, warehouse: Warehouse) -> Warehouse:
        pass

    @abstractmethod
    async def get_warehouse(self, warehouse_id: str, company_id: str) -> Warehouse | None:
        pass

    @abstractmethod
    async def get_warehouse_by_code(self, company_id: str, code: str) -> Warehouse | None:
        pass

    @abstractmethod
    async def list_warehouses(
        self,
        company_id: str,
        is_active: bool | None = None,
        warehouse_type: str | None = None,
        status: str | None = None,
        skip: int = 0,
        limit: int = 100,
    ) -> list[Warehouse]:
        pass

    @abstractmethod
    async def count_warehouses(
        self,
        company_id: str,
        is_active: bool | None = None,
        warehouse_type: str | None = None,
        status: str | None = None,
    ) -> int:
        pass

    @abstractmethod
    async def update_warehouse(self, warehouse_id: str, warehouse: Warehouse) -> Warehouse | None:
        pass

    @abstractmethod
    async def delete_warehouse(self, warehouse_id: str) -> bool:
        pass

    @abstractmethod
    async def user_exists(self, user_id: str) -> bool:
        pass

    # ---- Brands ----
    @abstractmethod
    async def create_brand(self, brand: Brand) -> Brand:
        pass

    @abstractmethod
    async def get_brand(self, brand_id: str, company_id: str) -> Brand | None:
        pass

    @abstractmethod
    async def get_brand_by_code(self, company_id: str, code: str) -> Brand | None:
        pass

    @abstractmethod
    async def get_next_brand_code(self, company_id: str) -> str:
        pass

    @abstractmethod
    async def list_brands(
        self, company_id: str, is_active: bool | None = None, skip: int = 0, limit: int = 100
    ) -> list[Brand]:
        pass

    @abstractmethod
    async def count_brands(self, company_id: str, is_active: bool | None = None) -> int:
        pass

    @abstractmethod
    async def update_brand(self, brand_id: str, brand: Brand) -> Brand | None:
        pass

    @abstractmethod
    async def delete_brand(self, brand_id: str) -> bool:
        pass

    # ---- Item types ----
    @abstractmethod
    async def create_item_type(self, item_type: ItemType) -> ItemType:
        pass

    @abstractmethod
    async def get_item_type(self, item_type_id: str, company_id: str) -> ItemType | None:
        pass

    @abstractmethod
    async def get_item_type_by_code(self, company_id: str, code: str) -> ItemType | None:
        pass

    @abstractmethod
    async def list_item_types(
        self,
        company_id: str,
        is_active: bool | None = None,
        category_id: str | None = None,
        skip: int = 0,
        limit: int = 100,
    ) -> list[ItemType]:
        pass

    @abstractmethod
    async def count_item_types(
        self,
        company_id: str,
        is_active: bool | None = None,
        category_id: str | None = None,
    ) -> int:
        pass

    @abstractmethod
    async def update_item_type(self, item_type_id: str, item_type: ItemType) -> ItemType | None:
        pass

    @abstractmethod
    async def delete_item_type(self, item_type_id: str) -> bool:
        pass

    # ---- Groups ----
    @abstractmethod
    async def create_group(self, group: ItemGroup) -> ItemGroup:
        pass

    @abstractmethod
    async def get_group(self, group_id: str, company_id: str) -> ItemGroup | None:
        pass

    @abstractmethod
    async def get_group_by_code(self, company_id: str, code: str) -> ItemGroup | None:
        pass

    @abstractmethod
    async def list_groups(
        self,
        company_id: str,
        is_active: bool | None = None,
        category_id: str | None = None,
        item_type_id: str | None = None,
        skip: int = 0,
        limit: int = 100,
    ) -> list[ItemGroup]:
        pass

    @abstractmethod
    async def count_groups(
        self,
        company_id: str,
        is_active: bool | None = None,
        category_id: str | None = None,
        item_type_id: str | None = None,
    ) -> int:
        pass

    @abstractmethod
    async def update_group(self, group_id: str, group: ItemGroup) -> ItemGroup | None:
        pass

    @abstractmethod
    async def delete_group(self, group_id: str) -> bool:
        pass

    # ---- Unit types ----
    @abstractmethod
    async def create_unit_type(self, unit_type: UnitType) -> UnitType:
        pass

    @abstractmethod
    async def get_unit_type(self, unit_type_id: str, company_id: str) -> UnitType | None:
        pass

    @abstractmethod
    async def get_unit_type_by_code(self, company_id: str, code: str) -> UnitType | None:
        pass

    @abstractmethod
    async def list_unit_types(
        self, company_id: str, is_active: bool | None = None, skip: int = 0, limit: int = 100
    ) -> list[UnitType]:
        pass

    @abstractmethod
    async def count_unit_types(self, company_id: str, is_active: bool | None = None) -> int:
        pass

    @abstractmethod
    async def update_unit_type(self, unit_type_id: str, unit_type: UnitType) -> UnitType | None:
        pass

    @abstractmethod
    async def delete_unit_type(self, unit_type_id: str) -> bool:
        pass

    # ---- Items ----
    @abstractmethod
    async def create_item(self, item: Item) -> Item:
        pass

    @abstractmethod
    async def get_item(self, item_id: str, company_id: str) -> Item | None:
        pass

    @abstractmethod
    async def get_item_by_sku(self, company_id: str, sku: str) -> Item | None:
        pass

    @abstractmethod
    async def get_item_by_barcode(self, company_id: str, barcode: str) -> Item | None:
        pass

    @abstractmethod
    async def get_next_item_code(self, company_id: str) -> str:
        pass

    @abstractmethod
    async def list_items(
        self,
        company_id: str,
        category_id: str | None = None,
        item_type_id: str | None = None,
        group_id: str | None = None,
        warehouse_id: str | None = None,
        is_active: bool | None = None,
        skip: int = 0,
        limit: int = 100,
    ) -> list[Item]:
        pass

    @abstractmethod
    async def count_items(
        self,
        company_id: str,
        category_id: str | None = None,
        item_type_id: str | None = None,
        group_id: str | None = None,
        warehouse_id: str | None = None,
        is_active: bool | None = None,
    ) -> int:
        pass

    @abstractmethod
    async def update_item(self, item_id: str, item: Item) -> Item | None:
        pass

    @abstractmethod
    async def delete_item(self, item_id: str) -> bool:
        pass

    # ---- Balances ----
    @abstractmethod
    async def get_balance(
        self, company_id: str, item_id: str, warehouse_id: str | None = None
    ) -> InventoryBalance | None:
        pass

    @abstractmethod
    async def list_balances(
        self,
        company_id: str,
        skip: int = 0,
        limit: int = 100,
        warehouse_id: str | None = None,
        item_id: str | None = None,
    ) -> list[InventoryBalance]:
        pass

    @abstractmethod
    async def count_balances(
        self,
        company_id: str,
        warehouse_id: str | None = None,
        item_id: str | None = None,
    ) -> int:
        pass

    @abstractmethod
    async def upsert_balance(self, balance: InventoryBalance) -> InventoryBalance:
        pass

    # ---- Transactions ----
    @abstractmethod
    async def create_transaction(
        self, transaction: InventoryTransaction
    ) -> tuple[InventoryTransaction, InventoryBalance]:
        """Create txn and update/create balance (average cost on receipt)."""
        pass

    @abstractmethod
    async def list_transactions(
        self,
        company_id: str,
        item_id: str | None = None,
        skip: int = 0,
        limit: int = 100,
    ) -> list[InventoryTransaction]:
        pass

    @abstractmethod
    async def count_transactions(self, company_id: str, item_id: str | None = None) -> int:
        pass

    # ---- Item transactions (multi-line documents) ----
    @abstractmethod
    async def create_item_transaction(self, doc: ItemTransaction) -> ItemTransaction:
        pass

    @abstractmethod
    async def get_item_transaction(
        self, txn_id: str, company_id: str
    ) -> ItemTransaction | None:
        pass

    @abstractmethod
    async def list_item_transactions(
        self,
        company_id: str,
        status: str | None = None,
        direction: str | None = None,
        warehouse_id: str | None = None,
        skip: int = 0,
        limit: int = 100,
    ) -> list[ItemTransaction]:
        pass

    @abstractmethod
    async def count_item_transactions(
        self,
        company_id: str,
        status: str | None = None,
        direction: str | None = None,
        warehouse_id: str | None = None,
    ) -> int:
        pass

    @abstractmethod
    async def update_item_transaction(self, doc: ItemTransaction) -> ItemTransaction | None:
        pass

    @abstractmethod
    async def get_next_item_transaction_number(self, company_id: str) -> str:
        pass

    @abstractmethod
    async def vendor_exists(self, vendor_id: str, company_id: str) -> bool:
        pass

    # ---- Stock transfers ----
    @abstractmethod
    async def create_stock_transfer(self, doc: StockTransfer) -> StockTransfer:
        pass

    @abstractmethod
    async def get_stock_transfer(
        self, transfer_id: str, company_id: str
    ) -> StockTransfer | None:
        pass

    @abstractmethod
    async def list_stock_transfers(
        self,
        company_id: str,
        status: str | None = None,
        from_warehouse_id: str | None = None,
        to_warehouse_id: str | None = None,
        skip: int = 0,
        limit: int = 100,
    ) -> list[StockTransfer]:
        pass

    @abstractmethod
    async def count_stock_transfers(
        self,
        company_id: str,
        status: str | None = None,
        from_warehouse_id: str | None = None,
        to_warehouse_id: str | None = None,
    ) -> int:
        pass

    @abstractmethod
    async def update_stock_transfer(
        self, doc: StockTransfer, *, replace_lines: bool = False
    ) -> StockTransfer | None:
        pass

    @abstractmethod
    async def delete_stock_transfer(self, transfer_id: str) -> bool:
        pass

    @abstractmethod
    async def get_next_stock_transfer_number(self, company_id: str) -> str:
        pass

    # ---- Locations ----
    @abstractmethod
    async def create_location(self, location: Location) -> Location:
        pass

    @abstractmethod
    async def get_location(self, location_id: str, company_id: str) -> Location | None:
        pass

    @abstractmethod
    async def get_location_by_code(self, company_id: str, code: str) -> Location | None:
        pass

    @abstractmethod
    async def list_locations(
        self,
        company_id: str,
        is_active: bool | None = None,
        skip: int = 0,
        limit: int = 100,
    ) -> list[Location]:
        pass

    @abstractmethod
    async def count_locations(
        self, company_id: str, is_active: bool | None = None
    ) -> int:
        pass

    @abstractmethod
    async def update_location(self, location_id: str, location: Location) -> Location | None:
        pass

    @abstractmethod
    async def delete_location(self, location_id: str) -> bool:
        pass

    # ---- Departments ----
    @abstractmethod
    async def create_department(self, department: Department) -> Department:
        pass

    @abstractmethod
    async def get_department(
        self, department_id: str, company_id: str
    ) -> Department | None:
        pass

    @abstractmethod
    async def get_department_by_code(
        self, company_id: str, code: str
    ) -> Department | None:
        pass

    @abstractmethod
    async def list_departments(
        self,
        company_id: str,
        is_active: bool | None = None,
        status: str | None = None,
        location_id: str | None = None,
        skip: int = 0,
        limit: int = 100,
    ) -> list[Department]:
        pass

    @abstractmethod
    async def count_departments(
        self,
        company_id: str,
        is_active: bool | None = None,
        status: str | None = None,
        location_id: str | None = None,
    ) -> int:
        pass

    @abstractmethod
    async def update_department(
        self, department_id: str, department: Department
    ) -> Department | None:
        pass

    @abstractmethod
    async def delete_department(self, department_id: str) -> bool:
        pass

    # ---- Department issues ----
    @abstractmethod
    async def create_department_issue(self, doc: DepartmentIssue) -> DepartmentIssue:
        pass

    @abstractmethod
    async def get_department_issue(
        self, issue_id: str, company_id: str
    ) -> DepartmentIssue | None:
        pass

    @abstractmethod
    async def list_department_issues(
        self,
        company_id: str,
        status: str | None = None,
        department_id: str | None = None,
        from_warehouse_id: str | None = None,
        skip: int = 0,
        limit: int = 100,
    ) -> list[DepartmentIssue]:
        pass

    @abstractmethod
    async def count_department_issues(
        self,
        company_id: str,
        status: str | None = None,
        department_id: str | None = None,
        from_warehouse_id: str | None = None,
    ) -> int:
        pass

    @abstractmethod
    async def update_department_issue(
        self, doc: DepartmentIssue, *, replace_lines: bool = False
    ) -> DepartmentIssue | None:
        pass

    @abstractmethod
    async def delete_department_issue(self, issue_id: str) -> bool:
        pass

    @abstractmethod
    async def get_next_department_issue_number(self, company_id: str) -> str:
        pass

    # ---- Department issue reports dashboard ----
    @abstractmethod
    async def get_department_issue_period_stats(
        self,
        company_id: str,
        from_date,
        to_date,
        *,
        department_id: str | None = None,
        from_warehouse_id: str | None = None,
        exclude_cancelled: bool = True,
    ) -> dict:
        pass

    @abstractmethod
    async def get_department_issue_daily_trend(
        self,
        company_id: str,
        from_date,
        to_date,
        *,
        department_id: str | None = None,
        from_warehouse_id: str | None = None,
        exclude_cancelled: bool = True,
    ) -> list[dict]:
        pass

    @abstractmethod
    async def get_department_issue_breakdown_by_department(
        self,
        company_id: str,
        from_date,
        to_date,
        *,
        department_id: str | None = None,
        from_warehouse_id: str | None = None,
        exclude_cancelled: bool = True,
    ) -> list[dict]:
        pass

    @abstractmethod
    async def get_department_issue_breakdown_by_warehouse(
        self,
        company_id: str,
        from_date,
        to_date,
        *,
        department_id: str | None = None,
        from_warehouse_id: str | None = None,
        exclude_cancelled: bool = True,
    ) -> list[dict]:
        pass

    @abstractmethod
    async def list_department_issue_summaries(
        self,
        company_id: str,
        *,
        from_date=None,
        to_date=None,
        status: str | None = None,
        department_id: str | None = None,
        from_warehouse_id: str | None = None,
        exclude_cancelled: bool = True,
        order_by_submitted: bool = False,
        skip: int = 0,
        limit: int = 10,
    ) -> list[DepartmentIssueSummary]:
        pass

    @abstractmethod
    async def count_department_issues_pending(
        self,
        company_id: str,
        *,
        from_date=None,
        to_date=None,
        department_id: str | None = None,
        from_warehouse_id: str | None = None,
    ) -> int:
        pass

    @abstractmethod
    async def get_stock_report(
        self,
        company_id: str,
        *,
        from_date=None,
        to_date=None,
        warehouse_id: str | None = None,
        category_id: str | None = None,
        item_type_id: str | None = None,
        search: str | None = None,
        stock_status: str | None = None,
        min_stock_value=None,
        page: int = 1,
        page_size: int = 20,
    ) -> StockReport:
        pass
