import math
import uuid
from pathlib import Path
from typing import Annotated

from fastapi import APIRouter, Depends, File, Form, Query, UploadFile

from app.application.exceptions import ValidationError
from app.application.services.inventory_service import InventoryService
from app.domain.entities.user_registration import UserRegistration
from app.domain.enums import (
    DepartmentIssueStatus,
    DepartmentStatus,
    InventoryTxnDirection,
    ItemTransactionStatus,
    StockTransferStatus,
    WarehousePriority,
    WarehouseStatus,
    WarehouseType,
)
from app.presentation.auth_dependencies import get_current_user
from app.presentation.company_dependencies import get_validated_company_id_query
from app.presentation.dependencies import get_inventory_service
from app.presentation.schemas.common import DataTableResponse, MessageResponse
from app.presentation.schemas.inventory import (
    BaseUnitCreate,
    BaseUnitResponse,
    BaseUnitUpdate,
    BrandResponse,
    InventoryBalanceResponse,
    InventoryTransactionCreate,
    InventoryTransactionResponse,
    ItemCategoryCreate,
    ItemCategoryResponse,
    ItemCategoryUpdate,
    ItemCreate,
    ItemGroupResponse,
    ItemResponse,
    ItemTransactionCreate,
    ItemTransactionResponse,
    ItemTransactionUpdate,
    ItemTypeCreate,
    ItemTypeResponse,
    ItemTypeUpdate,
    ItemUpdate,
    UnitTypeCreate,
    UnitTypeResponse,
    UnitTypeUpdate,
    WarehouseResponse,
    StockTransferCreate,
    StockTransferResponse,
    StockTransferUpdate,
    DepartmentCreate,
    DepartmentResponse,
    DepartmentUpdate,
    DepartmentIssueCreate,
    DepartmentIssueResponse,
    DepartmentIssueUpdate,
    LocationCreate,
    LocationResponse,
    LocationUpdate,
)

router = APIRouter(prefix="/inventory", tags=["Inventory"])

CompanyId = Annotated[str, Depends(get_validated_company_id_query)]

_STATIC_ROOT = Path(__file__).resolve().parents[4] / "static"
_ICON_ALLOWED_EXT = {".png", ".jpg", ".jpeg", ".svg"}
_LOGO_ALLOWED_EXT = {".png", ".jpg", ".jpeg"}
_ITEM_IMAGE_ALLOWED_EXT = {".png", ".jpg", ".jpeg", ".webp"}
_ITEM_TXN_ATTACHMENT_EXT = {".png", ".jpg", ".jpeg", ".pdf"}
_WAREHOUSE_ATTACHMENT_EXT = {".png", ".jpg", ".jpeg", ".pdf"}
_DEPARTMENT_ISSUE_ATTACHMENT_EXT = {
    ".png",
    ".jpg",
    ".jpeg",
    ".pdf",
    ".doc",
    ".docx",
    ".xls",
    ".xlsx",
}
_ICON_MAX_BYTES = 2 * 1024 * 1024
_ITEM_TXN_ATTACHMENT_MAX_BYTES = 5 * 1024 * 1024
_WAREHOUSE_ATTACHMENT_MAX_BYTES = 5 * 1024 * 1024
_DEPARTMENT_ISSUE_ATTACHMENT_MAX_BYTES = 5 * 1024 * 1024


def _build_datatable(data: list, total: int, page: int, page_size: int) -> DataTableResponse:
    return DataTableResponse(
        data=data,
        total=total,
        page=page,
        page_size=page_size,
        total_pages=max(1, math.ceil(total / page_size)) if page_size else 1,
    )


async def _save_group_icon(company_id: str, icon: UploadFile) -> str:
    filename = icon.filename or ""
    ext = Path(filename).suffix.lower()
    if ext not in _ICON_ALLOWED_EXT:
        raise ValidationError("Icon must be PNG, JPG, or SVG")
    content = await icon.read()
    if len(content) > _ICON_MAX_BYTES:
        raise ValidationError("Icon must be 2MB or smaller")
    dest_dir = _STATIC_ROOT / "item-groups" / company_id
    dest_dir.mkdir(parents=True, exist_ok=True)
    stored_name = f"{uuid.uuid4().hex}{ext}"
    (dest_dir / stored_name).write_bytes(content)
    return f"/static/item-groups/{company_id}/{stored_name}"


async def _save_brand_logo(company_id: str, logo: UploadFile) -> str:
    filename = logo.filename or ""
    ext = Path(filename).suffix.lower()
    if ext not in _LOGO_ALLOWED_EXT:
        raise ValidationError("Logo must be PNG, JPG, or JPEG")
    content = await logo.read()
    if len(content) > _ICON_MAX_BYTES:
        raise ValidationError("Logo must be 2MB or smaller")
    dest_dir = _STATIC_ROOT / "brands" / company_id
    dest_dir.mkdir(parents=True, exist_ok=True)
    stored_name = f"{uuid.uuid4().hex}{ext}"
    (dest_dir / stored_name).write_bytes(content)
    return f"/static/brands/{company_id}/{stored_name}"


async def _save_item_image(company_id: str, image: UploadFile) -> str:
    filename = image.filename or ""
    ext = Path(filename).suffix.lower()
    if ext not in _ITEM_IMAGE_ALLOWED_EXT:
        raise ValidationError("Item image must be PNG, JPG, JPEG, or WEBP")
    content = await image.read()
    if len(content) > _ICON_MAX_BYTES:
        raise ValidationError("Item image must be 2MB or smaller")
    dest_dir = _STATIC_ROOT / "items" / company_id
    dest_dir.mkdir(parents=True, exist_ok=True)
    stored_name = f"{uuid.uuid4().hex}{ext}"
    (dest_dir / stored_name).write_bytes(content)
    return f"/static/items/{company_id}/{stored_name}"


async def _save_warehouse_attachment(company_id: str, file: UploadFile) -> str:
    filename = file.filename or ""
    ext = Path(filename).suffix.lower()
    if ext not in _WAREHOUSE_ATTACHMENT_EXT:
        raise ValidationError("Attachment must be JPG, PNG, or PDF")
    content = await file.read()
    if len(content) > _WAREHOUSE_ATTACHMENT_MAX_BYTES:
        raise ValidationError("Attachment must be 5MB or smaller")
    dest_dir = _STATIC_ROOT / "warehouses" / company_id
    dest_dir.mkdir(parents=True, exist_ok=True)
    stored_name = f"{uuid.uuid4().hex}{ext}"
    (dest_dir / stored_name).write_bytes(content)
    return f"/static/warehouses/{company_id}/{stored_name}"


async def _save_department_issue_attachment(company_id: str, file: UploadFile) -> str:
    filename = file.filename or ""
    ext = Path(filename).suffix.lower()
    if ext not in _DEPARTMENT_ISSUE_ATTACHMENT_EXT:
        raise ValidationError("Attachment must be JPG, PNG, PDF, DOC, or XLS")
    content = await file.read()
    if len(content) > _DEPARTMENT_ISSUE_ATTACHMENT_MAX_BYTES:
        raise ValidationError("Attachment must be 5MB or smaller")
    dest_dir = _STATIC_ROOT / "department-issues" / company_id
    dest_dir.mkdir(parents=True, exist_ok=True)
    stored_name = f"{uuid.uuid4().hex}{ext}"
    (dest_dir / stored_name).write_bytes(content)
    return f"/static/department-issues/{company_id}/{stored_name}"


def _to_brand_response(brand) -> BrandResponse:
    return BrandResponse(
        id=brand.id or "",
        company_id=brand.company_id,
        code=brand.code,
        name=brand.name,
        description=brand.description,
        logo=brand.logo,
        website=brand.website,
        is_active=brand.is_active,
        contact_person=brand.contact_person,
        email=brand.email,
        phone=brand.phone,
        address=brand.address,
        created_at=brand.created_at,
        updated_at=brand.updated_at,
    )


def _to_item_type_response(item_type) -> ItemTypeResponse:
    return ItemTypeResponse(
        id=item_type.id or "",
        company_id=item_type.company_id,
        category_id=item_type.category_id,
        category_code=getattr(item_type, "category_code", None),
        category_name=getattr(item_type, "category_name", None),
        code=item_type.code,
        name=item_type.name,
        description=item_type.description,
        is_active=item_type.is_active,
        created_at=item_type.created_at,
        updated_at=item_type.updated_at,
    )


