from sqlalchemy.orm import Session
from sqlalchemy.exc import IntegrityError
from fastapi import HTTPException, status
from typing import List, Optional, Dict, Any
from datetime import datetime

from app.models.order_models import CartItem, Order, OrderItem, OrderStatus, PaymentMethod
from app.models.catalog_models import Product, ProductImage, ProductVariant
from app.models.user_models import User, UserAddress
from app.schemas.order_schemas import CartItemCreate, CartItemUpdate, OrderCreate, OrderUpdate


# Функции для работы с корзиной
def get_cart_item(db: Session, cart_item_id: int) -> Optional[CartItem]:
    return db.query(CartItem).filter(CartItem.id == cart_item_id).first()


def get_user_cart(db: Session, user_id: int) -> List[CartItem]:
    return db.query(CartItem).filter(CartItem.user_id == user_id).all()


def get_cart_item_by_variant(db: Session, user_id: int, variant_id: int) -> Optional[CartItem]:
    return db.query(CartItem).filter(
        CartItem.user_id == user_id,
        CartItem.variant_id == variant_id
    ).first()


def create_cart_item(db: Session, cart_item: CartItemCreate, user_id: int) -> CartItem:
    # Проверяем, что вариант существует
    variant = db.query(ProductVariant).filter(ProductVariant.id == cart_item.variant_id).first()
    if not variant:
        raise HTTPException(
            status_code=status.HTTP_404_NOT_FOUND,
            detail="Вариант продукта не найден"
        )
    
    # Проверяем, что продукт активен
    product = db.query(Product).filter(Product.id == variant.product_id).first()
    if not product or not product.is_active:
        raise HTTPException(
            status_code=status.HTTP_400_BAD_REQUEST,
            detail="Продукт не активен или не найден"
        )
    
    # Проверяем, есть ли уже такой товар в корзине
    existing_item = get_cart_item_by_variant(db, user_id, cart_item.variant_id)
    if existing_item:
        # Обновляем количество
        existing_item.quantity += cart_item.quantity
        try:
            db.commit()
            db.refresh(existing_item)
            return existing_item
        except Exception:
            db.rollback()
            raise HTTPException(
                status_code=status.HTTP_400_BAD_REQUEST,
                detail="Ошибка при обновлении элемента корзины"
            )
    
    # Создаем новый элемент корзины
    db_cart_item = CartItem(
        user_id=user_id,
        variant_id=cart_item.variant_id,
        quantity=cart_item.quantity
    )
    
    try:
        db.add(db_cart_item)
        db.commit()
        db.refresh(db_cart_item)
        return db_cart_item
    except IntegrityError:
        db.rollback()
        raise HTTPException(
            status_code=status.HTTP_400_BAD_REQUEST,
            detail="Ошибка при добавлении товара в корзину"
        )


def update_cart_item(db: Session, cart_item_id: int, cart_item: CartItemUpdate, user_id: int) -> CartItem:
    db_cart_item = db.query(CartItem).filter(
        CartItem.id == cart_item_id,
        CartItem.user_id == user_id
    ).first()
    
    if not db_cart_item:
        raise HTTPException(
            status_code=status.HTTP_404_NOT_FOUND,
            detail="Элемент корзины не найден или не принадлежит пользователю"
        )
    
    # Обновляем только предоставленные поля
    update_data = cart_item.dict(exclude_unset=True)
    
    # Применяем обновления
    for key, value in update_data.items():
        setattr(db_cart_item, key, value)
    
    try:
        db.commit()
        db.refresh(db_cart_item)
        return db_cart_item
    except IntegrityError:
        db.rollback()
        raise HTTPException(
            status_code=status.HTTP_400_BAD_REQUEST,
            detail="Ошибка при обновлении элемента корзины"
        )


def delete_cart_item(db: Session, cart_item_id: int, user_id: int) -> bool:
    db_cart_item = db.query(CartItem).filter(
        CartItem.id == cart_item_id,
        CartItem.user_id == user_id
    ).first()
    
    if not db_cart_item:
        raise HTTPException(
            status_code=status.HTTP_404_NOT_FOUND,
            detail="Элемент корзины не найден или не принадлежит пользователю"
        )
    
    try:
        db.delete(db_cart_item)
        db.commit()
        return True
    except Exception:
        db.rollback()
        raise HTTPException(
            status_code=status.HTTP_400_BAD_REQUEST,
            detail="Ошибка при удалении элемента корзины"
        )


