from sqlalchemy.orm import Session
from fastapi import HTTPException, status, UploadFile
from typing import List, Dict, Any, Optional
import os
import uuid
import shutil
from pathlib import Path

from app.config import settings
from app.repositories import catalog_repo, review_repo
from app.schemas.catalog_schemas import (
    CategoryCreate, CategoryUpdate, Category, CategoryWithSubcategories,
    ProductCreate, ProductUpdate, Product, ProductWithDetails,
    ProductVariantCreate, ProductVariantUpdate, ProductVariant,
    ProductImageCreate, ProductImageUpdate, ProductImage,
    CollectionCreate, CollectionUpdate, Collection,
    SizeCreate, SizeUpdate, Size
)


# Сервисы для коллекций
def create_collection(db: Session, collection: CollectionCreate) -> Dict[str, Any]:
    from app.schemas.catalog_schemas import Collection as CollectionSchema
    
    new_collection = catalog_repo.create_collection(db, collection)
    # Преобразуем объект SQLAlchemy в схему Pydantic
    collection_schema = CollectionSchema.model_validate(new_collection)
    return {"collection": collection_schema}


def update_collection(db: Session, collection_id: int, collection: CollectionUpdate) -> Dict[str, Any]:
    from app.schemas.catalog_schemas import Collection as CollectionSchema
    
    updated_collection = catalog_repo.update_collection(db, collection_id, collection)
    # Преобразуем объект SQLAlchemy в схему Pydantic
    collection_schema = CollectionSchema.model_validate(updated_collection)
    return {"collection": collection_schema}


def delete_collection(db: Session, collection_id: int) -> Dict[str, Any]:
    success = catalog_repo.delete_collection(db, collection_id)
    return {"success": success}


def get_collections(db: Session, skip: int = 0, limit: int = 100) -> Dict[str, Any]:
    from app.schemas.catalog_schemas import Collection as CollectionSchema
    
    collections = catalog_repo.get_collections(db, skip, limit)
    # Преобразуем объекты SQLAlchemy в схемы Pydantic
    collections_schema = [CollectionSchema.model_validate(collection) for collection in collections]
    return {"collections": collections_schema, "total": len(collections_schema)}


# Сервисы каталога
def create_category(db: Session, category: CategoryCreate) -> Dict[str, Any]:
    from app.schemas.catalog_schemas import Category as CategorySchema
    
    new_category = catalog_repo.create_category(db, category)
    # Преобразуем объект SQLAlchemy в схему Pydantic
    category_schema = CategorySchema.model_validate(new_category)
    return {"category": category_schema}


def update_category(db: Session, category_id: int, category: CategoryUpdate) -> Dict[str, Any]:
    from app.schemas.catalog_schemas import Category as CategorySchema
    
    updated_category = catalog_repo.update_category(db, category_id, category)
    # Преобразуем объект SQLAlchemy в схему Pydantic
    category_schema = CategorySchema.model_validate(updated_category)
    return {"category": category_schema}


def delete_category(db: Session, category_id: int) -> Dict[str, Any]:
    success = catalog_repo.delete_category(db, category_id)
    return {"success": success}


def get_category_tree(db: Session) -> List[Dict[str, Any]]:
    from app.schemas.catalog_schemas import Category as CategorySchema
    from sqlalchemy import func
    from app.models.catalog_models import Product
    
    # Получаем все категории верхнего уровня
    root_categories = catalog_repo.get_categories(db, parent_id=None)
    
    result = []
    for category in root_categories:
        # Преобразуем объект SQLAlchemy в схему Pydantic
        category_schema = CategorySchema.model_validate(category)
        # Получаем количество продуктов в категории
        products_count = db.query(func.count(Product.id)).filter(
            Product.category_id == category.id,
            Product.is_active == True
        ).scalar() or 0
        
        # Рекурсивно получаем подкатегории
        category_dict = category_schema.model_dump()
        subcategories, subcategories_products_count = _get_subcategories(db, category.id)
        category_dict["subcategories"] = subcategories
        category_dict["products_count"] = products_count + subcategories_products_count
        result.append(category_dict)
    
    return result