def _to_group_response(group) -> ItemGroupResponse:
    return ItemGroupResponse(
        id=group.id or "",
        company_id=group.company_id,
        category_id=group.category_id,
        category_code=getattr(group, "category_code", None),
        category_name=getattr(group, "category_name", None),
        item_type_id=group.item_type_id,
        item_type_code=getattr(group, "item_type_code", None),
        item_type_name=getattr(group, "item_type_name", None),
        code=group.code,
        name=group.name,
        description=group.description,
        is_active=group.is_active,
        sort_order=group.sort_order,
        icon=group.icon,
        remarks=group.remarks,
        created_at=group.created_at,
        updated_at=group.updated_at,
    )


# ---- Categories ----
@router.post("/categories", response_model=ItemCategoryResponse, status_code=201)
async def create_category(
    company_id: CompanyId,
    payload: ItemCategoryCreate,
    current_user: UserRegistration = Depends(get_current_user),
    service: InventoryService = Depends(get_inventory_service),
):
    category = await service.create_category(current_user, company_id, payload.model_dump())
    return ItemCategoryResponse.model_validate(category)


@router.get("/categories", response_model=DataTableResponse[ItemCategoryResponse])
async def list_categories(
    company_id: CompanyId,
    is_active: bool | None = None,
    page: int = Query(1, ge=1),
    page_size: int = Query(20, ge=1, le=500),
    current_user: UserRegistration = Depends(get_current_user),
    service: InventoryService = Depends(get_inventory_service),
):
    skip = (page - 1) * page_size
    items, total = await service.list_categories(
        current_user, company_id, is_active, skip, page_size
    )
    return _build_datatable(
        [ItemCategoryResponse.model_validate(i) for i in items], total, page, page_size
    )


@router.get("/categories/{category_id}", response_model=ItemCategoryResponse)
async def get_category(
    company_id: CompanyId,
    category_id: str,
    current_user: UserRegistration = Depends(get_current_user),
    service: InventoryService = Depends(get_inventory_service),
):
    category = await service.get_category(current_user, company_id, category_id)
    return ItemCategoryResponse.model_validate(category)


@router.put("/categories/{category_id}", response_model=ItemCategoryResponse)
async def update_category(
    company_id: CompanyId,
    category_id: str,
    payload: ItemCategoryUpdate,
    current_user: UserRegistration = Depends(get_current_user),
    service: InventoryService = Depends(get_inventory_service),
):
    category = await service.update_category(
        current_user, company_id, category_id, payload.model_dump(exclude_unset=True)
    )
    return ItemCategoryResponse.model_validate(category)


@router.delete("/categories/{category_id}", response_model=MessageResponse)
async def delete_category(
    company_id: CompanyId,
    category_id: str,
    current_user: UserRegistration = Depends(get_current_user),
    service: InventoryService = Depends(get_inventory_service),
):
    await service.delete_category(current_user, company_id, category_id)
    return MessageResponse(message="Category deleted successfully")


# ---- Item types ----
@router.post("/item-types", response_model=ItemTypeResponse, status_code=201)
async def create_item_type(
    company_id: CompanyId,
    payload: ItemTypeCreate,
    current_user: UserRegistration = Depends(get_current_user),
    service: InventoryService = Depends(get_inventory_service),
):
    item_type = await service.create_item_type(current_user, company_id, payload.model_dump())
    return _to_item_type_response(item_type)


@router.get("/item-types", response_model=DataTableResponse[ItemTypeResponse])
async def list_item_types(
    company_id: CompanyId,
    is_active: bool | None = None,
    category_id: str | None = None,
    page: int = Query(1, ge=1),
    page_size: int = Query(20, ge=1, le=500),
    current_user: UserRegistration = Depends(get_current_user),
    service: InventoryService = Depends(get_inventory_service),
):
    skip = (page - 1) * page_size
    items, total = await service.list_item_types(
        current_user, company_id, is_active, category_id, skip, page_size
    )
    return _build_datatable(
        [_to_item_type_response(i) for i in items], total, page, page_size
    )


@router.get("/item-types/{item_type_id}", response_model=ItemTypeResponse)
async def get_item_type(
    company_id: CompanyId,
    item_type_id: str,
    current_user: UserRegistration = Depends(get_current_user),
    service: InventoryService = Depends(get_inventory_service),
):
    item_type = await service.get_item_type(current_user, company_id, item_type_id)
    return _to_item_type_response(item_type)


@router.put("/item-types/{item_type_id}", response_model=ItemTypeResponse)
async def update_item_type(
    company_id: CompanyId,
    item_type_id: str,
    payload: ItemTypeUpdate,
    current_user: UserRegistration = Depends(get_current_user),
    service: InventoryService = Depends(get_inventory_service),
):
    item_type = await service.update_item_type(
        current_user, company_id, item_type_id, payload.model_dump(exclude_unset=True)
    )
    return _to_item_type_response(item_type)


@router.delete("/item-types/{item_type_id}", response_model=MessageResponse)
async def delete_item_type(
    company_id: CompanyId,
    item_type_id: str,
    current_user: UserRegistration = Depends(get_current_user),
    service: InventoryService = Depends(get_inventory_service),
):
    await service.delete_item_type(current_user, company_id, item_type_id)
    return MessageResponse(message="Item type deleted successfully")


# ---- Groups ----
@router.post("/groups", response_model=ItemGroupResponse, status_code=201)
async def create_group(
    company_id: CompanyId,
    code: Annotated[str, Form(min_length=1, max_length=30, description="Unique group code")],
    name: Annotated[str, Form(min_length=1, max_length=200, description="Group name")],
    category_id: Annotated[str, Form(min_length=1, description="Category ID")],
    item_type_id: Annotated[str, Form(min_length=1, description="Item type ID")],
    description: Annotated[str, Form(min_length=1, max_length=255)],
    is_active: Annotated[bool, Form(description="Status Active/Inactive")] = True,
    sort_order: Annotated[int | None, Form(description="Lower numbers appear first")] = None,
    remarks: Annotated[str | None, Form(max_length=255)] = None,
    icon: Annotated[
        UploadFile | None,
        File(description="PNG, JPG, or SVG — max 2MB"),
    ] = None,
    current_user: UserRegistration = Depends(get_current_user),
    service: InventoryService = Depends(get_inventory_service),
):
    payload = {
        "code": code,
        "name": name,
        "category_id": category_id,
        "item_type_id": item_type_id,
        "description": description,
        "is_active": is_active,
        "sort_order": sort_order,
        "remarks": remarks,
    }
    if icon is not None and icon.filename:
        payload["icon"] = await _save_group_icon(company_id, icon)
    group = await service.create_group(current_user, company_id, payload)
    return _to_group_response(group)


@router.get("/groups", response_model=DataTableResponse[ItemGroupResponse])
async def list_groups(
    company_id: CompanyId,
    is_active: bool | None = None,
    category_id: str | None = None,
    item_type_id: str | None = None,
    page: int = Query(1, ge=1),
    page_size: int = Query(20, ge=1, le=500),
    current_user: UserRegistration = Depends(get_current_user),
    service: InventoryService = Depends(get_inventory_service),
):
    skip = (page - 1) * page_size
    items, total = await service.list_groups(
        current_user, company_id, is_active, category_id, item_type_id, skip, page_size
    )
    return _build_datatable(
        [_to_group_response(i) for i in items], total, page, page_size
    )


@router.get("/groups/{group_id}", response_model=ItemGroupResponse)
async def get_group(
    company_id: CompanyId,
    group_id: str,
    current_user: UserRegistration = Depends(get_current_user),
    service: InventoryService = Depends(get_inventory_service),
):
    group = await service.get_group(current_user, company_id, group_id)
    return _to_group_response(group)


@router.put("/groups/{group_id}", response_model=ItemGroupResponse)
async def update_group(
    company_id: CompanyId,
    group_id: str,
    code: Annotated[str | None, Form(min_length=1, max_length=30)] = None,
    name: Annotated[str | None, Form(min_length=1, max_length=200)] = None,
    category_id: Annotated[str | None, Form(min_length=1)] = None,
    item_type_id: Annotated[str | None, Form(min_length=1)] = None,
    description: Annotated[str | None, Form(min_length=1, max_length=255)] = None,
    is_active: Annotated[bool | None, Form()] = None,
    sort_order: Annotated[int | None, Form()] = None,
    remarks: Annotated[str | None, Form(max_length=255)] = None,
    icon: Annotated[
        UploadFile | None,
        File(description="PNG, JPG, or SVG — max 2MB"),
    ] = None,
    current_user: UserRegistration = Depends(get_current_user),
    service: InventoryService = Depends(get_inventory_service),
):
    payload: dict = {}
    if code is not None:
        payload["code"] = code
    if name is not None:
        payload["name"] = name
    if category_id is not None:
        payload["category_id"] = category_id
    if item_type_id is not None:
        payload["item_type_id"] = item_type_id
    if description is not None:
        payload["description"] = description
    if is_active is not None:
        payload["is_active"] = is_active
    if sort_order is not None:
        payload["sort_order"] = sort_order
    if remarks is not None:
        payload["remarks"] = remarks
    if icon is not None and icon.filename:
        payload["icon"] = await _save_group_icon(company_id, icon)
    group = await service.update_group(current_user, company_id, group_id, payload)
    return _to_group_response(group)


