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:
    # Проверяем, что адрес доставки существует и принадлежит пользователю, если указан
    print(f"Начало создания заказа для пользователя {user_id}")
    print(f"Данные заказа: {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:
            print(f"Адрес доставки {order.shipping_address_id} не найден для пользователя {user_id}")
            raise HTTPException(
                status_code=status.HTTP_404_NOT_FOUND,
                detail="Указанный адрес доставки не найден"
            )
        print(f"Адрес доставки найден: {address}")
    
    # Создаем новый заказ
    new_order = Order(
        user_id=user_id,
        status=OrderStatus.PENDING,
        shipping_address_id=order.shipping_address_id,
        payment_method=order.payment_method,
        notes=order.notes,
        total_amount=0  # Будет обновлено после добавления товаров
    )
    
    db.add(new_order)
    db.flush()  # Получаем ID заказа
    
    print(f"Создан заказ с ID: {new_order.id}")
    
    # Получаем элементы корзины пользователя
    cart_items = []
    
    # Если указаны конкретные элементы корзины
    if order.cart_items:
        print(f"Используем указанные элементы корзины: {order.cart_items}")
        cart_items = db.query(CartItem).filter(
            CartItem.id.in_(order.cart_items),
            CartItem.user_id == user_id
        ).all()
    # Если указаны прямые элементы заказа
    elif order.items:
        print(f"Используем прямые элементы заказа: {order.items}")
        # Создаем элементы заказа напрямую
        for item_data in order.items:
            # Проверяем, что вариант существует
            variant = db.query(ProductVariant).filter(ProductVariant.id == item_data.variant_id).first()
            if not variant:
                db.rollback()
                raise HTTPException(
                    status_code=status.HTTP_404_NOT_FOUND,
                    detail=f"Вариант товара с ID {item_data.variant_id} не найден"
                )
            
            # Создаем элемент заказа
            order_item = OrderItem(
                order_id=new_order.id,
                product_id=variant.product_id,
                variant_id=variant.id,
                quantity=item_data.quantity,
                price=item_data.price
            )
            db.add(order_item)
            new_order.total_amount += order_item.price * order_item.quantity
    # Иначе используем все элементы корзины пользователя
    else:
        print(f"Используем все элементы корзины пользователя")
        cart_items = db.query(CartItem).filter(CartItem.user_id == user_id).all()
    
    # Если используем элементы корзины
    if cart_items:
        print(f"Найдено {len(cart_items)} элементов корзины")
        # Создаем элементы заказа из элементов корзины
        for cart_item in cart_items:
            # Получаем вариант товара и его цену
            variant = db.query(ProductVariant).filter(ProductVariant.id == cart_item.variant_id).first()
            if not variant:
                db.rollback()
                raise HTTPException(
                    status_code=status.HTTP_404_NOT_FOUND,
                    detail=f"Вариант товара с ID {cart_item.variant_id} не найден"
                )
            
            # Определяем цену (используем скидочную цену, если она есть)
            price = variant.discount_price if variant.discount_price else variant.price
            
            # Создаем элемент заказа
            order_item = OrderItem(
                order_id=new_order.id,
                variant_id=variant.id,
                quantity=cart_item.quantity,
                price=price
            )
            db.add(order_item)
            
            # Обновляем общую сумму заказа
            new_order.total_amount += price * cart_item.quantity
            
            # Удаляем элемент из корзины
            db.delete(cart_item)
    
    try:
        db.commit()
        db.refresh(new_order)
        print(f"Заказ успешно создан: {new_order.id}, общая сумма: {new_order.total_amount}")
        return new_order
    except Exception as e:
        db.rollback()
        print(f"Ошибка при создании заказа: {str(e)}")
        raise HTTPException(
            status_code=status.HTTP_400_BAD_REQUEST,
            detail=f"Ошибка при создании заказа: {str(e)}"
        )


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 = variant.discount_price if variant.discount_price else variant.price
        
        # Формируем результат
        result.append({
            "id": item.id,
            "user_id": item.user_id,
            "variant_id": item.variant_id,
            "slug": product.slug,
            "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,
            "total_price": price * item.quantity
        })
    
    return result


def get_order_with_details(db: Session, order_id: int) -> Optional[Dict]:
    order = db.query(Order).filter(Order.id == order_id).first()
    if not order:
        return None

    # Получаем все товары в заказе с их вариантами
    order_items = (
        db.query(OrderItem)
        .filter(OrderItem.order_id == order_id)
        .all()
    )

    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
        
        # Получаем основное изображение продукта
        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()
        
        items.append({
            "id": item.id,
            "product": {
                "id": product.id,
                "name": product.name,
                "image": image.image_url if image else None,
                "slug": product.slug
            },
            "variant_name": variant.size.name if variant.size else None,
            "quantity": item.quantity,
            "price": item.price
        })

    # Получаем адрес доставки
    shipping_address = None
    if order.shipping_address_id:
        address = db.query(UserAddress).filter(UserAddress.id == order.shipping_address_id).first()
        if address:
            shipping_address = {
                "address_line1": address.address_line1,
                "address_line2": address.address_line2,
                "city": address.city,
                "postal_code": address.postal_code,
                "country": address.country
            }

    return {
        "id": order.id,
        "user_id": order.user_id,
        "user_name": f"{order.user.first_name} {order.user.last_name}" if order.user else None,
        "user_email": order.user.email if order.user else None,
        "status": order.status,
        "total": order.total_amount,
        "created_at": order.created_at,
        "updated_at": order.updated_at,
        "items_count": len(items),
        "items": items,
        "shipping_address": shipping_address,
        "payment_method": order.payment_method,
        "tracking_number": order.tracking_number,
        "notes": order.notes
    }