def _get_subcategories(db: Session, parent_id: int) -> tuple[List[Dict[str, Any]], int]:
    from app.schemas.catalog_schemas import Category as CategorySchema
    from sqlalchemy import func
    from app.models.catalog_models import Product
    
    subcategories = catalog_repo.get_categories(db, parent_id=parent_id)
    
    result = []
    total_products_count = 0
    
    for category in subcategories:
        # Преобразуем объект SQLAlchemy в схему Pydantic
        category_schema = CategorySchema.model_validate(category)
        
        # Получаем количество продуктов в категории
        products_count = db.query(func.count(Product.id)).filter(
            Product.category_id == category.id,
            Product.is_active == True
        ).scalar() or 0
        
        category_dict = category_schema.model_dump()
        sub_subcategories, sub_products_count = _get_subcategories(db, category.id)
        
        total_products_in_category = products_count + sub_products_count
        total_products_count += total_products_in_category
        
        category_dict["subcategories"] = sub_subcategories
        category_dict["products_count"] = total_products_in_category
        
        result.append(category_dict)
    
    return result, total_products_count


def create_product(db: Session, product: ProductCreate) -> Dict[str, Any]:
    """Создать новый продукт"""
    try:
        # Проверяем, что категория существует
        category = catalog_repo.get_category(db, product.category_id)
        if not category:
            raise HTTPException(
                status_code=status.HTTP_404_NOT_FOUND,
                detail=f"Категория с ID {product.category_id} не найдена"
            )
        
        # Проверяем, что коллекция существует, если она указана
        if product.collection_id:
            collection = catalog_repo.get_collection(db, product.collection_id)
            if not collection:
                raise HTTPException(
                    status_code=status.HTTP_404_NOT_FOUND,
                    detail=f"Коллекция с ID {product.collection_id} не найдена"
                )
        
        # Создаем продукт
        db_product = catalog_repo.create_product(db, product)
        
        return {
            "success": True,
            "product": Product.from_orm(db_product)
        }
    except HTTPException as e:
        return {
            "success": False,
            "error": e.detail
        }
    except Exception as e:
        return {
            "success": False,
            "error": str(e)
        }


def update_product(db: Session, product_id: int, product: ProductUpdate) -> Dict[str, Any]:
    """Обновить продукт"""
    try:
        # Проверяем, что продукт существует
        db_product = catalog_repo.get_product(db, product_id)
        if not db_product:
            raise HTTPException(
                status_code=status.HTTP_404_NOT_FOUND,
                detail=f"Продукт с ID {product_id} не найден"
            )
        
        # Если меняется категория, проверяем, что она существует
        if product.category_id is not None:
            category = catalog_repo.get_category(db, product.category_id)
            if not category:
                raise HTTPException(
                    status_code=status.HTTP_404_NOT_FOUND,
                    detail=f"Категория с ID {product.category_id} не найдена"
                )
        
        # Если меняется коллекция, проверяем, что она существует
        if product.collection_id is not None:
            if product.collection_id:
                collection = catalog_repo.get_collection(db, product.collection_id)
                if not collection:
                    raise HTTPException(
                        status_code=status.HTTP_404_NOT_FOUND,
                        detail=f"Коллекция с ID {product.collection_id} не найдена"
                    )
        
        # Обновляем продукт
        updated_product = catalog_repo.update_product(db, product_id, product)
        
        return {
            "success": True,
            "product": Product.from_orm(updated_product)
        }
    except HTTPException as e:
        return {
            "success": False,
            "error": e.detail
        }
    except Exception as e:
        return {
            "success": False,
            "error": str(e)
        }


def delete_product(db: Session, product_id: int) -> Dict[str, Any]:
    success = catalog_repo.delete_product(db, product_id)
    return {"success": success}