@router.delete("/groups/{group_id}", response_model=MessageResponse)
async def delete_group(
    company_id: CompanyId,
    group_id: str,
    current_user: UserRegistration = Depends(get_current_user),
    service: InventoryService = Depends(get_inventory_service),
):
    await service.delete_group(current_user, company_id, group_id)
    return MessageResponse(message="Group deleted successfully")


# ---- Base units ----
@router.post("/base-units", response_model=BaseUnitResponse, status_code=201)
async def create_base_unit(
    company_id: CompanyId,
    payload: BaseUnitCreate,
    current_user: UserRegistration = Depends(get_current_user),
    service: InventoryService = Depends(get_inventory_service),
):
    base_unit = await service.create_base_unit(current_user, company_id, payload.model_dump())
    return BaseUnitResponse.model_validate(base_unit)


@router.get("/base-units", response_model=DataTableResponse[BaseUnitResponse])
async def list_base_units(
    company_id: CompanyId,
    is_active: bool | None = None,
    page: int = Query(1, ge=1),
    page_size: int = Query(20, ge=1, le=500),
    current_user: UserRegistration = Depends(get_current_user),
    service: InventoryService = Depends(get_inventory_service),
):
    skip = (page - 1) * page_size
    items, total = await service.list_base_units(
        current_user, company_id, is_active, skip, page_size
    )
    return _build_datatable(
        [BaseUnitResponse.model_validate(i) for i in items], total, page, page_size
    )


@router.get("/base-units/{base_unit_id}", response_model=BaseUnitResponse)
async def get_base_unit(
    company_id: CompanyId,
    base_unit_id: str,
    current_user: UserRegistration = Depends(get_current_user),
    service: InventoryService = Depends(get_inventory_service),
):
    base_unit = await service.get_base_unit(current_user, company_id, base_unit_id)
    return BaseUnitResponse.model_validate(base_unit)


@router.put("/base-units/{base_unit_id}", response_model=BaseUnitResponse)
async def update_base_unit(
    company_id: CompanyId,
    base_unit_id: str,
    payload: BaseUnitUpdate,
    current_user: UserRegistration = Depends(get_current_user),
    service: InventoryService = Depends(get_inventory_service),
):
    base_unit = await service.update_base_unit(
        current_user, company_id, base_unit_id, payload.model_dump(exclude_unset=True)
    )
    return BaseUnitResponse.model_validate(base_unit)


@router.delete("/base-units/{base_unit_id}", response_model=MessageResponse)
async def delete_base_unit(
    company_id: CompanyId,
    base_unit_id: str,
    current_user: UserRegistration = Depends(get_current_user),
    service: InventoryService = Depends(get_inventory_service),
):
    await service.delete_base_unit(current_user, company_id, base_unit_id)
    return MessageResponse(message="Base unit deleted successfully")


# ---- Warehouses ----
def _to_warehouse_response(warehouse) -> WarehouseResponse:
    return WarehouseResponse(
        id=warehouse.id or "",
        company_id=warehouse.company_id,
        code=warehouse.code,
        name=warehouse.name,
        warehouse_type=warehouse.warehouse_type,
        status=warehouse.status,
        priority=warehouse.priority,
        parent_warehouse_id=warehouse.parent_warehouse_id,
        parent_warehouse_code=getattr(warehouse, "parent_warehouse_code", None),
        parent_warehouse_name=getattr(warehouse, "parent_warehouse_name", None),
        manager_id=warehouse.manager_id,
        manager_name=getattr(warehouse, "manager_name", None),
        contact_person=warehouse.contact_person,
        phone=warehouse.phone,
        email=warehouse.email,
        address_line1=warehouse.address_line1,
        address_line2=warehouse.address_line2,
        country=warehouse.country,
        state_province=warehouse.state_province,
        city=warehouse.city,
        postal_code=warehouse.postal_code,
        latitude=warehouse.latitude,
        longitude=warehouse.longitude,
        address=warehouse.address,
        location=warehouse.location,
        is_active=warehouse.is_active,
        description=warehouse.description,
        capacity=warehouse.capacity,
        capacity_uom=warehouse.capacity_uom,
        operating_hours=warehouse.operating_hours,
        notes=warehouse.notes,
        remarks=warehouse.remarks,
        allow_stock_in=warehouse.allow_stock_in,
        allow_stock_out=warehouse.allow_stock_out,
        allow_stock_transfer=warehouse.allow_stock_transfer,
        allow_returns=warehouse.allow_returns,
        attachments=list(warehouse.attachments or []),
        created_at=warehouse.created_at,
        updated_at=warehouse.updated_at,
    )


@router.post(
    "/warehouses",
    response_model=WarehouseResponse,
    status_code=201,
    summary="Create warehouse (Save Warehouse / Save as Draft)",
)
async def create_warehouse(
    company_id: CompanyId,
    name: Annotated[str, Form(min_length=1, max_length=200, description="Warehouse name")],
    code: Annotated[str, Form(min_length=1, max_length=30, description="Unique warehouse code")],
    warehouse_type: Annotated[WarehouseType, Form(description="Main / Regional / Branch")],
    status: Annotated[
        WarehouseStatus,
        Form(description="draft = Save as Draft; active/inactive = Save Warehouse"),
    ] = WarehouseStatus.ACTIVE,
    priority: Annotated[WarehousePriority, Form()] = WarehousePriority.NORMAL,
    parent_warehouse_id: Annotated[str | None, Form()] = None,
    manager_id: Annotated[str | None, Form()] = None,
    contact_person: Annotated[str | None, Form(max_length=200)] = None,
    phone: Annotated[str | None, Form(max_length=30)] = None,
    email: Annotated[str | None, Form(max_length=255)] = None,
    address_line1: Annotated[
        str | None, Form(max_length=255, description="Required when status=active")
    ] = None,
    address_line2: Annotated[str | None, Form(max_length=255)] = None,
    country: Annotated[
        str | None, Form(max_length=100, description="Required when status=active")
    ] = None,
    state_province: Annotated[
        str | None, Form(max_length=100, description="Required when status=active")
    ] = None,
    city: Annotated[
        str | None, Form(max_length=100, description="Required when status=active")
    ] = None,
    postal_code: Annotated[str | None, Form(max_length=30)] = None,
    latitude: Annotated[str | None, Form(max_length=30)] = None,
    longitude: Annotated[str | None, Form(max_length=30)] = None,
    capacity: Annotated[str | None, Form(max_length=100)] = None,
    capacity_uom: Annotated[str | None, Form(max_length=50)] = None,
    operating_hours: Annotated[str | None, Form(max_length=100)] = None,
    notes: Annotated[str | None, Form(max_length=500)] = None,
    description: Annotated[str | None, Form(max_length=255)] = None,
    remarks: Annotated[str | None, Form(max_length=255)] = None,
    allow_stock_in: Annotated[bool, Form()] = True,
    allow_stock_out: Annotated[bool, Form()] = True,
    allow_stock_transfer: Annotated[bool, Form()] = True,
    allow_returns: Annotated[bool, Form()] = True,
    attachments: Annotated[
        list[UploadFile] | None,
        File(description="JPG/PNG/PDF — max 5MB each"),
    ] = None,
    current_user: UserRegistration = Depends(get_current_user),
    service: InventoryService = Depends(get_inventory_service),
):
    payload = {
        "name": name,
        "code": code,
        "warehouse_type": warehouse_type.value,
        "status": status.value,
        "priority": priority.value,
        "parent_warehouse_id": parent_warehouse_id or None,
        "manager_id": manager_id or None,
        "contact_person": contact_person,
        "phone": phone,
        "email": email,
        "address_line1": address_line1,
        "address_line2": address_line2,
        "country": country,
        "state_province": state_province,
        "city": city,
        "postal_code": postal_code,
        "latitude": latitude,
        "longitude": longitude,
        "capacity": capacity,
        "capacity_uom": capacity_uom,
        "operating_hours": operating_hours,
        "notes": notes,
        "description": description,
        "remarks": remarks,
        "allow_stock_in": allow_stock_in,
        "allow_stock_out": allow_stock_out,
        "allow_stock_transfer": allow_stock_transfer,
        "allow_returns": allow_returns,
        "attachments": [],
    }
    if attachments:
        paths: list[str] = []
        for file in attachments:
            if file.filename:
                paths.append(await _save_warehouse_attachment(company_id, file))
        payload["attachments"] = paths
    warehouse = await service.create_warehouse(current_user, company_id, payload)
    return _to_warehouse_response(warehouse)


