package access

import (
	"context"
	"errors"
	"testing"

	"sub2api-cn-relay-manager/internal/host/sub2api"
)

func TestSuspectsOpenAIResponsesCapabilityMismatch(t *testing.T) {
	t.Parallel()

	tests := []struct {
		name  string
		probe sub2api.ProbeResult
		want  bool
	}{
		{
			name:  "ok result is not suspect",
			probe: sub2api.ProbeResult{OK: true, Message: "API returned 403: Forbidden"},
			want:  false,
		},
		{
			name:  "blank message is not suspect",
			probe: sub2api.ProbeResult{OK: false},
			want:  false,
		},
		{
			name:  "403 forbidden is suspect",
			probe: sub2api.ProbeResult{OK: false, Message: " API returned 403: Forbidden "},
			want:  true,
		},
		{
			name:  "responses advisory chinese is suspect",
			probe: sub2api.ProbeResult{OK: false, Message: "账号本身可正常使用，但当前测试接口仅支持 Responses API 路径。请直接通过实际 API 调用验证。"},
			want:  true,
		},
		{
			name:  "responses advisory english is suspect",
			probe: sub2api.ProbeResult{OK: false, Message: "Responses API endpoint exists, please directly verify with actual API calls."},
			want:  true,
		},
		{
			name:  "unrelated failure is not suspect",
			probe: sub2api.ProbeResult{OK: false, Message: "API returned 401: invalid token"},
			want:  false,
		},
	}

	for _, tc := range tests {
		tc := tc
		t.Run(tc.name, func(t *testing.T) {
			t.Parallel()

			if got := SuspectsOpenAIResponsesCapabilityMismatch(tc.probe); got != tc.want {
				t.Fatalf("SuspectsOpenAIResponsesCapabilityMismatch(%+v) = %v, want %v", tc.probe, got, tc.want)
			}
		})
	}
}

func TestShouldAttemptOpenAIResponsesCapabilityRepair(t *testing.T) {
	t.Parallel()

	tests := []struct {
		name       string
		suspect    bool
		completion sub2api.GatewayCompletionResult
		want       bool
	}{
		{
			name:       "suspect 502 temporarily unavailable",
			suspect:    true,
			completion: sub2api.GatewayCompletionResult{StatusCode: 502, BodyPreview: `{"error":{"message":"Upstream service temporarily unavailable"}}`},
			want:       true,
		},
		{
			name:       "suspect 503 no available accounts",
			suspect:    true,
			completion: sub2api.GatewayCompletionResult{StatusCode: 503, BodyPreview: `{"error":{"message":"No available accounts"}}`},
			want:       true,
		},
		{
			name:       "non suspect does not repair",
			suspect:    false,
			completion: sub2api.GatewayCompletionResult{StatusCode: 502, BodyPreview: `{"error":{"message":"Upstream service temporarily unavailable"}}`},
			want:       false,
		},
		{
			name:       "successful completion does not repair",
			suspect:    true,
			completion: sub2api.GatewayCompletionResult{OK: true, StatusCode: 200},
			want:       false,
		},
		{
			name:       "wrong status does not repair",
			suspect:    true,
			completion: sub2api.GatewayCompletionResult{StatusCode: 500, BodyPreview: `{"error":{"message":"Upstream service temporarily unavailable"}}`},
			want:       false,
		},
		{
			name:       "wrong body does not repair",
			suspect:    true,
			completion: sub2api.GatewayCompletionResult{StatusCode: 502, BodyPreview: `{"error":{"message":"bad gateway"}}`},
			want:       false,
		},
	}

	for _, tc := range tests {
		tc := tc
		t.Run(tc.name, func(t *testing.T) {
			t.Parallel()

			if got := ShouldAttemptOpenAIResponsesCapabilityRepair(tc.suspect, tc.completion); got != tc.want {
				t.Fatalf("ShouldAttemptOpenAIResponsesCapabilityRepair(%v, %+v) = %v, want %v", tc.suspect, tc.completion, got, tc.want)
			}
		})
	}
}

func TestNormalizedAccountIDs(t *testing.T) {
	t.Parallel()

	got := normalizedAccountIDs([]string{" account-1 ", "", "account-2", "account-1", "   "})
	want := []string{"account-1", "account-2"}
	if len(got) != len(want) {
		t.Fatalf("normalizedAccountIDs() len = %d, want %d; values = %v", len(got), len(want), got)
	}
	for i := range want {
		if got[i] != want[i] {
			t.Fatalf("normalizedAccountIDs()[%d] = %q, want %q; values = %v", i, got[i], want[i], got)
		}
	}
}

func TestMaybeRepairOpenAIResponsesCapabilitySkipsWithoutAccountIDs(t *testing.T) {
	t.Parallel()

	service := NewService(&fakeRepairHost{})
	original := sub2api.GatewayCompletionResult{
		StatusCode:  502,
		BodyPreview: `{"error":{"message":"No available accounts"}}`,
	}
	got, err := service.maybeRepairOpenAIResponsesCapability(context.Background(), ClosureRequest{
		AccountIDs:                 []string{" ", ""},
		ResponsesCapabilitySuspect: true,
	}, sub2api.GatewayCompletionCheckRequest{APIKey: "user-key", Model: "kimi-k2.6"}, original)
	if err != nil {
		t.Fatalf("maybeRepairOpenAIResponsesCapability() error = %v", err)
	}
	if got != original {
		t.Fatalf("maybeRepairOpenAIResponsesCapability() = %+v, want original %+v", got, original)
	}
}

