package integration

import (
	"context"
	"strings"
	"testing"
	"time"

	"github.com/shopspring/decimal"

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

// TestMembershipBenefitSnapshotImmutable verifies the Phase 6 item 3 membership
// benefit is computed at transaction time and frozen into the immutable
// sales-line snapshot: the discount and tier cannot be altered after the fact,
// the membership audit is preserved, and a later tier change does not rewrite
// the historical line (business-rules.md §13.1, migrations 000011/000013).
func TestMembershipBenefitSnapshotImmutable(t *testing.T) {
	pool := testPool(t)
	setUp(t, pool)
	ctx := context.Background()

	cat := seedCategory(t, pool)
	unit := seedUnit(t, pool)
	prod := seedProduct(t, pool, cat, unit, "12000")
	wh := seedWarehouse(t, pool)
	seedStock(t, pool, prod.ID, wh, "100")
	cust := seedCustomer(t, pool)

	memRepo := membership.NewRepository(pool)
	memSvc := membership.NewService(memRepo, seqAdapter{pool}, wib)
	created, err := memSvc.Create(ctx, membership.CreateInput{
		CustomerID: cust, Tier: membership.TierGold, JoinedDate: "2026-08-17",
	}, nil)
	if err != nil {
		t.Fatalf("create membership: %v", err)
	}

	invSvc := inventory.NewService(inventory.NewRepository(pool), seqAdapter{pool}, wib)
	svc := salesService(pool, invSvc)
	order, err := svc.Create(ctx, sales.SOInput{
		CustomerID:  cust,
		WarehouseID: &wh,
		OrderDate:   time.Now().In(wib).Format("2006-01-02"),
		Items:       []sales.SOItemInput{{ProductID: prod.ID, Quantity: decimal.NewFromInt(1)}},
	})
	if err != nil {
		t.Fatalf("create order: %v", err)
	}

	// The frozen snapshot must record the gold 5% benefit on the line.
	var (
		tier    string
		discAmt decimal.Decimal
	)
	if err := pool.QueryRow(ctx,
		`SELECT membership_tier, membership_discount_amount FROM sales_order_items WHERE sales_order_id = $1`,
		order.ID).Scan(&tier, &discAmt); err != nil {
		t.Fatalf("read snapshot: %v", err)
	}
	if tier != "gold" {
		t.Fatalf("snapshot tier = %q, want gold", tier)
	}
	if !discAmt.Equal(decimal.NewFromInt(600)) {
		t.Fatalf("snapshot discount = %s, want 600 (5%% of 12000)", discAmt)
	}

	// The immutable guard must reject any rewrite of the snapshot columns.
	if _, err := pool.Exec(ctx,
		`UPDATE sales_order_items SET membership_discount_amount = 0 WHERE sales_order_id = $1`,
		order.ID); err == nil {
		t.Fatal("expected immutable UPDATE to be rejected, got nil")
	} else if !strings.Contains(err.Error(), "immutable") {
		t.Fatalf("expected immutable error, got %v", err)
	}

	// The membership tier audit must be preserved.
	var histCount int
	if err := pool.QueryRow(ctx,
		`SELECT count(*) FROM membership_tier_history WHERE membership_id = $1`,
		created.ID).Scan(&histCount); err != nil {
		t.Fatalf("audit count: %v", err)
	}
	if histCount < 1 {
		t.Fatalf("expected >=1 tier-history rows, got %d", histCount)
	}

	// A later tier change must NOT rewrite the already-stored snapshot.
	if _, err := memSvc.Update(ctx, created.ID, membership.UpdateInput{
		Tier: membership.TierPlatinum, Reason: "upgrade",
	}, nil); err != nil {
		t.Fatalf("update tier: %v", err)
	}
	var (
		tier2    string
		discAmt2 decimal.Decimal
	)
	if err := pool.QueryRow(ctx,
		`SELECT membership_tier, membership_discount_amount FROM sales_order_items WHERE sales_order_id = $1`,
		order.ID).Scan(&tier2, &discAmt2); err != nil {
		t.Fatalf("read snapshot again: %v", err)
	}
	if tier2 != "gold" {
		t.Fatalf("historical tier changed to %q after upgrade, want gold (immutable)", tier2)
	}
	if !discAmt2.Equal(decimal.NewFromInt(600)) {
		t.Fatalf("historical discount changed to %s after upgrade, want 600 (immutable)", discAmt2)
	}
}