@router.get("/warehouses", response_model=DataTableResponse[WarehouseResponse])
async def list_warehouses(
    company_id: CompanyId,
    is_active: bool | None = None,
    warehouse_type: WarehouseType | None = None,
    status: WarehouseStatus | None = None,
    page: int = Query(1, ge=1),
    page_size: int = Query(20, ge=1, le=500),
    current_user: UserRegistration = Depends(get_current_user),
    service: InventoryService = Depends(get_inventory_service),
):
    skip = (page - 1) * page_size
    items, total = await service.list_warehouses(
        current_user,
        company_id,
        is_active,
        warehouse_type.value if warehouse_type else None,
        status.value if status else None,
        skip,
        page_size,
    )
    return _build_datatable(
        [_to_warehouse_response(i) for i in items], total, page, page_size
    )


@router.get("/warehouses/{warehouse_id}", response_model=WarehouseResponse)
async def get_warehouse(
    company_id: CompanyId,
    warehouse_id: str,
    current_user: UserRegistration = Depends(get_current_user),
    service: InventoryService = Depends(get_inventory_service),
):
    warehouse = await service.get_warehouse(current_user, company_id, warehouse_id)
    return _to_warehouse_response(warehouse)


@router.put(
    "/warehouses/{warehouse_id}",
    response_model=WarehouseResponse,
    summary="Update warehouse",
)
async def update_warehouse(
    company_id: CompanyId,
    warehouse_id: str,
    name: Annotated[str | None, Form(min_length=1, max_length=200)] = None,
    code: Annotated[str | None, Form(min_length=1, max_length=30)] = None,
    warehouse_type: Annotated[WarehouseType | None, Form()] = None,
    status: Annotated[WarehouseStatus | None, Form()] = None,
    priority: Annotated[WarehousePriority | None, Form()] = None,
    parent_warehouse_id: Annotated[str | None, Form()] = None,
    manager_id: Annotated[str | None, Form()] = None,
    contact_person: Annotated[str | None, Form(max_length=200)] = None,
    phone: Annotated[str | None, Form(max_length=30)] = None,
    email: Annotated[str | None, Form(max_length=255)] = None,
    address_line1: Annotated[str | None, Form(max_length=255)] = None,
    address_line2: Annotated[str | None, Form(max_length=255)] = None,
    country: Annotated[str | None, Form(max_length=100)] = None,
    state_province: Annotated[str | None, Form(max_length=100)] = None,
    city: Annotated[str | None, Form(max_length=100)] = None,
    postal_code: Annotated[str | None, Form(max_length=30)] = None,
    latitude: Annotated[str | None, Form(max_length=30)] = None,
    longitude: Annotated[str | None, Form(max_length=30)] = None,
    capacity: Annotated[str | None, Form(max_length=100)] = None,
    capacity_uom: Annotated[str | None, Form(max_length=50)] = None,
    operating_hours: Annotated[str | None, Form(max_length=100)] = None,
    notes: Annotated[str | None, Form(max_length=500)] = None,
    description: Annotated[str | None, Form(max_length=255)] = None,
    remarks: Annotated[str | None, Form(max_length=255)] = None,
    allow_stock_in: Annotated[bool | None, Form()] = None,
    allow_stock_out: Annotated[bool | None, Form()] = None,
    allow_stock_transfer: Annotated[bool | None, Form()] = None,
    allow_returns: Annotated[bool | None, Form()] = None,
    current_user: UserRegistration = Depends(get_current_user),
    service: InventoryService = Depends(get_inventory_service),
):
    payload: dict = {}
    if name is not None:
        payload["name"] = name
    if code is not None:
        payload["code"] = code
    if warehouse_type is not None:
        payload["warehouse_type"] = warehouse_type.value
    if status is not None:
        payload["status"] = status.value
    if priority is not None:
        payload["priority"] = priority.value
    if parent_warehouse_id is not None:
        payload["parent_warehouse_id"] = parent_warehouse_id or None
    if manager_id is not None:
        payload["manager_id"] = manager_id or None
    if contact_person is not None:
        payload["contact_person"] = contact_person
    if phone is not None:
        payload["phone"] = phone
    if email is not None:
        payload["email"] = email
    if address_line1 is not None:
        payload["address_line1"] = address_line1
    if address_line2 is not None:
        payload["address_line2"] = address_line2
    if country is not None:
        payload["country"] = country
    if state_province is not None:
        payload["state_province"] = state_province
    if city is not None:
        payload["city"] = city
    if postal_code is not None:
        payload["postal_code"] = postal_code
    if latitude is not None:
        payload["latitude"] = latitude
    if longitude is not None:
        payload["longitude"] = longitude
    if capacity is not None:
        payload["capacity"] = capacity
    if capacity_uom is not None:
        payload["capacity_uom"] = capacity_uom
    if operating_hours is not None:
        payload["operating_hours"] = operating_hours
    if notes is not None:
        payload["notes"] = notes
    if description is not None:
        payload["description"] = description
    if remarks is not None:
        payload["remarks"] = remarks
    if allow_stock_in is not None:
        payload["allow_stock_in"] = allow_stock_in
    if allow_stock_out is not None:
        payload["allow_stock_out"] = allow_stock_out
    if allow_stock_transfer is not None:
        payload["allow_stock_transfer"] = allow_stock_transfer
    if allow_returns is not None:
        payload["allow_returns"] = allow_returns
    warehouse = await service.update_warehouse(
        current_user, company_id, warehouse_id, payload
    )
    return _to_warehouse_response(warehouse)


@router.post(
    "/warehouses/{warehouse_id}/attachments",
    response_model=WarehouseResponse,
    summary="Upload warehouse attachments",
)
async def upload_warehouse_attachments(
    company_id: CompanyId,
    warehouse_id: str,
    files: Annotated[
        list[UploadFile],
        File(description="JPG/PNG/PDF — max 5MB each"),
    ],
    current_user: UserRegistration = Depends(get_current_user),
    service: InventoryService = Depends(get_inventory_service),
):
    paths: list[str] = []
    for file in files:
        if file.filename:
            paths.append(await _save_warehouse_attachment(company_id, file))
    if not paths:
        raise ValidationError("At least one attachment file is required")
    warehouse = await service.add_warehouse_attachments(
        current_user, company_id, warehouse_id, paths
    )
    return _to_warehouse_response(warehouse)


@router.delete("/warehouses/{warehouse_id}", response_model=MessageResponse)
async def delete_warehouse(
    company_id: CompanyId,
    warehouse_id: str,
    current_user: UserRegistration = Depends(get_current_user),
    service: InventoryService = Depends(get_inventory_service),
):
    await service.delete_warehouse(current_user, company_id, warehouse_id)
    return MessageResponse(message="Warehouse deleted successfully")


# ---- Brands ----
@router.post("/brands", response_model=BrandResponse, status_code=201)
async def create_brand(
    company_id: CompanyId,
    name: Annotated[str, Form(min_length=1, max_length=200, description="Brand name")],
    code: Annotated[
        str | None,
        Form(min_length=1, max_length=30, description="Auto BR-001 if left blank"),
    ] = None,
    description: Annotated[str | None, Form(max_length=255)] = None,
    website: Annotated[str | None, Form(max_length=255)] = None,
    is_active: Annotated[bool, Form(description="Status Active/Inactive")] = True,
    contact_person: Annotated[str | None, Form(max_length=200)] = None,
    email: Annotated[str | None, Form(max_length=255)] = None,
    phone: Annotated[str | None, Form(max_length=30)] = None,
    address: Annotated[str | None, Form(max_length=500)] = None,
    logo: Annotated[
        UploadFile | None,
        File(description="PNG, JPG, or JPEG — max 2MB"),
    ] = None,
    current_user: UserRegistration = Depends(get_current_user),
    service: InventoryService = Depends(get_inventory_service),
):
    payload = {
        "code": code,
        "name": name,
        "description": description,
        "website": website,
        "is_active": is_active,
        "contact_person": contact_person,
        "email": email,
        "phone": phone,
        "address": address,
    }
    if logo is not None and logo.filename:
        payload["logo"] = await _save_brand_logo(company_id, logo)
    brand = await service.create_brand(current_user, company_id, payload)
    return _to_brand_response(brand)