def get_product_details(db: Session, product_id: int) -> Dict[str, Any]:
    """Получить детальную информацию о продукте"""
    try:
        # Получаем продукт
        product = catalog_repo.get_product(db, product_id)
        if not product:
            raise HTTPException(
                status_code=status.HTTP_404_NOT_FOUND,
                detail=f"Продукт с ID {product_id} не найден"
            )
        
        # Получаем варианты продукта
        variants = catalog_repo.get_product_variants(db, product_id)
        
        # Получаем изображения продукта
        images = catalog_repo.get_product_images(db, product_id)
        
        # Получаем категорию
        category = catalog_repo.get_category(db, product.category_id)
        
        # Получаем коллекцию, если она указана
        collection = None
        if product.collection_id:
            collection = catalog_repo.get_collection(db, product.collection_id)
        
        # Создаем детальное представление продукта
        product_details = ProductWithDetails(
            id=product.id,
            name=product.name,
            slug=product.slug,
            description=product.description,
            price=product.price,
            discount_price=product.discount_price,
            care_instructions=product.care_instructions,
            is_active=product.is_active,
            category_id=product.category_id,
            collection_id=product.collection_id,
            created_at=product.created_at,
            updated_at=product.updated_at,
            category=Category.from_orm(category),
            collection=Collection.from_orm(collection) if collection else None,
            variants=[ProductVariant.from_orm(variant) for variant in variants],
            images=[ProductImage.from_orm(image) for image in images]
        )
        
        return {
            "success": True,
            "product": product_details
        }
    except HTTPException as e:
        return {
            "success": False,
            "error": e.detail
        }
    except Exception as e:
        return {
            "success": False,
            "error": str(e)
        }


def add_product_variant(db: Session, variant: ProductVariantCreate) -> Dict[str, Any]:
    """Добавить вариант продукта"""
    try:
        # Проверяем, что продукт существует
        product = catalog_repo.get_product(db, variant.product_id)
        if not product:
            raise HTTPException(
                status_code=status.HTTP_404_NOT_FOUND,
                detail=f"Продукт с ID {variant.product_id} не найден"
            )
        
        # Проверяем, что размер существует
        size = catalog_repo.get_size(db, variant.size_id)
        if not size:
            raise HTTPException(
                status_code=status.HTTP_404_NOT_FOUND,
                detail=f"Размер с ID {variant.size_id} не найден"
            )
        
        # Создаем вариант продукта
        db_variant = catalog_repo.create_product_variant(db, variant)
        
        return {
            "success": True,
            "variant": ProductVariant.from_orm(db_variant)
        }
    except HTTPException as e:
        return {
            "success": False,
            "error": e.detail
        }
    except Exception as e:
        return {
            "success": False,
            "error": str(e)
        }


def update_product_variant(db: Session, variant_id: int, variant: ProductVariantUpdate) -> Dict[str, Any]:
    """Обновить вариант продукта"""
    try:
        # Проверяем, что вариант существует
        db_variant = catalog_repo.get_product_variant(db, variant_id)
        if not db_variant:
            raise HTTPException(
                status_code=status.HTTP_404_NOT_FOUND,
                detail=f"Вариант продукта с ID {variant_id} не найден"
            )
        
        # Если меняется продукт, проверяем, что он существует
        if variant.product_id is not None:
            product = catalog_repo.get_product(db, variant.product_id)
            if not product:
                raise HTTPException(
                    status_code=status.HTTP_404_NOT_FOUND,
                    detail=f"Продукт с ID {variant.product_id} не найден"
                )
        
        # Если меняется размер, проверяем, что он существует
        if variant.size_id is not None:
            size = catalog_repo.get_size(db, variant.size_id)
            if not size:
                raise HTTPException(
                    status_code=status.HTTP_404_NOT_FOUND,
                    detail=f"Размер с ID {variant.size_id} не найден"
                )
        
        # Обновляем вариант продукта
        updated_variant = catalog_repo.update_product_variant(db, variant_id, variant)
        
        return {
            "success": True,
            "variant": ProductVariant.from_orm(updated_variant)
        }
    except HTTPException as e:
        return {
            "success": False,
            "error": e.detail
        }
    except Exception as e:
        return {
            "success": False,
            "error": str(e)
        }


def delete_product_variant(db: Session, variant_id: int) -> Dict[str, Any]:
    success = catalog_repo.delete_product_variant(db, variant_id)
    return {"success": success}