def clear_cart(db: Session, user_id: int) -> bool:
    try:
        db.query(CartItem).filter(CartItem.user_id == user_id).delete()
        db.commit()
        return True
    except Exception:
        db.rollback()
        raise HTTPException(
            status_code=status.HTTP_400_BAD_REQUEST,
            detail="Ошибка при очистке корзины"
        )


# Функции для работы с заказами
def get_order(db: Session, order_id: int) -> Optional[Order]:
    return db.query(Order).filter(Order.id == order_id).first()


def get_user_orders(db: Session, user_id: int, skip: int = 0, limit: int = 100) -> List[Order]:
    return db.query(Order).filter(Order.user_id == user_id).order_by(Order.created_at.desc()).offset(skip).limit(limit).all()


def get_all_orders(
    db: Session, 
    skip: int = 0, 
    limit: int = 100, 
    status: Optional[OrderStatus] = None
) -> List[Order]:
    query = db.query(Order)
    
    if status:
        query = query.filter(Order.status == status)
    
    return query.order_by(Order.created_at.desc()).offset(skip).limit(limit).all()


def create_order(db: Session, order: OrderCreate, user_id: int) -> Order:
    # Проверяем, что адрес доставки существует и принадлежит пользователю, если указан
    if order.shipping_address_id:
        address = db.query(UserAddress).filter(
            UserAddress.id == order.shipping_address_id,
            UserAddress.user_id == user_id
        ).first()
        if not address:
            raise HTTPException(
                status_code=status.HTTP_404_NOT_FOUND,
                detail="Адрес доставки не найден или не принадлежит пользователю"
            )
    
    # Получаем элементы заказа
    order_items = []
    total_amount = 0
    
    # Если указаны элементы корзины, используем их
    if order.cart_items:
        cart_items = db.query(CartItem).filter(
            CartItem.id.in_(order.cart_items),
            CartItem.user_id == user_id
        ).all()
        
        if len(cart_items) != len(order.cart_items):
            raise HTTPException(
                status_code=status.HTTP_400_BAD_REQUEST,
                detail="Некоторые элементы корзины не найдены или не принадлежат пользователю"
            )
        
        for cart_item in cart_items:
            # Получаем вариант и продукт
            variant = db.query(ProductVariant).filter(ProductVariant.id == cart_item.variant_id).first()
            if not variant:
                raise HTTPException(
                    status_code=status.HTTP_404_NOT_FOUND,
                    detail=f"Вариант продукта с ID {cart_item.variant_id} не найден"
                )
            
            product = db.query(Product).filter(Product.id == variant.product_id).first()
            if not product:
                raise HTTPException(
                    status_code=status.HTTP_404_NOT_FOUND,
                    detail=f"Продукт для варианта с ID {cart_item.variant_id} не найден"
                )
            
            # Рассчитываем цену
            price = product.discount_price if product.discount_price else product.price
            price += variant.price_adjustment
            
            # Создаем элемент заказа
            order_item = OrderItem(
                variant_id=cart_item.variant_id,
                quantity=cart_item.quantity,
                price=price
            )
            order_items.append(order_item)
            
            # Обновляем общую сумму
            total_amount += price * cart_item.quantity
    
    # Если указаны прямые элементы заказа, используем их
    elif order.items:
        for item in order.items:
            # Проверяем, что вариант существует
            variant = db.query(ProductVariant).filter(ProductVariant.id == item.variant_id).first()
            if not variant:
                raise HTTPException(
                    status_code=status.HTTP_404_NOT_FOUND,
                    detail=f"Вариант продукта с ID {item.variant_id} не найден"
                )
            
            # Создаем элемент заказа
            order_item = OrderItem(
                variant_id=item.variant_id,
                quantity=item.quantity,
                price=item.price
            )
            order_items.append(order_item)
            
            # Обновляем общую сумму
            total_amount += item.price * item.quantity
    
    else:
        # Если не указаны ни элементы корзины, ни прямые элементы заказа, используем всю корзину пользователя
        cart_items = get_user_cart(db, user_id)
        
        if not cart_items:
            raise HTTPException(
                status_code=status.HTTP_400_BAD_REQUEST,
                detail="Корзина пуста"
            )
        
        for cart_item in cart_items:
            # Получаем вариант и продукт
            variant = db.query(ProductVariant).filter(ProductVariant.id == cart_item.variant_id).first()
            if not variant:
                raise HTTPException(
                    status_code=status.HTTP_404_NOT_FOUND,
                    detail=f"Вариант продукта с ID {cart_item.variant_id} не найден"
                )
            
            product = db.query(Product).filter(Product.id == variant.product_id).first()
            if not product:
                raise HTTPException(
                    status_code=status.HTTP_404_NOT_FOUND,
                    detail=f"Продукт для варианта с ID {cart_item.variant_id} не найден"
                )
            
            # Рассчитываем цену
            price = product.discount_price if product.discount_price else product.price
            price += variant.price_adjustment
            
            # Создаем элемент заказа
            order_item = OrderItem(
                variant_id=cart_item.variant_id,
                quantity=cart_item.quantity,
                price=price
            )
            order_items.append(order_item)
            
            # Обновляем общую сумму
            total_amount += price * cart_item.quantity
    
    # Создаем заказ
    db_order = Order(
        user_id=user_id,
        status=OrderStatus.PENDING,
        total_amount=total_amount,
        shipping_address_id=order.shipping_address_id,
        payment_method=order.payment_method,
        notes=order.notes
    )
    
    try:
        # Добавляем заказ
        db.add(db_order)
        db.flush()  # Получаем ID заказа, не фиксируя транзакцию
        
        # Добавляем элементы заказа
        for item in order_items:
            item.order_id = db_order.id
            db.add(item)
        
        # Очищаем корзину, если заказ создан из корзины
        if not order.items:
            db.query(CartItem).filter(CartItem.user_id == user_id).delete()
        
        db.commit()
        db.refresh(db_order)
        return db_order
    except IntegrityError:
        db.rollback()
        raise HTTPException(
            status_code=status.HTTP_400_BAD_REQUEST,
            detail="Ошибка при создании заказа"
        )