@router.get("/brands", response_model=DataTableResponse[BrandResponse])
async def list_brands(
    company_id: CompanyId,
    is_active: bool | None = None,
    page: int = Query(1, ge=1),
    page_size: int = Query(20, ge=1, le=500),
    current_user: UserRegistration = Depends(get_current_user),
    service: InventoryService = Depends(get_inventory_service),
):
    skip = (page - 1) * page_size
    items, total = await service.list_brands(
        current_user, company_id, is_active, skip, page_size
    )
    return _build_datatable(
        [_to_brand_response(i) for i in items], total, page, page_size
    )


@router.get("/brands/{brand_id}", response_model=BrandResponse)
async def get_brand(
    company_id: CompanyId,
    brand_id: str,
    current_user: UserRegistration = Depends(get_current_user),
    service: InventoryService = Depends(get_inventory_service),
):
    brand = await service.get_brand(current_user, company_id, brand_id)
    return _to_brand_response(brand)


@router.put("/brands/{brand_id}", response_model=BrandResponse)
async def update_brand(
    company_id: CompanyId,
    brand_id: str,
    code: Annotated[str | None, Form(min_length=1, max_length=30)] = None,
    name: Annotated[str | None, Form(min_length=1, max_length=200)] = None,
    description: Annotated[str | None, Form(max_length=255)] = None,
    website: Annotated[str | None, Form(max_length=255)] = None,
    is_active: Annotated[bool | None, Form()] = None,
    contact_person: Annotated[str | None, Form(max_length=200)] = None,
    email: Annotated[str | None, Form(max_length=255)] = None,
    phone: Annotated[str | None, Form(max_length=30)] = None,
    address: Annotated[str | None, Form(max_length=500)] = None,
    logo: Annotated[
        UploadFile | None,
        File(description="PNG, JPG, or JPEG — max 2MB"),
    ] = None,
    current_user: UserRegistration = Depends(get_current_user),
    service: InventoryService = Depends(get_inventory_service),
):
    payload: dict = {}
    if code is not None:
        payload["code"] = code
    if name is not None:
        payload["name"] = name
    if description is not None:
        payload["description"] = description
    if website is not None:
        payload["website"] = website
    if is_active is not None:
        payload["is_active"] = is_active
    if contact_person is not None:
        payload["contact_person"] = contact_person
    if email is not None:
        payload["email"] = email
    if phone is not None:
        payload["phone"] = phone
    if address is not None:
        payload["address"] = address
    if logo is not None and logo.filename:
        payload["logo"] = await _save_brand_logo(company_id, logo)
    brand = await service.update_brand(current_user, company_id, brand_id, payload)
    return _to_brand_response(brand)


@router.delete("/brands/{brand_id}", response_model=MessageResponse)
async def delete_brand(
    company_id: CompanyId,
    brand_id: str,
    current_user: UserRegistration = Depends(get_current_user),
    service: InventoryService = Depends(get_inventory_service),
):
    await service.delete_brand(current_user, company_id, brand_id)
    return MessageResponse(message="Brand deleted successfully")


# ---- Unit types ----
def _to_unit_type_response(unit_type) -> UnitTypeResponse:
    return UnitTypeResponse(
        id=unit_type.id or "",
        company_id=unit_type.company_id,
        code=unit_type.code,
        name=unit_type.name,
        base_unit_id=unit_type.base_unit_id,
        base_unit_code=getattr(unit_type, "base_unit_code", None),
        base_unit_name=getattr(unit_type, "base_unit_name", None),
        unit_kind=unit_type.unit_kind,
        conversion_rate=unit_type.conversion_rate,
        decimal_places=unit_type.decimal_places,
        is_active=unit_type.is_active,
        sort_order=unit_type.sort_order,
        description=unit_type.description,
        remarks=unit_type.remarks,
        created_at=unit_type.created_at,
        updated_at=unit_type.updated_at,
    )


@router.post("/unit-types", response_model=UnitTypeResponse, status_code=201)
async def create_unit_type(
    company_id: CompanyId,
    payload: UnitTypeCreate,
    current_user: UserRegistration = Depends(get_current_user),
    service: InventoryService = Depends(get_inventory_service),
):
    unit_type = await service.create_unit_type(current_user, company_id, payload.model_dump())
    return _to_unit_type_response(unit_type)


@router.get("/unit-types", response_model=DataTableResponse[UnitTypeResponse])
async def list_unit_types(
    company_id: CompanyId,
    is_active: bool | None = None,
    page: int = Query(1, ge=1),
    page_size: int = Query(20, ge=1, le=500),
    current_user: UserRegistration = Depends(get_current_user),
    service: InventoryService = Depends(get_inventory_service),
):
    skip = (page - 1) * page_size
    items, total = await service.list_unit_types(
        current_user, company_id, is_active, skip, page_size
    )
    return _build_datatable(
        [_to_unit_type_response(i) for i in items], total, page, page_size
    )


@router.get("/unit-types/{unit_type_id}", response_model=UnitTypeResponse)
async def get_unit_type(
    company_id: CompanyId,
    unit_type_id: str,
    current_user: UserRegistration = Depends(get_current_user),
    service: InventoryService = Depends(get_inventory_service),
):
    unit_type = await service.get_unit_type(current_user, company_id, unit_type_id)
    return _to_unit_type_response(unit_type)


@router.put("/unit-types/{unit_type_id}", response_model=UnitTypeResponse)
async def update_unit_type(
    company_id: CompanyId,
    unit_type_id: str,
    payload: UnitTypeUpdate,
    current_user: UserRegistration = Depends(get_current_user),
    service: InventoryService = Depends(get_inventory_service),
):
    unit_type = await service.update_unit_type(
        current_user, company_id, unit_type_id, payload.model_dump(exclude_unset=True)
    )
    return _to_unit_type_response(unit_type)


@router.delete("/unit-types/{unit_type_id}", response_model=MessageResponse)
async def delete_unit_type(
    company_id: CompanyId,
    unit_type_id: str,
    current_user: UserRegistration = Depends(get_current_user),
    service: InventoryService = Depends(get_inventory_service),
):
    await service.delete_unit_type(current_user, company_id, unit_type_id)
    return MessageResponse(message="Unit type deleted successfully")


# ---- Items ----
def _to_item_response(item) -> ItemResponse:
    return ItemResponse(
        id=item.id or "",
        company_id=item.company_id,
        sku=item.sku,
        name=item.name,
        barcode=item.barcode,
        image=getattr(item, "image", None),
        description=item.description,
        specifications=item.specifications,
        remarks=item.remarks,
        category_id=item.category_id,
        category_code=getattr(item, "category_code", None),
        category_name=getattr(item, "category_name", None),
        item_type_id=item.item_type_id,
        item_type_code=getattr(item, "item_type_code", None),
        item_type_name=getattr(item, "item_type_name", None),
        group_id=item.group_id,
        group_code=getattr(item, "group_code", None),
        group_name=getattr(item, "group_name", None),
        brand_name=item.brand_name,
        base_unit_id=item.base_unit_id,
        base_unit_code=getattr(item, "base_unit_code", None),
        base_unit_name=getattr(item, "base_unit_name", None),
        warehouse_id=item.warehouse_id,
        warehouse_code=getattr(item, "warehouse_code", None),
        warehouse_name=getattr(item, "warehouse_name", None),
        unit_type_id=item.unit_type_id,
        pricing_model=item.pricing_model,
        purchase_price=item.purchase_price,
        sale_price=item.sale_price,
        tax_percent=item.tax_percent,
        reorder_level=item.reorder_level,
        track_inventory=item.track_inventory,
        inventory_account_id=item.inventory_account_id,
        expense_account_id=item.expense_account_id,
        cogs_account_id=item.cogs_account_id,
        is_active=item.is_active,
        created_at=item.created_at,
        updated_at=item.updated_at,
    )


@router.post("/items", response_model=ItemResponse, status_code=201)
async def create_item(
    company_id: CompanyId,
    payload: ItemCreate,
    current_user: UserRegistration = Depends(get_current_user),
    service: InventoryService = Depends(get_inventory_service),
):
    item = await service.create_item(current_user, company_id, payload.model_dump())
    return _to_item_response(item)


@router.get("/items", response_model=DataTableResponse[ItemResponse])
async def list_items(
    company_id: CompanyId,
    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,
    page: int = Query(1, ge=1),
    page_size: int = Query(20, ge=1, le=500),
    current_user: UserRegistration = Depends(get_current_user),
    service: InventoryService = Depends(get_inventory_service),
):
    skip = (page - 1) * page_size
    items, total = await service.list_items(
        current_user,
        company_id,
        category_id,
        item_type_id,
        group_id,
        warehouse_id,
        is_active,
        skip,
        page_size,
    )
    return _build_datatable(
        [_to_item_response(i) for i in items], total, page, page_size
    )


