package economy

import (
	"context"
	"testing"
	"time"
)

// makeUser یک کاربرِ آزمایشی با شماره‌ی دلخواه می‌سازد.
func makeUser(t *testing.T, s *Service, mobile string) int64 {
	t.Helper()
	res, err := s.db.Exec(`INSERT INTO users (mobile) VALUES (?)`, mobile)
	if err != nil {
		t.Fatal(err)
	}
	id, _ := res.LastInsertId()
	return id
}

func TestTournamentJoinPointsSettle(t *testing.T) {
	ctx := context.Background()
	svc, u1 := setup(t)
	u2 := makeUser(t, svc, "09120000002")
	setCoins(t, svc, u1, 1000)
	setCoins(t, svc, u2, 1000)

	// تورنومنتی که همین حالا فعال است.
	id, err := svc.CreateTournament(ctx, Tournament{
		Title:    "شبانه",
		EntryFee: 200,
		Prizes:   []int64{500, 200}, // رتبه۱=۵۰۰، رتبه۲=۲۰۰
		StartsAt: nowUTC().Add(-time.Minute),
		EndsAt:   nowUTC().Add(time.Hour),
	})
	if err != nil {
		t.Fatalf("create: %v", err)
	}

	// هر دو ثبت‌نام می‌کنند ⇒ ۲۰۰ سکه کسر می‌شود.
	if err := svc.JoinTournament(ctx, u1, id); err != nil {
		t.Fatalf("join u1: %v", err)
	}
	if err := svc.JoinTournament(ctx, u2, id); err != nil {
		t.Fatalf("join u2: %v", err)
	}
	if c := coinsOf(t, svc, u1); c != 800 {
		t.Errorf("entry fee not charged: coins=%d", c)
	}
	// ثبت‌نامِ دوباره باید خطا دهد.
	if err := svc.JoinTournament(ctx, u1, id); err != ErrAlreadyJoined {
		t.Errorf("expected ErrAlreadyJoined, got %v", err)
	}

	// u1 دو برد، u2 یک برد ⇒ u1 رتبه‌ی اول.
	svc.AddTournamentPoints(ctx, u1, true)
	svc.AddTournamentPoints(ctx, u1, true)
	svc.AddTournamentPoints(ctx, u2, true)
	svc.AddTournamentPoints(ctx, u2, false)

	st, err := svc.TournamentStandings(ctx, id, 10)
	if err != nil {
		t.Fatalf("standings: %v", err)
	}
	if len(st) != 2 || st[0].UserID != u1 || st[0].Points != 200 {
		t.Fatalf("bad standings: %+v", st)
	}
	if st[1].UserID != u2 || st[1].Points != 125 {
		t.Errorf("u2 points wrong: %+v", st[1])
	}

	// پایانِ تورنومنت را به گذشته می‌بریم؛ تسویه جوایز را پرداخت می‌کند.
	if _, err := svc.db.Exec(`UPDATE tournaments SET ends_at = ? WHERE id = ?`,
		nowUTC().Add(-time.Minute).Format(tsLayout), id); err != nil {
		t.Fatal(err)
	}
	svc.SettleTournaments(ctx)

	if c := coinsOf(t, svc, u1); c != 800+500 {
		t.Errorf("u1 prize not paid: coins=%d (want %d)", c, 800+500)
	}
	if c := coinsOf(t, svc, u2); c != 800+200 {
		t.Errorf("u2 prize not paid: coins=%d (want %d)", c, 800+200)
	}

	// تسویه‌ی دوباره نباید دوباره پرداخت کند.
	svc.SettleTournaments(ctx)
	if c := coinsOf(t, svc, u1); c != 1300 {
		t.Errorf("double settle paid again: coins=%d", c)
	}
}

func TestListTournamentsWithRows(t *testing.T) {
	// رگرسیون: با MaxOpenConns(1)، ListTournaments نباید حین باز بودنِ rows
	// زیرکوئری بزند (self-deadlock). این تست با تورنومنتِ واقعی آن مسیر را می‌پیماید.
	ctx := context.Background()
	svc, u1 := setup(t)
	setCoins(t, svc, u1, 1000)
	id, err := svc.CreateTournament(ctx, Tournament{
		Title: "لیست", EntryFee: 100, Prizes: []int64{500},
		StartsAt: nowUTC().Add(-time.Minute), EndsAt: nowUTC().Add(time.Hour),
	})
	if err != nil {
		t.Fatal(err)
	}
	if err := svc.JoinTournament(ctx, u1, id); err != nil {
		t.Fatal(err)
	}
	svc.AddTournamentPoints(ctx, u1, true)

	done := make(chan []TournamentView, 1)
	go func() {
		v, e := svc.ListTournaments(ctx, u1)
		if e != nil {
			t.Errorf("list: %v", e)
		}
		done <- v
	}()
	select {
	case v := <-done:
		if len(v) != 1 || !v[0].Joined || v[0].MyPoints != 100 ||
			v[0].Players != 1 || v[0].MyRank != 1 {
			t.Errorf("bad view: %+v", v)
		}
	case <-time.After(5 * time.Second):
		t.Fatal("ListTournaments deadlocked (nested query on single conn)")
	}
}

func TestTournamentJoinInsufficient(t *testing.T) {
	ctx := context.Background()
	svc, u1 := setup(t)
	setCoins(t, svc, u1, 50)
	id, _ := svc.CreateTournament(ctx, Tournament{
		Title: "گران", EntryFee: 500, Prizes: []int64{100},
		StartsAt: nowUTC().Add(-time.Minute), EndsAt: nowUTC().Add(time.Hour),
	})
	if err := svc.JoinTournament(ctx, u1, id); err != ErrInsufficient {
		t.Errorf("expected ErrInsufficient, got %v", err)
	}
}