def upload_product_image(db: Session, product_id: int, file: UploadFile, is_primary: bool = False) -> Dict[str, Any]:
    from app.schemas.catalog_schemas import ProductImage as ProductImageSchema
    
    # Проверяем, что продукт существует
    product = catalog_repo.get_product(db, product_id)
    if not product:
        raise HTTPException(
            status_code=status.HTTP_404_NOT_FOUND,
            detail="Продукт не найден"
        )
    
    # Проверяем расширение файла
    file_extension = file.filename.split(".")[-1].lower()
    if file_extension not in settings.ALLOWED_UPLOAD_EXTENSIONS:
        raise HTTPException(
            status_code=status.HTTP_400_BAD_REQUEST,
            detail=f"Неподдерживаемый формат файла. Разрешены: {', '.join(settings.ALLOWED_UPLOAD_EXTENSIONS)}"
        )
    
    # Создаем директорию для загрузок, если она не существует
    upload_dir = Path(settings.UPLOAD_DIRECTORY) / "products" / str(product_id)
    upload_dir.mkdir(parents=True, exist_ok=True)
    
    # Генерируем уникальное имя файла
    unique_filename = f"{uuid.uuid4()}.{file_extension}"
    file_path = upload_dir / unique_filename
    
    # Сохраняем файл
    with file_path.open("wb") as buffer:
        shutil.copyfileobj(file.file, buffer)
    
    # Создаем запись об изображении в БД
    image_data = ProductImageCreate(
        product_id=product_id,
        image_url=f"/uploads/products/{product_id}/{unique_filename}",
        alt_text=file.filename,
        is_primary=is_primary
    )
    
    new_image = catalog_repo.create_product_image(db, image_data)
    
    # Преобразуем объект SQLAlchemy в схему Pydantic
    image_schema = ProductImageSchema.model_validate(new_image)
    return {"image": image_schema}


def update_product_image(db: Session, image_id: int, is_primary: bool) -> Dict[str, Any]:
    from app.schemas.catalog_schemas import ProductImage as ProductImageSchema
    
    updated_image = catalog_repo.update_product_image(db, image_id, is_primary)
    # Преобразуем объект SQLAlchemy в схему Pydantic
    image_schema = ProductImageSchema.model_validate(updated_image)
    return {"image": image_schema}


def delete_product_image(db: Session, image_id: int) -> Dict[str, Any]:
    # Получаем информацию об изображении перед удалением
    image = catalog_repo.get_product_image(db, image_id)
    if not image:
        raise HTTPException(
            status_code=status.HTTP_404_NOT_FOUND,
            detail="Изображение не найдено"
        )
    
    # Удаляем запись из БД
    success = catalog_repo.delete_product_image(db, image_id)
    
    # Удаляем файл с диска
    if success:
        try:
            # Получаем путь к файлу из URL
            file_path = Path(settings.UPLOAD_DIRECTORY) / image.image_url.lstrip("/uploads/")
            if file_path.exists():
                file_path.unlink()
        except Exception:
            # Если не удалось удалить файл, просто логируем ошибку
            # В реальном приложении здесь должно быть логирование
            pass
    
    return {"success": success}


# Функции для работы с размерами
def get_size(db: Session, size_id: int) -> Size:
    db_size = catalog_repo.get_size(db, size_id)
    if not db_size:
        raise HTTPException(
            status_code=status.HTTP_404_NOT_FOUND,
            detail=f"Размер с ID {size_id} не найден"
        )
    return Size.from_orm(db_size)


def get_size_by_code(db: Session, code: str) -> Size:
    db_size = catalog_repo.get_size_by_code(db, code)
    if not db_size:
        raise HTTPException(
            status_code=status.HTTP_404_NOT_FOUND,
            detail=f"Размер с кодом {code} не найден"
        )
    return Size.from_orm(db_size)


def get_sizes(db: Session, skip: int = 0, limit: int = 100) -> List[Size]:
    db_sizes = catalog_repo.get_sizes(db, skip, limit)
    return [Size.from_orm(size) for size in db_sizes]


def create_size(db: Session, size: SizeCreate) -> Size:
    return Size.from_orm(catalog_repo.create_size(db, size))


def update_size(db: Session, size_id: int, size: SizeUpdate) -> Size:
    return Size.from_orm(catalog_repo.update_size(db, size_id, size))


def delete_size(db: Session, size_id: int) -> bool:
    return catalog_repo.delete_size(db, size_id) 