def update_order(db: Session, order_id: int, order: OrderUpdate, is_admin: bool = False) -> Order:
    db_order = get_order(db, order_id)
    if not db_order:
        raise HTTPException(
            status_code=status.HTTP_404_NOT_FOUND,
            detail="Заказ не найден"
        )
    
    # Обычные пользователи могут только отменить заказ
    if not is_admin and order.status and order.status != OrderStatus.CANCELLED:
        raise HTTPException(
            status_code=status.HTTP_403_FORBIDDEN,
            detail="Недостаточно прав для изменения статуса заказа"
        )
    
    # Нельзя изменить статус заказа с CANCELLED или REFUNDED
    if db_order.status in [OrderStatus.CANCELLED, OrderStatus.REFUNDED] and order.status:
        raise HTTPException(
            status_code=status.HTTP_400_BAD_REQUEST,
            detail=f"Нельзя изменить статус заказа, который уже {db_order.status}"
        )
    
    # Обновляем только предоставленные поля
    update_data = order.dict(exclude_unset=True)
    
    # Проверяем, что адрес доставки существует и принадлежит пользователю, если указан
    if "shipping_address_id" in update_data and update_data["shipping_address_id"]:
        address = db.query(UserAddress).filter(
            UserAddress.id == update_data["shipping_address_id"],
            UserAddress.user_id == db_order.user_id
        ).first()
        if not address:
            raise HTTPException(
                status_code=status.HTTP_404_NOT_FOUND,
                detail="Адрес доставки не найден или не принадлежит пользователю"
            )
    
    # Применяем обновления
    for key, value in update_data.items():
        setattr(db_order, key, value)
    
    try:
        db.commit()
        db.refresh(db_order)
        return db_order
    except IntegrityError:
        db.rollback()
        raise HTTPException(
            status_code=status.HTTP_400_BAD_REQUEST,
            detail="Ошибка при обновлении заказа"
        )


