import re
from datetime import datetime, timezone
from typing import List, Optional, Tuple
from sqlalchemy.orm import Session
from app.models.product import Product
from app.models.category import Category
from app.repositories.product import product_repository
from app.repositories.category import category_repository
from app.core.exceptions import NotFoundException, BadRequestException

class ProductService:
    """Business logic layer for handling categories and products lifecycle."""

    def _generate_slug(self, text: str) -> str:
        """Helper to convert string into URL-friendly slug."""
        text = text.lower().strip()
        text = re.sub(r'[^\w\s-]', '', text)
        return re.sub(r'[-\s]+', '-', text)

    # --- CATEGORY ACTIONS ---
    def create_category(self, db: Session, name: str, description: Optional[str] = None) -> Category:
        slug = self._generate_slug(name)
        existing = category_repository.get_by_slug(db, slug)
        if existing:
            raise BadRequestException("Category slug already exists.")
            
        category = Category(name=name, slug=slug, description=description)
        return category_repository.create(db, obj_in=category)

    def list_categories(self, db: Session) -> List[Category]:
        return category_repository.get_multi(db, limit=100)

    # --- PRODUCT ACTIONS ---
    def create_product(
        self,
        db: Session,
        *,
        name: str,
        description: str,
        price: float,
        stock: int,
        material: str,
        category_id: str,
        image_url: Optional[str] = None
    ) -> Product:
        """Create a new product with custom generated slug."""
        slug = self._generate_slug(name)
        # Tambahkan suffix acak singkat jika slug duplikat
        existing = product_repository.get_by_slug(db, slug)
        if existing:
            import time
            slug = f"{slug}-{int(time.time())}"

        # Validasi kategori ada
        cat = category_repository.get(db, id=category_id)
        if not cat:
            raise NotFoundException("Category not found.")

        if material.lower() not in ["titanium", "xuping"]:
            raise BadRequestException("Material must be either 'titanium' or 'xuping'.")

        product = Product(
            name=name,
            slug=slug,
            description=description,
            price=price,
            stock=stock,
            material=material.lower(),
            category_id=category_id,
            image_url=image_url
        )
        return product_repository.create(db, obj_in=product)

    def get_product(self, db: Session, product_id: str) -> Product:
        product = product_repository.get(db, id=product_id)
        if not product or product.deleted_at is not None:
            raise NotFoundException("Product not found.")
        return product

    def get_product_by_slug(self, db: Session, slug: str) -> Product:
        product = product_repository.get_by_slug(db, slug)
        if not product:
            raise NotFoundException("Product not found.")
        return product

    def list_products(
        self,
        db: Session,
        *,
        search: Optional[str] = None,
        material: Optional[str] = None,
        category_id: Optional[str] = None,
        sort_by: Optional[str] = "created_at",
        sort_order: Optional[str] = "desc",
        skip: int = 0,
        limit: int = 12
    ) -> Tuple[List[Product], int]:
        """Query products with paging, filter, and search options."""
        return product_repository.get_filtered_products(
            db,
            search=search,
            material=material,
            category_id=category_id,
            sort_by=sort_by,
            sort_order=sort_order,
            skip=skip,
            limit=limit
        )

    def update_product(self, db: Session, product_id: str, obj_in: dict) -> Product:
        product = self.get_product(db, product_id)
        if "name" in obj_in:
            obj_in["slug"] = self._generate_slug(obj_in["name"])
        return product_repository.update(db, db_obj=product, obj_in=obj_in)

    def soft_delete_product(self, db: Session, product_id: str) -> Product:
        """Mark product deleted by setting deleted_at time without physical removal."""
        product = self.get_product(db, product_id)
        product.deleted_at = datetime.now(timezone.utc)
        db.add(product)
        db.commit()
        db.refresh(product)
        return product

product_service = ProductService()