@router.get("/items/{item_id}", response_model=ItemResponse)
async def get_item(
    company_id: CompanyId,
    item_id: str,
    current_user: UserRegistration = Depends(get_current_user),
    service: InventoryService = Depends(get_inventory_service),
):
    item = await service.get_item(current_user, company_id, item_id)
    return _to_item_response(item)


@router.put("/items/{item_id}", response_model=ItemResponse)
async def update_item(
    company_id: CompanyId,
    item_id: str,
    payload: ItemUpdate,
    current_user: UserRegistration = Depends(get_current_user),
    service: InventoryService = Depends(get_inventory_service),
):
    item = await service.update_item(
        current_user, company_id, item_id, payload.model_dump(exclude_unset=True)
    )
    return _to_item_response(item)


@router.post("/items/{item_id}/image", response_model=ItemResponse)
async def upload_item_image(
    company_id: CompanyId,
    item_id: str,
    image: Annotated[UploadFile, File(description="PNG, JPG, JPEG, or WEBP — max 2MB")],
    current_user: UserRegistration = Depends(get_current_user),
    service: InventoryService = Depends(get_inventory_service),
):
    if not image.filename:
        raise ValidationError("Item image file is required")
    image_path = await _save_item_image(company_id, image)
    item = await service.update_item(
        current_user, company_id, item_id, {"image": image_path}
    )
    return _to_item_response(item)


@router.delete("/items/{item_id}", response_model=MessageResponse)
async def delete_item(
    company_id: CompanyId,
    item_id: str,
    current_user: UserRegistration = Depends(get_current_user),
    service: InventoryService = Depends(get_inventory_service),
):
    await service.delete_item(current_user, company_id, item_id)
    return MessageResponse(message="Item deleted successfully")


# ---- Stock ----
@router.get("/stock", response_model=DataTableResponse[InventoryBalanceResponse])
async def list_stock(
    company_id: CompanyId,
    page: int = Query(1, ge=1),
    page_size: int = Query(20, ge=1, le=500),
    warehouse_id: str | None = None,
    item_id: str | None = None,
    current_user: UserRegistration = Depends(get_current_user),
    service: InventoryService = Depends(get_inventory_service),
):
    skip = (page - 1) * page_size
    items, total = await service.list_balances(
        current_user, company_id, skip, page_size, warehouse_id, item_id
    )
    return _build_datatable(
        [InventoryBalanceResponse.model_validate(i) for i in items], total, page, page_size
    )


@router.get("/stock/{item_id}", response_model=InventoryBalanceResponse)
async def get_stock(
    company_id: CompanyId,
    item_id: str,
    warehouse_id: str | None = None,
    current_user: UserRegistration = Depends(get_current_user),
    service: InventoryService = Depends(get_inventory_service),
):
    balance = await service.get_balance(current_user, company_id, item_id, warehouse_id)
    return InventoryBalanceResponse.model_validate(balance)


# ---- Transactions ----
@router.post("/transactions", response_model=InventoryTransactionResponse, status_code=201)
async def create_transaction(
    company_id: CompanyId,
    payload: InventoryTransactionCreate,
    current_user: UserRegistration = Depends(get_current_user),
    service: InventoryService = Depends(get_inventory_service),
):
    txn = await service.create_transaction(current_user, company_id, payload.model_dump())
    return InventoryTransactionResponse.model_validate(txn)


@router.get("/transactions", response_model=DataTableResponse[InventoryTransactionResponse])
async def list_transactions(
    company_id: CompanyId,
    item_id: str | None = None,
    page: int = Query(1, ge=1),
    page_size: int = Query(20, ge=1, le=500),
    current_user: UserRegistration = Depends(get_current_user),
    service: InventoryService = Depends(get_inventory_service),
):
    skip = (page - 1) * page_size
    items, total = await service.list_transactions(
        current_user, company_id, item_id, skip, page_size
    )
    return _build_datatable(
        [InventoryTransactionResponse.model_validate(i) for i in items], total, page, page_size
    )


# ---- Item Transactions (Add Item Transaction form) ----
async def _save_item_txn_attachment(company_id: str, file: UploadFile) -> str:
    filename = file.filename or ""
    ext = Path(filename).suffix.lower()
    if ext not in _ITEM_TXN_ATTACHMENT_EXT:
        raise ValidationError("Attachment must be PDF, JPG, or PNG")
    content = await file.read()
    if len(content) > _ITEM_TXN_ATTACHMENT_MAX_BYTES:
        raise ValidationError("Attachment must be 5MB or smaller")
    dest_dir = _STATIC_ROOT / "item-transactions" / company_id
    dest_dir.mkdir(parents=True, exist_ok=True)
    stored_name = f"{uuid.uuid4().hex}{ext}"
    (dest_dir / stored_name).write_bytes(content)
    return f"/static/item-transactions/{company_id}/{stored_name}"


@router.post(
    "/item-transactions",
    response_model=ItemTransactionResponse,
    status_code=201,
)
async def create_item_transaction(
    company_id: CompanyId,
    payload: ItemTransactionCreate,
    current_user: UserRegistration = Depends(get_current_user),
    service: InventoryService = Depends(get_inventory_service),
):
    """Create item transaction (Save as Draft, or post immediately with post_now=true)."""
    doc = await service.create_item_transaction(
        current_user, company_id, payload.model_dump()
    )
    return ItemTransactionResponse.model_validate(doc)


@router.get(
    "/item-transactions",
    response_model=DataTableResponse[ItemTransactionResponse],
)
async def list_item_transactions(
    company_id: CompanyId,
    status: ItemTransactionStatus | None = None,
    direction: InventoryTxnDirection | None = None,
    warehouse_id: str | None = None,
    page: int = Query(1, ge=1),
    page_size: int = Query(20, ge=1, le=500),
    current_user: UserRegistration = Depends(get_current_user),
    service: InventoryService = Depends(get_inventory_service),
):
    """List item transactions (stock in/out documents)."""
    skip = (page - 1) * page_size
    items, total = await service.list_item_transactions(
        current_user, company_id, status, direction, warehouse_id, skip, page_size
    )
    return _build_datatable(
        [ItemTransactionResponse.model_validate(i) for i in items],
        total,
        page,
        page_size,
    )


@router.get(
    "/item-transactions/{txn_id}",
    response_model=ItemTransactionResponse,
)
async def get_item_transaction(
    company_id: CompanyId,
    txn_id: str,
    current_user: UserRegistration = Depends(get_current_user),
    service: InventoryService = Depends(get_inventory_service),
):
    doc = await service.get_item_transaction(current_user, company_id, txn_id)
    return ItemTransactionResponse.model_validate(doc)


@router.put(
    "/item-transactions/{txn_id}",
    response_model=ItemTransactionResponse,
)
async def update_item_transaction(
    company_id: CompanyId,
    txn_id: str,
    payload: ItemTransactionUpdate,
    current_user: UserRegistration = Depends(get_current_user),
    service: InventoryService = Depends(get_inventory_service),
):
    """Update a draft item transaction."""
    doc = await service.update_item_transaction(
        current_user, company_id, txn_id, payload.model_dump(exclude_unset=True)
    )
    return ItemTransactionResponse.model_validate(doc)


@router.post(
    "/item-transactions/{txn_id}/attachments",
    response_model=ItemTransactionResponse,
)
async def upload_item_transaction_attachments(
    company_id: CompanyId,
    txn_id: str,
    files: Annotated[
        list[UploadFile],
        File(description="Supporting documents (PDF/JPG/PNG) — max 5MB each"),
    ],
    current_user: UserRegistration = Depends(get_current_user),
    service: InventoryService = Depends(get_inventory_service),
):
    paths: list[str] = []
    for file in files:
        if file.filename:
            paths.append(await _save_item_txn_attachment(company_id, file))
    if not paths:
        raise ValidationError("At least one attachment file is required")
    doc = await service.add_item_transaction_attachments(
        current_user, company_id, txn_id, paths
    )
    return ItemTransactionResponse.model_validate(doc)


@router.post(
    "/item-transactions/{txn_id}/post",
    response_model=ItemTransactionResponse,
)
async def post_item_transaction(
    company_id: CompanyId,
    txn_id: str,
    current_user: UserRegistration = Depends(get_current_user),
    service: InventoryService = Depends(get_inventory_service),
):
    """Save Transaction — posts stock impact to inventory ledger and balances."""
    doc = await service.post_item_transaction(current_user, company_id, txn_id)
    return ItemTransactionResponse.model_validate(doc)


