"""Ürün iş mantığı — CRUD, stok durumu, varyant, embedding reindex tetikleme.

NOT: Embedding reindex Celery'ye devredilir. RAG modülü (Adım 6) gelene kadar
`_schedule_reindex` sadece status'ü 'pending'e çeker; task wiring orada yapılır.
"""
import logging
from datetime import datetime
from decimal import Decimal

from fastapi import Depends

from app.models.product import Product
from app.repositories.product_repo import ProductRepository
from app.schemas.product import (
    CreateProductRequest,
    ProductResponse,
    UpdateProductRequest,
)
from app.utils.exceptions import NotFoundError
from app.utils.pagination import Page, decode_cursor, encode_cursor

logger = logging.getLogger("flovy")


def compute_stock_status(stock_quantity: int, low_stock_threshold: int) -> str:
    if stock_quantity <= 0:
        return "out_of_stock"
    if stock_quantity <= low_stock_threshold:
        return "low"
    return "in_stock"


class ProductService:
    def __init__(self, repo: ProductRepository = Depends()):
        self.repo = repo

    async def list_products(
        self, tenant_id: str, limit: int = 20, cursor: str | None = None
    ) -> Page:
        cursor_created_at: datetime | None = None
        cursor_id: str | None = None
        raw = decode_cursor(cursor)
        if raw and "|" in raw:
            ts, cid = raw.split("|", 1)
            try:
                cursor_created_at = datetime.fromisoformat(ts)
                cursor_id = cid
            except ValueError:
                cursor_created_at = None

        rows = await self.repo.list_by_tenant(
            tenant_id, limit + 1, cursor_created_at, cursor_id
        )
        has_more = len(rows) > limit
        rows = rows[:limit]
        items = [ProductResponse.model_validate(p).model_dump(mode="json") for p in rows]

        next_cursor = None
        if has_more and rows:
            last = rows[-1]
            next_cursor = encode_cursor(f"{last.created_at.isoformat()}|{last.id}")

        return Page(items=items, next_cursor=next_cursor, has_more=has_more, limit=limit)

    async def search_products(
        self,
        tenant_id: str,
        query: str | None = None,
        category_id: str | None = None,
        max_price: Decimal | None = None,
        limit: int = 10,
    ) -> list[dict]:
        rows = await self.repo.search(tenant_id, query, category_id, max_price, limit)
        return [ProductResponse.model_validate(p).model_dump(mode="json") for p in rows]

    async def get_product(self, product_id: str, tenant_id: str) -> ProductResponse:
        product = await self._get_or_404(product_id, tenant_id)
        return ProductResponse.model_validate(product)

    async def create_product(
        self, tenant_id: str, body: CreateProductRequest
    ) -> ProductResponse:
        data = body.model_dump(exclude={"variants"})
        data["stock_status"] = compute_stock_status(
            data["stock_quantity"], data["low_stock_threshold"]
        )
        data["embedding_status"] = "pending"
        product = await self.repo.create(tenant_id=tenant_id, **data)

        if body.variants:
            product = await self.repo.replace_variants(
                product, [v.model_dump() for v in body.variants]
            )

        self._schedule_reindex(product)
        return ProductResponse.model_validate(product)

    async def update_product(
        self, product_id: str, tenant_id: str, body: UpdateProductRequest
    ) -> ProductResponse:
        product = await self._get_or_404(product_id, tenant_id)
        updates = body.model_dump(exclude_none=True)

        new_qty = updates.get("stock_quantity", product.stock_quantity)
        new_threshold = updates.get("low_stock_threshold", product.low_stock_threshold)
        updates["stock_status"] = compute_stock_status(new_qty, new_threshold)

        # içerik değişimi embedding'i geçersiz kılar
        if any(k in updates for k in ("title", "description", "bullet_points")):
            updates["embedding_status"] = "pending"

        product = await self.repo.update(product, **updates)

        if updates.get("embedding_status") == "pending":
            self._schedule_reindex(product)
        return ProductResponse.model_validate(product)

    async def delete_product(self, product_id: str, tenant_id: str) -> None:
        product = await self._get_or_404(product_id, tenant_id)
        await self.repo.soft_delete(product)
        self._remove_index(product_id, tenant_id)

    async def reindex_product(self, product_id: str, tenant_id: str) -> ProductResponse:
        product = await self._get_or_404(product_id, tenant_id)
        product = await self.repo.update(product, embedding_status="pending")
        self._schedule_reindex(product)
        return ProductResponse.model_validate(product)

    async def _get_or_404(self, product_id: str, tenant_id: str) -> Product:
        product = await self.repo.get(product_id, tenant_id)
        if not product:
            raise NotFoundError("Ürün")
        return product

    def _schedule_reindex(self, product: Product) -> None:
        """Ürün embedding'ini Celery'ye devret. Broker erişilemezse akışı bozmaz."""
        try:
            from app.tasks.index_knowledge import index_product

            index_product.apply_async(args=[product.id, product.tenant_id], retry=False)
        except Exception as e:  # noqa: BLE001 — broker down olabilir
            logger.warning(
                "index_product enqueue başarısız (product=%s): %s", product.id, e
            )

    def _remove_index(self, product_id: str, tenant_id: str) -> None:
        """Silinen ürünün chunk'larını Celery ile kaldır (gotcha: orphan chunk yok)."""
        try:
            from app.tasks.index_knowledge import remove_product_index

            remove_product_index.apply_async(args=[product_id, tenant_id], retry=False)
        except Exception as e:  # noqa: BLE001
            logger.warning("remove_product_index enqueue başarısız (%s): %s", product_id, e)
