package oci

import (
	"context"
	"testing"
)

// countingClient 只实现被测方法并计数;其余方法经嵌入 nil 接口(不被调用)。
type countingClient struct {
	Client
	instCalls int
	adCalls   int
	launches  int
}

func (f *countingClient) ListInstances(ctx context.Context, cred Credentials, region string) ([]Instance, error) {
	f.instCalls++
	return []Instance{{ID: "i-1", DisplayName: "b"}, {ID: "i-2", DisplayName: "a"}}, nil
}

func (f *countingClient) ListAvailabilityDomains(ctx context.Context, cred Credentials, region string) ([]string, error) {
	f.adCalls++
	return []string{"ad-1"}, nil
}

func (f *countingClient) LaunchInstance(ctx context.Context, cred Credentials, in CreateInstanceInput) (Instance, error) {
	f.launches++
	return Instance{ID: "i-new"}, nil
}

func testCred(tenancy string) Credentials { return Credentials{TenancyOCID: tenancy} }

func TestCachedClientHitAndIsolation(t *testing.T) {
	inner := &countingClient{}
	c := NewCachedClient(inner)
	ctx := context.Background()

	for i := 0; i < 3; i++ {
		if _, err := c.ListInstances(ctx, testCred("t1"), "r1"); err != nil {
			t.Fatalf("ListInstances: %v", err)
		}
	}
	if inner.instCalls != 1 {
		t.Errorf("同 key 三连读回源 %d 次, want 1", inner.instCalls)
	}
	// 不同租户 / 区域各自回源
	_, _ = c.ListInstances(ctx, testCred("t2"), "r1")
	_, _ = c.ListInstances(ctx, testCred("t1"), "r2")
	if inner.instCalls != 3 {
		t.Errorf("跨租户/区域回源 %d 次, want 3", inner.instCalls)
	}
}

func TestCachedClientWriteBusts(t *testing.T) {
	inner := &countingClient{}
	c := NewCachedClient(inner)
	ctx := context.Background()

	_, _ = c.ListInstances(ctx, testCred("t1"), "r1")
	_, _ = c.ListAvailabilityDomains(ctx, testCred("t1"), "r1")
	_, _ = c.ListInstances(ctx, testCred("t2"), "r1")
	if _, err := c.LaunchInstance(ctx, testCred("t1"), CreateInstanceInput{}); err != nil {
		t.Fatalf("LaunchInstance: %v", err)
	}
	_, _ = c.ListInstances(ctx, testCred("t1"), "r1")
	_, _ = c.ListAvailabilityDomains(ctx, testCred("t1"), "r1")
	if inner.instCalls != 3 || inner.adCalls != 2 {
		t.Errorf("写后同租户应全部回源: inst=%d want 3, ad=%d want 2", inner.instCalls, inner.adCalls)
	}
	// 其他租户缓存不受影响
	_, _ = c.ListInstances(ctx, testCred("t2"), "r1")
	if inner.instCalls != 3 {
		t.Errorf("t2 缓存被误失效: inst=%d want 3", inner.instCalls)
	}
}

func TestCachedClientReturnsClone(t *testing.T) {
	inner := &countingClient{}
	c := NewCachedClient(inner)
	ctx := context.Background()

	first, _ := c.ListInstances(ctx, testCred("t1"), "r1")
	first[0], first[1] = first[1], first[0] // 调用方就地重排
	second, _ := c.ListInstances(ctx, testCred("t1"), "r1")
	if second[0].ID != "i-1" {
		t.Errorf("缓存底层数组被调用方污染: second[0]=%s, want i-1", second[0].ID)
	}
}

func TestCachedClientInvalidateTenancy(t *testing.T) {
	inner := &countingClient{}
	c := NewCachedClient(inner)
	ctx := context.Background()

	_, _ = c.ListInstances(ctx, testCred("t1"), "r1")
	_, _ = c.ListInstances(ctx, testCred("t2"), "r1")
	c.InvalidateTenancy("t1")
	_, _ = c.ListInstances(ctx, testCred("t1"), "r1")
	_, _ = c.ListInstances(ctx, testCred("t2"), "r1")
	if inner.instCalls != 3 {
		t.Fatalf("invalidate tenancy calls = %d, want 3", inner.instCalls)
	}
}