func TestMaybeRepairOpenAIResponsesCapabilitySwallowsDisableError(t *testing.T) {
	t.Parallel()

	host := &fakeRepairHost{disableErr: errors.New("update failed")}
	service := NewService(host)
	original := sub2api.GatewayCompletionResult{
		StatusCode:  502,
		BodyPreview: `{"error":{"message":"Upstream service temporarily unavailable"}}`,
	}
	got, err := service.maybeRepairOpenAIResponsesCapability(context.Background(), ClosureRequest{
		AccountIDs:                 []string{"account-1"},
		ResponsesCapabilitySuspect: true,
	}, sub2api.GatewayCompletionCheckRequest{APIKey: "user-key", Model: "kimi-k2.6"}, original)
	if err != nil {
		t.Fatalf("maybeRepairOpenAIResponsesCapability() error = %v", err)
	}
	if got != original {
		t.Fatalf("maybeRepairOpenAIResponsesCapability() = %+v, want original %+v", got, original)
	}
	if host.disableCalls != 1 {
		t.Fatalf("disableCalls = %d, want 1", host.disableCalls)
	}
	if host.completionCalls != 0 {
		t.Fatalf("completionCalls = %d, want 0", host.completionCalls)
	}
}

func TestMaybeRepairOpenAIResponsesCapabilityClearsTempUnschedulableBeforeRetry(t *testing.T) {
	t.Parallel()

	host := &fakeRepairHost{}
	service := NewService(host)
	got, err := service.maybeRepairOpenAIResponsesCapability(context.Background(), ClosureRequest{
		AccountIDs:                 []string{"account-1", "account-1"},
		ResponsesCapabilitySuspect: true,
	}, sub2api.GatewayCompletionCheckRequest{APIKey: "user-key", Model: "kimi-k2.6"}, sub2api.GatewayCompletionResult{
		StatusCode:  503,
		BodyPreview: `{"error":{"message":"No available accounts"}}`,
	})
	if err != nil {
		t.Fatalf("maybeRepairOpenAIResponsesCapability() error = %v", err)
	}
	if !got.OK || got.StatusCode != 200 {
		t.Fatalf("maybeRepairOpenAIResponsesCapability() = %+v, want repaired completion success", got)
	}
	if host.disableCalls != 1 {
		t.Fatalf("disableCalls = %d, want 1", host.disableCalls)
	}
	if host.clearTempUnschedulableCalls != 1 {
		t.Fatalf("clearTempUnschedulableCalls = %d, want 1", host.clearTempUnschedulableCalls)
	}
	if len(host.clearedTempUnschedulableAccountIDs) != 1 || host.clearedTempUnschedulableAccountIDs[0] != "account-1" {
		t.Fatalf("clearedTempUnschedulableAccountIDs = %v, want [account-1]", host.clearedTempUnschedulableAccountIDs)
	}
	if host.completionCalls != 1 {
		t.Fatalf("completionCalls = %d, want 1", host.completionCalls)
	}
}

type fakeRepairHost struct {
	disableErr                         error
	clearTempUnschedulableErr          error
	disableCalls                       int
	clearTempUnschedulableCalls        int
	completionCalls                    int
	clearedTempUnschedulableAccountIDs []string
}

func (f *fakeRepairHost) EnsureSubscriptionAccess(_ context.Context, _ sub2api.EnsureSubscriptionAccessRequest) (sub2api.SubscriptionAccessRef, error) {
	return sub2api.SubscriptionAccessRef{}, nil
}

func (f *fakeRepairHost) AssignSubscription(_ context.Context, _ sub2api.AssignSubscriptionRequest) (sub2api.SubscriptionRef, error) {
	return sub2api.SubscriptionRef{}, nil
}

func (f *fakeRepairHost) CheckGatewayAccess(_ context.Context, _ sub2api.GatewayAccessCheckRequest) (sub2api.GatewayAccessResult, error) {
	return sub2api.GatewayAccessResult{}, nil
}

func (f *fakeRepairHost) CheckGatewayCompletion(_ context.Context, _ sub2api.GatewayCompletionCheckRequest) (sub2api.GatewayCompletionResult, error) {
	f.completionCalls++
	return sub2api.GatewayCompletionResult{OK: true, StatusCode: 200}, nil
}

func (f *fakeRepairHost) DisableOpenAIResponsesAPI(_ context.Context, _ []string) error {
	f.disableCalls++
	return f.disableErr
}

func (f *fakeRepairHost) ClearTempUnschedulable(_ context.Context, accountIDs []string) error {
	f.clearTempUnschedulableCalls++
	f.clearedTempUnschedulableAccountIDs = append([]string(nil), accountIDs...)
	return f.clearTempUnschedulableErr
}
