from typing import List, Optional, Tuple
from uuid import uuid4
from logging import getLogger
from pydantic import BaseModel

import toteat.toteat as toteat
from models.sales import ItemWeb, OrderWeb
from models.items import Product
from models.user import User
from services.toteat_service import add_order_item
from services.data_service import DataServiceFactory

logger = getLogger(__name__)

_user_svc = DataServiceFactory.get_user_service()
_product_svc = DataServiceFactory.get_product_service()
_sale_svc = DataServiceFactory.get_sales_service()

PROMO_PRODUCT_TYPE = "Cervezas"
REWARD_POINTS_PER_BEER = 10


class ComparePricesResponse(BaseModel):
    product: Product
    oldPrice: int
    newPrice: int
    isAvailable: bool


def compare_prices(products: List[Product], items: List[ItemWeb]) -> list:
    """Detecta diferencias de precio o productos inactivos entre el carrito y la BD."""
    result = []
    for product, item in zip(products, items):
        if product.status == 0:
            result.append(ComparePricesResponse(
                product=product, oldPrice=item.price,
                newPrice=product.price, isAvailable=False
            ))
        elif product.price != item.price:
            result.append(ComparePricesResponse(
                product=product, oldPrice=item.price,
                newPrice=product.price, isAvailable=True
            ))
    return [r.model_dump() for r in result]


async def fetch_sorted_products(items: List[ItemWeb]) -> List[Product]:
    """Obtiene productos de la BD ordenados igual que los items del carrito."""
    products = await _product_svc.get_products([item.id for item in items])
    return sorted(products, key=lambda x: x.id)


def push_items_to_toteat(
    items: List[ItemWeb],
    products: List[Product],
    table: int,
    user_name: str,
) -> Tuple[List[str], int]:
    """
    Registra cada item en Toteat POS.
    Retorna (errores, cervezas_para_promo).
    Lanza excepción si toteat.get_token() falla — el handler la captura como error fatal.
    """
    toteat.get_token()
    errors: List[str] = []
    beers = 0
    for item, product in zip(items, products):
        try:
            if product.type == PROMO_PRODUCT_TYPE and user_name != "Guest":
                beers += item.quantity
            result = add_order_item(item.id, item.quantity, table)
            if not result:
                errors.append(f"Error adding product {item.id} to table {table}.")
        except Exception as e:
            errors.append(f"Error processing product {item.id}: {e}")
    return errors, beers


def get_active_sale_id(table: int) -> Optional[str]:
    """Devuelve el ID de la venta activa de la mesa en Toteat, o None si no hay."""
    sale = toteat.get_active_sale(toteat.get_table(table))
    return sale['id'] if sale else None


def update_reward_progress(user: User, beers: int) -> int:
    """
    Actualiza puntos de fidelización del usuario.
    No lanza excepción — un fallo aquí no bloquea el pedido.
    """
    new_progress = user.reward_progress + beers * REWARD_POINTS_PER_BEER
    try:
        _user_svc.set_reward_progress(user.id, new_progress)
        logger.info(f"Reward progress updated for user {user.email}: {user.reward_progress} -> {new_progress}")
    except Exception as e:
        logger.error(f"Error updating reward progress for user {user.id}: {e}")
        new_progress = user.reward_progress
    return new_progress


def create_sale_record(order: OrderWeb, active_sale_id: str, items: List[ItemWeb]) -> int:
    """Persiste la venta en PostgreSQL. Retorna el ID o -1 si falló."""
    return _sale_svc.create(
        order.customerId,
        active_sale_id or uuid4().hex,
        order.totalAmount,
        order.table,
        [item.id for item in items],
        [item.quantity for item in items],
    )
