package session

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

type fakeResolver struct {
	uid      string
	guest    bool
	platform string
	err      error
	calls    int
}

func (f *fakeResolver) ResolveSession(_ context.Context, _ string) (string, bool, string, error) {
	f.calls++
	if f.err != nil {
		return "", false, "", f.err
	}
	return f.uid, f.guest, f.platform, nil
}

func TestResolveCachesBackendHit(t *testing.T) {
	r := &fakeResolver{uid: "user-1"}
	c := NewCache(r, time.Minute, 10)

	for i := 0; i < 3; i++ {
		uid, _, _, err := c.Resolve(context.Background(), "tok")
		if err != nil || uid != "user-1" {
			t.Fatalf("resolve #%d = (%q, %v)", i, uid, err)
		}
	}
	if r.calls != 1 {
		t.Fatalf("backend calls = %d, want 1 (cached)", r.calls)
	}
}

func TestResolveCarriesAndCachesGuestFlagAndPlatform(t *testing.T) {
	r := &fakeResolver{uid: "guest-1", guest: true, platform: "vk/ios"}
	c := NewCache(r, time.Minute, 10)
	for i := 0; i < 2; i++ {
		uid, guest, platform, err := c.Resolve(context.Background(), "tok")
		if err != nil || uid != "guest-1" || !guest || platform != "vk/ios" {
			t.Fatalf("resolve #%d = (%q, guest=%v, platform=%q, %v)", i, uid, guest, platform, err)
		}
	}
	if r.calls != 1 {
		t.Fatalf("backend calls = %d, want 1 (guest flag + platform cached)", r.calls)
	}
}

func TestResolvePropagatesBackendError(t *testing.T) {
	r := &fakeResolver{err: errors.New("nope")}
	c := NewCache(r, time.Minute, 10)
	if _, _, _, err := c.Resolve(context.Background(), "tok"); err == nil {
		t.Fatal("expected backend error to propagate")
	}
}

func TestResolveReResolvesAfterTTL(t *testing.T) {
	r := &fakeResolver{uid: "user-1"}
	c := NewCache(r, time.Minute, 10)
	base := time.Now()
	c.now = func() time.Time { return base }

	if _, _, _, err := c.Resolve(context.Background(), "tok"); err != nil {
		t.Fatal(err)
	}
	c.now = func() time.Time { return base.Add(2 * time.Minute) } // past TTL
	if _, _, _, err := c.Resolve(context.Background(), "tok"); err != nil {
		t.Fatal(err)
	}
	if r.calls != 2 {
		t.Fatalf("backend calls = %d, want 2 (re-resolve after expiry)", r.calls)
	}
}

func TestInvalidateForcesReResolve(t *testing.T) {
	r := &fakeResolver{uid: "user-1"}
	c := NewCache(r, time.Minute, 10)
	_, _, _, _ = c.Resolve(context.Background(), "tok")
	c.Invalidate("tok")
	_, _, _, _ = c.Resolve(context.Background(), "tok")
	if r.calls != 2 {
		t.Fatalf("backend calls = %d, want 2 after invalidate", r.calls)
	}
}
