"""Product service — business logic for the global product catalogue and price history."""

from datetime import date, timedelta
from decimal import Decimal

from sqlalchemy.exc import IntegrityError, SQLAlchemyError
from sqlalchemy.ext.asyncio import AsyncSession

from app.models.product import Produk, ProdukHarga
from app.repositories import product_repository
from app.schemas.product import ProdukCreate, ProdukHargaCreate, ProdukUpdate


async def list_products(
    db: AsyncSession, skip: int = 0, limit: int = 50
) -> tuple[list[dict], int]:
    """List all products with their current price (single bulk price query, no N+1)."""
    produk_list, total = await product_repository.get_all(db, skip, limit)
    today = date.today()
    # Single query for all current prices — avoids N+1
    harga_map = await product_repository.get_current_harga_bulk(
        db, [p.id for p in produk_list], today
    )
    results = [
        {
            "id": p.id,
            "nama": p.nama,
            "kode": p.kode,
            "jenis": p.jenis,
            "is_subsidi": p.is_subsidi,
            "kuota_config": p.kuota_config,
            "is_active": p.is_active,
            "harga_saat_ini": harga_map[p.id].harga if p.id in harga_map else None,
            "berlaku_sejak": harga_map[p.id].berlaku_mulai if p.id in harga_map else None,
        }
        for p in produk_list
    ]
    return results, total


async def create_product(db: AsyncSession, data: ProdukCreate) -> Produk:
    """Create a new product, optionally inserting an initial price entry."""
    harga_awal = data.harga_awal
    berlaku_mulai = data.berlaku_mulai or date.today()
    produk_data = data.model_dump(exclude={"harga_awal", "berlaku_mulai"})
    try:
        produk = await product_repository.create(db, **produk_data)
        if harga_awal is not None:
            await product_repository.create_harga(db, produk.id, harga_awal, berlaku_mulai)
        await db.commit()
        return produk
    except IntegrityError:
        await db.rollback()
        raise ValueError("Data produk konflik atau sudah ada")
    except SQLAlchemyError:
        await db.rollback()
        raise


async def get_product(db: AsyncSession, produk_id: int) -> Produk:
    """Return a product by ID, raising ValueError if not found."""
    produk = await product_repository.get_by_id(db, produk_id)
    if not produk:
        raise ValueError("Produk tidak ditemukan")
    return produk


async def update_product(
    db: AsyncSession, produk_id: int, data: ProdukUpdate
) -> Produk:
    """Update scalar fields on a product."""
    produk = await get_product(db, produk_id)
    update_data = data.model_dump(exclude_none=True)
    try:
        result = await product_repository.update(db, produk, **update_data)
        await db.commit()
        return result
    except IntegrityError:
        await db.rollback()
        raise ValueError("Data produk konflik atau sudah ada")
    except SQLAlchemyError:
        await db.rollback()
        raise


async def add_harga(
    db: AsyncSession, produk_id: int, data: ProdukHargaCreate
) -> ProdukHarga:
    """Add a new price entry for a product.

    Atomically closes the current open price (berlaku_sampai = new_date - 1 day via flush),
    then inserts the new price and commits both changes in one transaction.
    """
    await get_product(db, produk_id)
    berlaku_sampai = data.berlaku_mulai - timedelta(days=1)
    try:
        # close_current_harga only flushes — part of same transaction
        await product_repository.close_current_harga(db, produk_id, berlaku_sampai)
        result = await product_repository.create_harga(db, produk_id, data.harga, data.berlaku_mulai)
        await db.commit()
        return result
    except IntegrityError:
        await db.rollback()
        raise ValueError("Data harga konflik atau sudah ada")
    except SQLAlchemyError:
        await db.rollback()
        raise


async def get_harga_history(
    db: AsyncSession, produk_id: int
) -> list[ProdukHarga]:
    """Return the full price history for a product."""
    await get_product(db, produk_id)
    return await product_repository.get_harga_history(db, produk_id)
