package integration

import (
	"context"
	"errors"
	"testing"

	"github.com/shopspring/decimal"

	"github.com/TaruLab/keluargamart-backend/internal/cart"
	"github.com/TaruLab/keluargamart-backend/internal/inventory"
	"github.com/TaruLab/keluargamart-backend/internal/sales"
)

func TestCartCheckout_Atomic(t *testing.T) {
	pool := testPool(t)
	setUp(t, pool)
	ctx := context.Background()
	inv := inventory.NewService(inventory.NewRepository(pool), seqAdapter{pool}, wib)
	salesSvc := salesService(pool, inv)
	cartSvc := cartService(pool, salesSvc)

	wh := seedWarehouse(t, pool)
	cat := seedCategory(t, pool)
	unit := seedUnit(t, pool)
	p := seedProduct(t, pool, cat, unit, "100")
	seedStock(t, pool, p.ID, wh, "10")
	user := seedUser(t, pool)

	if _, err := cartSvc.AddItem(ctx, user, p.Code, decimal.NewFromInt(3)); err != nil {
		t.Fatal(err)
	}
	placed, err := cartSvc.Checkout(ctx, user, "Buyer", "buyer@test.local", cart.CheckoutInput{
		WarehouseID: wh, PaymentMethod: "cash",
	})
	if err != nil {
		t.Fatalf("checkout: %v", err)
	}
	if placed == nil || placed.ID == 0 {
		t.Fatal("no placed order")
	}
	// Checkout deducts physical stock immediately (even while pending) —
	// no reservation row is used anymore.
	var quantity, reserved decimal.Decimal
	if err := pool.QueryRow(ctx,
		`SELECT quantity, reserved_quantity FROM stocks WHERE product_id=$1 AND warehouse_id=$2`,
		p.ID, wh).Scan(&quantity, &reserved); err != nil {
		t.Fatal(err)
	}
	if !quantity.Equal(decimal.NewFromInt(7)) {
		t.Errorf("quantity = %s, want 7 (10 - 3 deducted at checkout)", quantity)
	}
	if !reserved.IsZero() {
		t.Errorf("reserved = %s, want 0 (deduction, not reservation)", reserved)
	}
	items, err := cartSvc.GetCart(ctx, user)
	if err != nil {
		t.Fatal(err)
	}
	if len(items.Items) != 0 {
		t.Errorf("cart after successful checkout = %d items, want 0", len(items.Items))
	}

	// Failed checkout must leave the cart intact and create no order.
	if _, err := cartSvc.AddItem(ctx, user, p.Code, decimal.NewFromInt(999)); err != nil {
		t.Fatal(err)
	}
	beforeOrders := soCount(t, pool)
	_, err = cartSvc.Checkout(ctx, user, "Buyer", "buyer@test.local", cart.CheckoutInput{
		WarehouseID: wh, PaymentMethod: "cash",
	})
	if !errors.Is(err, sales.ErrInsufficientStock) {
		t.Fatalf("failed checkout error = %v, want ErrInsufficientStock", err)
	}
	if n := soCount(t, pool); n != beforeOrders {
		t.Errorf("sales_orders after failed checkout = %d, want %d", n, beforeOrders)
	}
	items, err = cartSvc.GetCart(ctx, user)
	if err != nil {
		t.Fatal(err)
	}
	if len(items.Items) != 1 {
		t.Errorf("cart after failed checkout = %d items, want 1 (not cleared)", len(items.Items))
	}
}
