package integration

import (
	"context"
	"errors"
	"regexp"
	"testing"

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

// These tests exercise the membership service against a real PostgreSQL. They
// verify the business gates that the DB schema + service enforce for Phase 6
// item 3 scaffolding (business-rules.md §13.1): exactly three tiers, one
// membership per customer, and an append-only tier audit. They skip when
// DATABASE_URL is unset.

func TestMembershipDuplicateRejected(t *testing.T) {
	pool := testPool(t)
	setUp(t, pool)
	ctx := context.Background()

	repo := membership.NewRepository(pool)
	svc := membership.NewService(repo, seqAdapter{pool}, wib)

	cust := seedCustomer(t, pool)
	if _, err := svc.Create(ctx, membership.CreateInput{
		CustomerID: cust, Tier: membership.TierBronze, JoinedDate: "2026-08-17",
	}, nil); err != nil {
		t.Fatalf("first create: %v", err)
	}

	if _, err := svc.Create(ctx, membership.CreateInput{
		CustomerID: cust, Tier: membership.TierBronze, JoinedDate: "2026-08-17",
	}, nil); !errors.Is(err, membership.ErrAlreadyMember) {
		t.Fatalf("expected ErrAlreadyMember for second membership, got %v", err)
	}
}

func TestMembershipSilverRejected(t *testing.T) {
	pool := testPool(t)
	setUp(t, pool)
	ctx := context.Background()

	repo := membership.NewRepository(pool)
	svc := membership.NewService(repo, seqAdapter{pool}, wib)

	cust := seedCustomer(t, pool)
	_, err := svc.Create(ctx, membership.CreateInput{
		CustomerID: cust, Tier: membership.Tier("silver"), JoinedDate: "2026-08-17",
	}, nil)
	if !errors.Is(err, membership.ErrTierInvalid) {
		t.Fatalf("expected ErrTierInvalid for silver tier, got %v", err)
	}
}

func TestMembershipTierHistoryAppend(t *testing.T) {
	pool := testPool(t)
	setUp(t, pool)
	ctx := context.Background()

	repo := membership.NewRepository(pool)
	svc := membership.NewService(repo, seqAdapter{pool}, wib)

	cust := seedCustomer(t, pool)
	created, err := svc.Create(ctx, membership.CreateInput{
		CustomerID: cust, Tier: membership.TierBronze, JoinedDate: "2026-08-17",
	}, nil)
	if err != nil {
		t.Fatalf("create: %v", err)
	}

	if _, err := svc.Update(ctx, created.ID, membership.UpdateInput{
		Tier: membership.TierGold, Reason: "promo",
	}, nil); err != nil {
		t.Fatalf("update: %v", err)
	}

	history, err := svc.TierHistory(ctx, created.ID)
	if err != nil {
		t.Fatalf("tierHistory: %v", err)
	}
	if len(history) < 2 {
		t.Fatalf("expected initial + update entries, got %d", len(history))
	}
	// ListMembershipTierHistory orders id DESC, so the newest entry is first.
	latest := history[0]
	if latest.ToTier != membership.TierGold {
		t.Fatalf("expected latest tier gold, got %s", latest.ToTier)
	}
	if latest.FromTier == nil || *latest.FromTier != membership.TierBronze {
		t.Fatalf("expected from-tier bronze, got %v", latest.FromTier)
	}
}

func TestMembershipNumberFormat(t *testing.T) {
	pool := testPool(t)
	setUp(t, pool)
	ctx := context.Background()

	repo := membership.NewRepository(pool)
	svc := membership.NewService(repo, seqAdapter{pool}, wib)

	cust := seedCustomer(t, pool)
	created, err := svc.Create(ctx, membership.CreateInput{
		CustomerID: cust, Tier: membership.TierBronze, JoinedDate: "2026-08-17",
	}, nil)
	if err != nil {
		t.Fatalf("create: %v", err)
	}
	re := regexp.MustCompile(`^MBR-\d{6}-\d{6}$`)
	if !re.MatchString(created.MemberNumber) {
		t.Fatalf("unexpected member number format: %q", created.MemberNumber)
	}
}