@router.post(
    "/item-transactions/{txn_id}/cancel",
    response_model=ItemTransactionResponse,
)
async def cancel_item_transaction(
    company_id: CompanyId,
    txn_id: str,
    current_user: UserRegistration = Depends(get_current_user),
    service: InventoryService = Depends(get_inventory_service),
):
    doc = await service.cancel_item_transaction(current_user, company_id, txn_id)
    return ItemTransactionResponse.model_validate(doc)


# ---- Stock transfers ----
@router.post(
    "/stock-transfers",
    response_model=StockTransferResponse,
    status_code=201,
    summary="Create stock transfer (Save as Draft / Next: Review)",
)
async def create_stock_transfer(
    company_id: CompanyId,
    payload: StockTransferCreate,
    current_user: UserRegistration = Depends(get_current_user),
    service: InventoryService = Depends(get_inventory_service),
):
    doc = await service.create_stock_transfer(
        current_user, company_id, payload.model_dump()
    )
    return StockTransferResponse.model_validate(doc)


@router.get(
    "/stock-transfers",
    response_model=DataTableResponse[StockTransferResponse],
)
async def list_stock_transfers(
    company_id: CompanyId,
    status: StockTransferStatus | None = None,
    from_warehouse_id: str | None = None,
    to_warehouse_id: str | None = None,
    page: int = Query(1, ge=1),
    page_size: int = Query(20, ge=1, le=500),
    current_user: UserRegistration = Depends(get_current_user),
    service: InventoryService = Depends(get_inventory_service),
):
    skip = (page - 1) * page_size
    items, total = await service.list_stock_transfers(
        current_user,
        company_id,
        status.value if status else None,
        from_warehouse_id,
        to_warehouse_id,
        skip,
        page_size,
    )
    return _build_datatable(
        [StockTransferResponse.model_validate(i) for i in items],
        total,
        page,
        page_size,
    )


@router.get(
    "/stock-transfers/{transfer_id}",
    response_model=StockTransferResponse,
)
async def get_stock_transfer(
    company_id: CompanyId,
    transfer_id: str,
    current_user: UserRegistration = Depends(get_current_user),
    service: InventoryService = Depends(get_inventory_service),
):
    doc = await service.get_stock_transfer(current_user, company_id, transfer_id)
    return StockTransferResponse.model_validate(doc)


@router.put(
    "/stock-transfers/{transfer_id}",
    response_model=StockTransferResponse,
)
async def update_stock_transfer(
    company_id: CompanyId,
    transfer_id: str,
    payload: StockTransferUpdate,
    current_user: UserRegistration = Depends(get_current_user),
    service: InventoryService = Depends(get_inventory_service),
):
    doc = await service.update_stock_transfer(
        current_user,
        company_id,
        transfer_id,
        payload.model_dump(exclude_unset=True),
    )
    return StockTransferResponse.model_validate(doc)


@router.delete(
    "/stock-transfers/{transfer_id}",
    response_model=MessageResponse,
)
async def delete_stock_transfer(
    company_id: CompanyId,
    transfer_id: str,
    current_user: UserRegistration = Depends(get_current_user),
    service: InventoryService = Depends(get_inventory_service),
):
    await service.delete_stock_transfer(current_user, company_id, transfer_id)
    return MessageResponse(message="Stock transfer deleted successfully")


@router.post(
    "/stock-transfers/{transfer_id}/submit-review",
    response_model=StockTransferResponse,
    summary="Next: Review",
)
async def submit_stock_transfer_for_review(
    company_id: CompanyId,
    transfer_id: str,
    current_user: UserRegistration = Depends(get_current_user),
    service: InventoryService = Depends(get_inventory_service),
):
    doc = await service.submit_stock_transfer_for_review(
        current_user, company_id, transfer_id
    )
    return StockTransferResponse.model_validate(doc)


@router.post(
    "/stock-transfers/{transfer_id}/approve",
    response_model=StockTransferResponse,
)
async def approve_stock_transfer(
    company_id: CompanyId,
    transfer_id: str,
    current_user: UserRegistration = Depends(get_current_user),
    service: InventoryService = Depends(get_inventory_service),
):
    doc = await service.approve_stock_transfer(current_user, company_id, transfer_id)
    return StockTransferResponse.model_validate(doc)


@router.post(
    "/stock-transfers/{transfer_id}/pick-pack",
    response_model=StockTransferResponse,
)
async def start_pick_pack_stock_transfer(
    company_id: CompanyId,
    transfer_id: str,
    current_user: UserRegistration = Depends(get_current_user),
    service: InventoryService = Depends(get_inventory_service),
):
    doc = await service.start_pick_pack_stock_transfer(
        current_user, company_id, transfer_id
    )
    return StockTransferResponse.model_validate(doc)


@router.post(
    "/stock-transfers/{transfer_id}/in-transit",
    response_model=StockTransferResponse,
)
async def mark_stock_transfer_in_transit(
    company_id: CompanyId,
    transfer_id: str,
    current_user: UserRegistration = Depends(get_current_user),
    service: InventoryService = Depends(get_inventory_service),
):
    doc = await service.mark_stock_transfer_in_transit(
        current_user, company_id, transfer_id
    )
    return StockTransferResponse.model_validate(doc)


@router.post(
    "/stock-transfers/{transfer_id}/receive",
    response_model=StockTransferResponse,
)
async def start_receive_stock_transfer(
    company_id: CompanyId,
    transfer_id: str,
    current_user: UserRegistration = Depends(get_current_user),
    service: InventoryService = Depends(get_inventory_service),
):
    doc = await service.start_receive_stock_transfer(
        current_user, company_id, transfer_id
    )
    return StockTransferResponse.model_validate(doc)


@router.post(
    "/stock-transfers/{transfer_id}/complete",
    response_model=StockTransferResponse,
    summary="Complete receive — posts transfer_out + transfer_in ledger",
)
async def complete_stock_transfer(
    company_id: CompanyId,
    transfer_id: str,
    current_user: UserRegistration = Depends(get_current_user),
    service: InventoryService = Depends(get_inventory_service),
):
    doc = await service.complete_stock_transfer(current_user, company_id, transfer_id)
    return StockTransferResponse.model_validate(doc)


@router.post(
    "/stock-transfers/{transfer_id}/cancel",
    response_model=StockTransferResponse,
)
async def cancel_stock_transfer(
    company_id: CompanyId,
    transfer_id: str,
    current_user: UserRegistration = Depends(get_current_user),
    service: InventoryService = Depends(get_inventory_service),
):
    doc = await service.cancel_stock_transfer(current_user, company_id, transfer_id)
    return StockTransferResponse.model_validate(doc)


# ---- Locations (for Department Location dropdown) ----
@router.post("/locations", response_model=LocationResponse, status_code=201)
async def create_location(
    company_id: CompanyId,
    payload: LocationCreate,
    current_user: UserRegistration = Depends(get_current_user),
    service: InventoryService = Depends(get_inventory_service),
):
    location = await service.create_location(
        current_user, company_id, payload.model_dump()
    )
    return LocationResponse.model_validate(location)


@router.get("/locations", response_model=DataTableResponse[LocationResponse])
async def list_locations(
    company_id: CompanyId,
    is_active: bool | None = None,
    page: int = Query(1, ge=1),
    page_size: int = Query(20, ge=1, le=500),
    current_user: UserRegistration = Depends(get_current_user),
    service: InventoryService = Depends(get_inventory_service),
):
    skip = (page - 1) * page_size
    items, total = await service.list_locations(
        current_user, company_id, is_active, skip, page_size
    )
    return _build_datatable(
        [LocationResponse.model_validate(i) for i in items], total, page, page_size
    )


@router.get("/locations/{location_id}", response_model=LocationResponse)
async def get_location(
    company_id: CompanyId,
    location_id: str,
    current_user: UserRegistration = Depends(get_current_user),
    service: InventoryService = Depends(get_inventory_service),
):
    location = await service.get_location(current_user, company_id, location_id)
    return LocationResponse.model_validate(location)


@router.put("/locations/{location_id}", response_model=LocationResponse)
async def update_location(
    company_id: CompanyId,
    location_id: str,
    payload: LocationUpdate,
    current_user: UserRegistration = Depends(get_current_user),
    service: InventoryService = Depends(get_inventory_service),
):
    location = await service.update_location(
        current_user, company_id, location_id, payload.model_dump(exclude_unset=True)
    )
    return LocationResponse.model_validate(location)


@router.delete("/locations/{location_id}", response_model=MessageResponse)
async def delete_location(
    company_id: CompanyId,
    location_id: str,
    current_user: UserRegistration = Depends(get_current_user),
    service: InventoryService = Depends(get_inventory_service),
):
    await service.delete_location(current_user, company_id, location_id)
    return MessageResponse(message="Location deleted successfully")