def delete_order(db: Session, order_id: int, is_admin: bool = False) -> bool:
    db_order = get_order(db, order_id)
    if not db_order:
        raise HTTPException(
            status_code=status.HTTP_404_NOT_FOUND,
            detail="Заказ не найден"
        )
    
    # Только администраторы могут удалять заказы
    if not is_admin:
        raise HTTPException(
            status_code=status.HTTP_403_FORBIDDEN,
            detail="Недостаточно прав для удаления заказа"
        )
    
    try:
        db.delete(db_order)
        db.commit()
        return True
    except Exception:
        db.rollback()
        raise HTTPException(
            status_code=status.HTTP_400_BAD_REQUEST,
            detail="Ошибка при удалении заказа"
        )


# Функции для получения детальной информации
def get_cart_with_product_details(db: Session, user_id: int) -> List[Dict[str, Any]]:
    cart_items = get_user_cart(db, user_id)
    result = []
    
    for item in cart_items:
        # Получаем информацию о варианте и продукте
        variant = db.query(ProductVariant).filter(ProductVariant.id == item.variant_id).first()
        if not variant:
            continue
        
        product = db.query(Product).filter(Product.id == variant.product_id).first()
        if not product:
            continue
        
        # Получаем основное изображение продукта
        image = db.query(ProductImage).filter(
            ProductImage.product_id == product.id,
            ProductImage.is_primary == True
        ).first()
        
        # Если нет основного изображения, берем первое доступное
        if not image:
            image = db.query(ProductImage).filter(
                ProductImage.product_id == product.id
            ).first()
        
        # Рассчитываем цену
        price = product.discount_price if product.discount_price else product.price
        price += variant.price_adjustment
        
        # Формируем результат
        result.append({
            "id": item.id,
            "user_id": item.user_id,
            "variant_id": item.variant_id,
            "quantity": item.quantity,
            "created_at": item.created_at,
            "updated_at": item.updated_at,
            "product_id": product.id,
            "product_name": product.name,
            "product_price": price,
            "product_image": image.image_url if image else None,
            "variant_name": variant.name,
            "variant_price_adjustment": variant.price_adjustment,
            "total_price": price * item.quantity
        })
    
    return result


def get_order_with_details(db: Session, order_id: int) -> Dict[str, Any]:
    order = get_order(db, order_id)
    if not order:
        raise HTTPException(
            status_code=status.HTTP_404_NOT_FOUND,
            detail="Заказ не найден"
        )
    
    # Получаем пользователя
    user = db.query(User).filter(User.id == order.user_id).first()
    
    # Получаем адрес доставки
    shipping_address = None
    if order.shipping_address_id:
        address = db.query(UserAddress).filter(UserAddress.id == order.shipping_address_id).first()
        if address:
            shipping_address = {
                "id": address.id,
                "address_line1": address.address_line1,
                "address_line2": address.address_line2,
                "city": address.city,
                "state": address.state,
                "postal_code": address.postal_code,
                "country": address.country
            }
    
    # Получаем элементы заказа с деталями
    items = []
    for item in order.items:
        # Получаем информацию о варианте и продукте
        variant = db.query(ProductVariant).filter(ProductVariant.id == item.variant_id).first()
        if not variant:
            continue
        
        product = db.query(Product).filter(Product.id == variant.product_id).first()
        if not product:
            continue
        
        # Формируем элемент заказа
        items.append({
            "id": item.id,
            "order_id": item.order_id,
            "variant_id": item.variant_id,
            "quantity": item.quantity,
            "price": item.price,
            "created_at": item.created_at,
            "product_id": product.id,
            "product_name": product.name,
            "variant_name": variant.name,
            "total_price": item.price * item.quantity
        })
    
    # Формируем результат
    result = {
        "id": order.id,
        "user_id": order.user_id,
        "status": order.status,
        "total_amount": order.total_amount,
        "shipping_address_id": order.shipping_address_id,
        "payment_method": order.payment_method,
        "payment_details": order.payment_details,
        "tracking_number": order.tracking_number,
        "notes": order.notes,
        "created_at": order.created_at,
        "updated_at": order.updated_at,
        "user_email": user.email if user else None,
        "shipping_address": shipping_address,
        "items": items
    }
    
    return result