# ---- Departments ----
@router.post(
    "/departments",
    response_model=DepartmentResponse,
    status_code=201,
    summary="Create department (Save Department / Save as Draft)",
)
async def create_department(
    company_id: CompanyId,
    payload: DepartmentCreate,
    current_user: UserRegistration = Depends(get_current_user),
    service: InventoryService = Depends(get_inventory_service),
):
    department = await service.create_department(
        current_user, company_id, payload.model_dump()
    )
    return DepartmentResponse.model_validate(department)


@router.get("/departments", response_model=DataTableResponse[DepartmentResponse])
async def list_departments(
    company_id: CompanyId,
    is_active: bool | None = None,
    status: DepartmentStatus | None = None,
    location_id: str | None = None,
    page: int = Query(1, ge=1),
    page_size: int = Query(20, ge=1, le=500),
    current_user: UserRegistration = Depends(get_current_user),
    service: InventoryService = Depends(get_inventory_service),
):
    skip = (page - 1) * page_size
    items, total = await service.list_departments(
        current_user,
        company_id,
        is_active,
        status.value if status else None,
        location_id,
        skip,
        page_size,
    )
    return _build_datatable(
        [DepartmentResponse.model_validate(i) for i in items], total, page, page_size
    )


@router.get("/departments/{department_id}", response_model=DepartmentResponse)
async def get_department(
    company_id: CompanyId,
    department_id: str,
    current_user: UserRegistration = Depends(get_current_user),
    service: InventoryService = Depends(get_inventory_service),
):
    department = await service.get_department(current_user, company_id, department_id)
    return DepartmentResponse.model_validate(department)


@router.put("/departments/{department_id}", response_model=DepartmentResponse)
async def update_department(
    company_id: CompanyId,
    department_id: str,
    payload: DepartmentUpdate,
    current_user: UserRegistration = Depends(get_current_user),
    service: InventoryService = Depends(get_inventory_service),
):
    department = await service.update_department(
        current_user,
        company_id,
        department_id,
        payload.model_dump(exclude_unset=True),
    )
    return DepartmentResponse.model_validate(department)


@router.delete("/departments/{department_id}", response_model=MessageResponse)
async def delete_department(
    company_id: CompanyId,
    department_id: str,
    current_user: UserRegistration = Depends(get_current_user),
    service: InventoryService = Depends(get_inventory_service),
):
    await service.delete_department(current_user, company_id, department_id)
    return MessageResponse(message="Department deleted successfully")


# ---- Department issues (Issue Items to Department) ----
@router.post(
    "/department-issues",
    response_model=DepartmentIssueResponse,
    status_code=201,
    summary="Create department issue (Save as Draft / Submit for Approval)",
)
async def create_department_issue(
    company_id: CompanyId,
    payload: DepartmentIssueCreate,
    current_user: UserRegistration = Depends(get_current_user),
    service: InventoryService = Depends(get_inventory_service),
):
    doc = await service.create_department_issue(
        current_user, company_id, payload.model_dump()
    )
    return DepartmentIssueResponse.model_validate(doc)


@router.get(
    "/department-issues",
    response_model=DataTableResponse[DepartmentIssueResponse],
)
async def list_department_issues(
    company_id: CompanyId,
    status: DepartmentIssueStatus | None = None,
    department_id: str | None = None,
    from_warehouse_id: str | None = None,
    page: int = Query(1, ge=1),
    page_size: int = Query(20, ge=1, le=500),
    current_user: UserRegistration = Depends(get_current_user),
    service: InventoryService = Depends(get_inventory_service),
):
    skip = (page - 1) * page_size
    items, total = await service.list_department_issues(
        current_user,
        company_id,
        status.value if status else None,
        department_id,
        from_warehouse_id,
        skip,
        page_size,
    )
    return _build_datatable(
        [DepartmentIssueResponse.model_validate(i) for i in items],
        total,
        page,
        page_size,
    )


@router.get(
    "/department-issues/{issue_id}",
    response_model=DepartmentIssueResponse,
)
async def get_department_issue(
    company_id: CompanyId,
    issue_id: str,
    current_user: UserRegistration = Depends(get_current_user),
    service: InventoryService = Depends(get_inventory_service),
):
    doc = await service.get_department_issue(current_user, company_id, issue_id)
    return DepartmentIssueResponse.model_validate(doc)


@router.put(
    "/department-issues/{issue_id}",
    response_model=DepartmentIssueResponse,
)
async def update_department_issue(
    company_id: CompanyId,
    issue_id: str,
    payload: DepartmentIssueUpdate,
    current_user: UserRegistration = Depends(get_current_user),
    service: InventoryService = Depends(get_inventory_service),
):
    doc = await service.update_department_issue(
        current_user,
        company_id,
        issue_id,
        payload.model_dump(exclude_unset=True),
    )
    return DepartmentIssueResponse.model_validate(doc)


@router.delete(
    "/department-issues/{issue_id}",
    response_model=MessageResponse,
)
async def delete_department_issue(
    company_id: CompanyId,
    issue_id: str,
    current_user: UserRegistration = Depends(get_current_user),
    service: InventoryService = Depends(get_inventory_service),
):
    await service.delete_department_issue(current_user, company_id, issue_id)
    return MessageResponse(message="Department issue deleted successfully")


@router.post(
    "/department-issues/{issue_id}/attachments",
    response_model=DepartmentIssueResponse,
    summary="Upload department issue attachments",
)
async def upload_department_issue_attachments(
    company_id: CompanyId,
    issue_id: str,
    files: Annotated[
        list[UploadFile],
        File(description="JPG/PNG/PDF/DOC/XLS — max 5MB each"),
    ],
    current_user: UserRegistration = Depends(get_current_user),
    service: InventoryService = Depends(get_inventory_service),
):
    paths: list[str] = []
    for file in files:
        if file.filename:
            paths.append(await _save_department_issue_attachment(company_id, file))
    if not paths:
        raise ValidationError("At least one attachment file is required")
    doc = await service.add_department_issue_attachments(
        current_user, company_id, issue_id, paths
    )
    return DepartmentIssueResponse.model_validate(doc)


@router.post(
    "/department-issues/{issue_id}/submit-approval",
    response_model=DepartmentIssueResponse,
    summary="Submit for Approval",
)
async def submit_department_issue_for_approval(
    company_id: CompanyId,
    issue_id: str,
    current_user: UserRegistration = Depends(get_current_user),
    service: InventoryService = Depends(get_inventory_service),
):
    doc = await service.submit_department_issue_for_approval(
        current_user, company_id, issue_id
    )
    return DepartmentIssueResponse.model_validate(doc)


@router.post(
    "/department-issues/{issue_id}/approve",
    response_model=DepartmentIssueResponse,
)
async def approve_department_issue(
    company_id: CompanyId,
    issue_id: str,
    current_user: UserRegistration = Depends(get_current_user),
    service: InventoryService = Depends(get_inventory_service),
):
    doc = await service.approve_department_issue(current_user, company_id, issue_id)
    return DepartmentIssueResponse.model_validate(doc)


@router.post(
    "/department-issues/{issue_id}/issue-items",
    response_model=DepartmentIssueResponse,
    summary="Start Issue Items step",
)
async def start_issue_department_items(
    company_id: CompanyId,
    issue_id: str,
    current_user: UserRegistration = Depends(get_current_user),
    service: InventoryService = Depends(get_inventory_service),
):
    doc = await service.start_issue_department_items(
        current_user, company_id, issue_id
    )
    return DepartmentIssueResponse.model_validate(doc)


@router.post(
    "/department-issues/{issue_id}/complete",
    response_model=DepartmentIssueResponse,
    summary="Complete issue — posts stock out from warehouse",
)
async def complete_department_issue(
    company_id: CompanyId,
    issue_id: str,
    current_user: UserRegistration = Depends(get_current_user),
    service: InventoryService = Depends(get_inventory_service),
):
    doc = await service.complete_department_issue(current_user, company_id, issue_id)
    return DepartmentIssueResponse.model_validate(doc)


@router.post(
    "/department-issues/{issue_id}/cancel",
    response_model=DepartmentIssueResponse,
)
async def cancel_department_issue(
    company_id: CompanyId,
    issue_id: str,
    current_user: UserRegistration = Depends(get_current_user),
    service: InventoryService = Depends(get_inventory_service),
):
    doc = await service.cancel_department_issue(current_user, company_id, issue_id)
    return DepartmentIssueResponse.model_validate(doc)
