package provision

import (
	"context"
	"database/sql"
	"encoding/json"
	"fmt"
	"strings"
	"testing"

	_ "modernc.org/sqlite"
	"sub2api-cn-relay-manager/internal/host/sub2api"
	"sub2api-cn-relay-manager/internal/pack"
	"sub2api-cn-relay-manager/internal/store/sqlite"
	"sub2api-cn-relay-manager/internal/testutil"
)

func TestRuntimeImportServicePersistsOperationalState(t *testing.T) {
	store := openProvisionTestStore(t)
	defer closeProvisionTestStore(t, store)

	seedProvisionHost(t, store, "host-1", "https://sub2api.example.com")

	host := &fakeHostAdapter{
		batchAccounts: []sub2api.AccountRef{{ID: "account_1"}, {ID: "account_2"}},
		testResults: map[string]sub2api.ProbeResult{
			"account_1": {OK: true, Status: "passed"},
			"account_2": {OK: true, Status: "passed"},
		},
		models: map[string][]sub2api.AccountModel{
			"account_1": {{ID: "deepseek-chat"}},
			"account_2": {{ID: "deepseek-chat"}},
		},
		gatewayResult: sub2api.GatewayAccessResult{OK: true, StatusCode: 200, HasExpectedModel: true, Models: []string{"deepseek-chat"}},
	}

	svc := NewRuntimeImportService(store, host)
	result, err := svc.Import(context.Background(), RuntimeImportRequest{
		HostID:      "host-1",
		HostBaseURL: "https://sub2api.example.com",
		Pack: pack.LoadedPack{
			Manifest: pack.Manifest{PackID: "openai-cn-pack", Version: "1.0.0", TargetHost: "sub2api", MinHostVersion: "0.1.126", MaxHostVersion: "0.2.x"},
			Checksum: "checksum-1",
		},
		Provider: sampleProviderManifest(),
		Mode:     ImportModePartial,
		Keys:     []string{" key-1 ", "key-2", "key-1"},
		Access: AccessRequest{
			Mode:        AccessModeSelfService,
			ProbeAPIKey: "user-key",
		},
	})
	if err != nil {
		t.Fatalf("RuntimeImportService.Import() error = %v", err)
	}
	if result.BatchID <= 0 {
		t.Fatalf("BatchID = %d, want positive id", result.BatchID)
	}
	if result.Report.BatchStatus != BatchStatusSucceeded {
		t.Fatalf("BatchStatus = %q, want %q", result.Report.BatchStatus, BatchStatusSucceeded)
	}
	if len(result.Report.HostOverlays) != 0 {
		t.Fatalf("HostOverlays = %+v, want none for sample provider", result.Report.HostOverlays)
	}

	if got := queryCount(t, store.SQLDB(), "hosts"); got != 1 {
		t.Fatalf("hosts row count = %d, want 1", got)
	}
	if got := queryCount(t, store.SQLDB(), "packs"); got != 1 {
		t.Fatalf("packs row count = %d, want 1", got)
	}
	if got := queryCount(t, store.SQLDB(), "providers"); got != 1 {
		t.Fatalf("providers row count = %d, want 1", got)
	}
	if got := queryCount(t, store.SQLDB(), "import_batches"); got != 1 {
		t.Fatalf("import_batches row count = %d, want 1", got)
	}
	if got := queryCount(t, store.SQLDB(), "import_batch_items"); got != 2 {
		t.Fatalf("import_batch_items row count = %d, want 2", got)
	}
	if got := queryCount(t, store.SQLDB(), "managed_resources"); got != 4 {
		t.Fatalf("managed_resources row count = %d, want 4", got)
	}
	if got := queryCount(t, store.SQLDB(), "probe_results"); got != 2 {
		t.Fatalf("probe_results row count = %d, want 2", got)
	}
	if got := queryCount(t, store.SQLDB(), "access_closure_records"); got != 1 {
		t.Fatalf("access_closure_records row count = %d, want 1", got)
	}
	if got := queryCount(t, store.SQLDB(), "provider_accounts"); got != 2 {
		t.Fatalf("provider_accounts row count = %d, want 2", got)
	}

	var batchStatus string
	var accessStatus string
	if err := store.SQLDB().QueryRowContext(context.Background(), "SELECT batch_status, access_status FROM import_batches WHERE id = ?", result.BatchID).Scan(&batchStatus, &accessStatus); err != nil {
		t.Fatalf("query import batch state: %v", err)
	}
	if batchStatus != BatchStatusSucceeded {
		t.Fatalf("persisted batch_status = %q, want %q", batchStatus, BatchStatusSucceeded)
	}
	if accessStatus != AccessStatusSelfServiceReady {
		t.Fatalf("persisted access_status = %q, want %q", accessStatus, AccessStatusSelfServiceReady)
	}

	var fingerprint string
	var accountStatus string
	if err := store.SQLDB().QueryRowContext(context.Background(), "SELECT key_fingerprint, account_status FROM import_batch_items ORDER BY id LIMIT 1").Scan(&fingerprint, &accountStatus); err != nil {
		t.Fatalf("query import batch item: %v", err)
	}
	if fingerprint == "key-1" || fingerprint == "key-2" || len(fingerprint) < 10 {
		t.Fatalf("key_fingerprint = %q, want hashed fingerprint instead of raw key", fingerprint)
	}
	if accountStatus != "passed" {
		t.Fatalf("account_status = %q, want passed", accountStatus)
	}

	var inventoryStatus string
	var inventoryShadowGroup string
	if err := store.SQLDB().QueryRowContext(context.Background(), "SELECT account_status, shadow_group_id FROM provider_accounts WHERE host_account_id = ? ORDER BY id LIMIT 1", "account_1").Scan(&inventoryStatus, &inventoryShadowGroup); err != nil {
		t.Fatalf("query provider account inventory: %v", err)
	}
	if inventoryStatus != sqlite.ProviderAccountStatusActive {
		t.Fatalf("provider_accounts.account_status = %q, want %q", inventoryStatus, sqlite.ProviderAccountStatusActive)
	}
	if inventoryShadowGroup == "" {
		t.Fatal("provider_accounts.shadow_group_id = empty, want group id")
	}
}

func TestRuntimeImportServiceIncludesMatchingHostOverlaysInReport(t *testing.T) {
	store := openProvisionTestStore(t)
	defer closeProvisionTestStore(t, store)

	seedProvisionHost(t, store, "host-1", "https://sub2api.example.com")

	host := &fakeHostAdapter{
		hostVersion:   "0.1.129",
		batchAccounts: []sub2api.AccountRef{{ID: "account_1"}},
		testResults:   map[string]sub2api.ProbeResult{"account_1": {OK: true, Status: "passed"}},
		models:        map[string][]sub2api.AccountModel{"account_1": {{ID: "kimi-k2.6"}}},
		gatewayResult: sub2api.GatewayAccessResult{OK: true, StatusCode: 200, HasExpectedModel: true, Models: []string{"kimi-k2.6"}, CompletionOK: true, CompletionStatus: 200},
	}

	provider := sampleProviderManifest()
	provider.ProviderID = "kimi-a7m"
	provider.DefaultModels = []string{"kimi-k2.6"}
	provider.SmokeTestModel = "kimi-k2.6"
	provider.ChannelTemplate.ModelMapping = map[string]string{"kimi-k2.6": "kimi-k2.6"}
	provider.HostOverlays = []pack.HostOverlay{{
		OverlayID:      "sub2api-stock-v0129-kimi-a7m",
		DisplayName:    "sub2api stock v0.1.129 Kimi A7M overlay",
		TargetHost:     "sub2api",
		MinHostVersion: "0.1.129",
		MaxHostVersion: "0.1.129",
		Reason:         "stock host still routes chat traffic into unsupported Responses path",
	}}

	result, err := NewRuntimeImportService(store, host).Import(context.Background(), RuntimeImportRequest{
		HostID:      "host-1",
		HostBaseURL: "https://sub2api.example.com",
		Pack: pack.LoadedPack{
			Manifest: pack.Manifest{PackID: "openai-cn-pack", Version: "1.0.0", TargetHost: "sub2api", MinHostVersion: "0.1.126", MaxHostVersion: "0.2.x"},
			Checksum: "checksum-1",
		},
		Provider: provider,
		Mode:     ImportModePartial,
		Keys:     []string{"key-1"},
		Access: AccessRequest{
			Mode:          AccessModeSubscription,
			Subscriptions: []SubscriptionTarget{{UserID: "crm-user", DurationDays: 30}},
		},
	})
	if err != nil {
		t.Fatalf("RuntimeImportService.Import() error = %v", err)
	}
	if len(result.Report.HostOverlays) != 1 || result.Report.HostOverlays[0].OverlayID != "sub2api-stock-v0129-kimi-a7m" {
		t.Fatalf("HostOverlays = %+v, want matching overlay", result.Report.HostOverlays)
	}
}

func TestRuntimeImportServicePersistsFailedBatchAfterStrictRollback(t *testing.T) {
	store := openProvisionTestStore(t)
	defer closeProvisionTestStore(t, store)

	seedProvisionHost(t, store, "host-1", "https://sub2api.example.com")

	host := &fakeHostAdapter{
		batchAccounts: []sub2api.AccountRef{{ID: "account_1"}, {ID: "account_2"}},
		testResults: map[string]sub2api.ProbeResult{
			"account_1": {OK: true, Status: "passed"},
			"account_2": {OK: false, Status: "failed", Message: "bad key"},
		},
		models: map[string][]sub2api.AccountModel{
			"account_1": {{ID: "deepseek-chat"}},
			"account_2": {{ID: "deepseek-chat"}},
		},
	}

	svc := NewRuntimeImportService(store, host)
	result, err := svc.Import(context.Background(), RuntimeImportRequest{
		HostID:      "host-1",
		HostBaseURL: "https://sub2api.example.com",
		Pack: pack.LoadedPack{
			Manifest: pack.Manifest{PackID: "openai-cn-pack", Version: "1.0.0", TargetHost: "sub2api", MinHostVersion: "0.1.126", MaxHostVersion: "0.2.x"},
			Checksum: "checksum-1",
		},
		Provider: sampleProviderManifest(),
		Mode:     ImportModeStrict,
		Keys:     []string{"key-1", "key-2"},
		Access: AccessRequest{
			Mode:        AccessModeSelfService,
			ProbeAPIKey: "user-key",
		},
	})
	if err == nil {
		t.Fatal("RuntimeImportService.Import() error = nil, want strict failure")
	}
	if result.BatchID <= 0 {
		t.Fatalf("BatchID = %d, want positive id", result.BatchID)
	}

	var batchStatus string
	var accessStatus string
	if err := store.SQLDB().QueryRowContext(context.Background(), "SELECT batch_status, access_status FROM import_batches WHERE id = ?", result.BatchID).Scan(&batchStatus, &accessStatus); err != nil {
		t.Fatalf("query failed import batch state: %v", err)
	}
	if batchStatus != BatchStatusFailed {
		t.Fatalf("persisted batch_status = %q, want %q", batchStatus, BatchStatusFailed)
	}
	if accessStatus != AccessStatusBroken {
		t.Fatalf("persisted access_status = %q, want %q", accessStatus, AccessStatusBroken)
	}
	if got := queryCount(t, store.SQLDB(), "managed_resources"); got != 0 {
		t.Fatalf("managed_resources row count = %d, want 0 after strict rollback", got)
	}
	if got := queryCount(t, store.SQLDB(), "probe_results"); got != 2 {
		t.Fatalf("probe_results row count = %d, want 2", got)
	}
	if got := queryCount(t, store.SQLDB(), "access_closure_records"); got != 1 {
		t.Fatalf("access_closure_records row count = %d, want 1", got)
	}
}

func TestRuntimeImportServicePersistsWarningAccountStatusForAdvisoryProbeFailure(t *testing.T) {
	store := openProvisionTestStore(t)
	defer closeProvisionTestStore(t, store)

	seedProvisionHost(t, store, "host-1", "https://sub2api.example.com")

	host := &fakeHostAdapter{
		batchAccounts: []sub2api.AccountRef{{ID: "account_1"}},
		testResults: map[string]sub2api.ProbeResult{
			"account_1": {
				OK:      false,
				Status:  "failed",
				Message: "账号本身可正常使用，但当前测试接口仅支持 Responses API 路径。请直接通过实际 API 调用验证。",
			},
		},
		models: map[string][]sub2api.AccountModel{
			"account_1": {{ID: "deepseek-chat"}},
		},
		gatewayResult: sub2api.GatewayAccessResult{
			OK:               true,
			StatusCode:       200,
			HasExpectedModel: true,
			Models:           []string{"deepseek-chat"},
			CompletionOK:     true,
			CompletionStatus: 200,
		},
	}

	svc := NewRuntimeImportService(store, host)
	result, err := svc.Import(context.Background(), RuntimeImportRequest{
		HostID:      "host-1",
		HostBaseURL: "https://sub2api.example.com",
		Pack: pack.LoadedPack{
			Manifest: pack.Manifest{PackID: "openai-cn-pack", Version: "1.0.0", TargetHost: "sub2api", MinHostVersion: "0.1.126", MaxHostVersion: "0.2.x"},
			Checksum: "checksum-1",
		},
		Provider: sampleProviderManifest(),
		Mode:     ImportModePartial,
		Keys:     []string{"key-1"},
		Access: AccessRequest{
			Mode:        AccessModeSelfService,
			ProbeAPIKey: "user-key",
		},
	})
	if err != nil {
		t.Fatalf("RuntimeImportService.Import() error = %v", err)
	}
	if result.Report.BatchStatus != BatchStatusSucceeded {
		t.Fatalf("BatchStatus = %q, want %q", result.Report.BatchStatus, BatchStatusSucceeded)
	}

	var accountStatus string
	var summary string
	if err := store.SQLDB().QueryRowContext(context.Background(), "SELECT account_status, probe_summary_json FROM import_batch_items WHERE batch_id = ? ORDER BY id LIMIT 1", result.BatchID).Scan(&accountStatus, &summary); err != nil {
		t.Fatalf("query import batch item: %v", err)
	}
	if accountStatus != AccountStatusWarning {
		t.Fatalf("account_status = %q, want %q", accountStatus, AccountStatusWarning)
	}
	if !strings.Contains(summary, "\"probe_advisory\":true") {
		t.Fatalf("probe_summary_json = %s, want probe_advisory=true", summary)
	}
}

func TestRuntimeImportServicePersistsWarningAccountStatusForForbiddenProbeRace(t *testing.T) {
	store := openProvisionTestStore(t)
	defer closeProvisionTestStore(t, store)

	seedProvisionHost(t, store, "host-1", "https://sub2api.example.com")

	host := &fakeHostAdapter{
		batchAccounts: []sub2api.AccountRef{{ID: "account_1"}},
		testResults: map[string]sub2api.ProbeResult{
			"account_1": {
				OK:      false,
				Status:  "failed",
				Message: "API returned 403: Forbidden",
			},
		},
		models: map[string][]sub2api.AccountModel{
			"account_1": {{ID: "deepseek-chat"}},
		},
		gatewayResult: sub2api.GatewayAccessResult{
			OK:               true,
			StatusCode:       200,
			HasExpectedModel: true,
			Models:           []string{"deepseek-chat"},
			CompletionOK:     true,
			CompletionStatus: 200,
		},
	}

	svc := NewRuntimeImportService(store, host)
	result, err := svc.Import(context.Background(), RuntimeImportRequest{
		HostID:      "host-1",
		HostBaseURL: "https://sub2api.example.com",
		Pack: pack.LoadedPack{
			Manifest: pack.Manifest{PackID: "openai-cn-pack", Version: "1.0.0", TargetHost: "sub2api", MinHostVersion: "0.1.126", MaxHostVersion: "0.2.x"},
			Checksum: "checksum-1",
		},
		Provider: sampleProviderManifest(),
		Mode:     ImportModePartial,
		Keys:     []string{"key-1"},
		Access: AccessRequest{
			Mode:        AccessModeSelfService,
			ProbeAPIKey: "user-key",
		},
	})
	if err != nil {
		t.Fatalf("RuntimeImportService.Import() error = %v", err)
	}
	if result.Report.BatchStatus != BatchStatusSucceeded {
		t.Fatalf("BatchStatus = %q, want %q", result.Report.BatchStatus, BatchStatusSucceeded)
	}

	var accountStatus string
	var summary string
	if err := store.SQLDB().QueryRowContext(context.Background(), "SELECT account_status, probe_summary_json FROM import_batch_items WHERE batch_id = ? ORDER BY id LIMIT 1", result.BatchID).Scan(&accountStatus, &summary); err != nil {
		t.Fatalf("query import batch item: %v", err)
	}
	if accountStatus != AccountStatusWarning {
		t.Fatalf("account_status = %q, want %q", accountStatus, AccountStatusWarning)
	}
	if !strings.Contains(summary, "\"probe_advisory\":true") {
		t.Fatalf("probe_summary_json = %s, want probe_advisory=true", summary)
	}
}

func TestRuntimeImportServicePersistsPartialManagedResourcesOnAccessFailure(t *testing.T) {
	store := openProvisionTestStore(t)
	defer closeProvisionTestStore(t, store)

	seedProvisionHost(t, store, "host-1", "https://sub2api.example.com")

	host := &fakeHostAdapter{
		batchAccounts: []sub2api.AccountRef{{ID: "account_1"}},
		testResults: map[string]sub2api.ProbeResult{
			"account_1": {OK: true, Status: "passed"},
		},
		models: map[string][]sub2api.AccountModel{
			"account_1": {{ID: "deepseek-chat"}},
		},
		assignErr: fmt.Errorf("group is not a subscription type"),
	}

	svc := NewRuntimeImportService(store, host)
	result, err := svc.Import(context.Background(), RuntimeImportRequest{
		HostID:      "host-1",
		HostBaseURL: "https://sub2api.example.com",
		Pack: pack.LoadedPack{
			Manifest: pack.Manifest{PackID: "openai-cn-pack", Version: "1.0.0", TargetHost: "sub2api", MinHostVersion: "0.1.126", MaxHostVersion: "0.2.x"},
			Checksum: "checksum-1",
		},
		Provider: sampleProviderManifest(),
		Mode:     ImportModePartial,
		Keys:     []string{"key-1"},
		Access: AccessRequest{
			Mode:          AccessModeSubscription,
			ProbeAPIKey:   "user-key",
			Subscriptions: []SubscriptionTarget{{UserID: "1", DurationDays: 30}},
		},
	})
	if err == nil {
		t.Fatal("RuntimeImportService.Import() error = nil, want partial failure")
	}
	if result.BatchID <= 0 {
		t.Fatalf("BatchID = %d, want positive id", result.BatchID)
	}
	if got := queryCount(t, store.SQLDB(), "managed_resources"); got != 4 {
		t.Fatalf("managed_resources row count = %d, want 4 persisted resources on partial failure", got)
	}
	var batchStatus string
	if err := store.SQLDB().QueryRowContext(context.Background(), "SELECT batch_status FROM import_batches WHERE id = ?", result.BatchID).Scan(&batchStatus); err != nil {
		t.Fatalf("query import batch status: %v", err)
	}
	if batchStatus != BatchStatusPartial {
		t.Fatalf("persisted batch_status = %q, want %q", batchStatus, BatchStatusPartial)
	}
}

func TestRuntimeImportServicePersistsSelfServiceProbeKeyInAccessClosure(t *testing.T) {
	store := openProvisionTestStore(t)
	defer closeProvisionTestStore(t, store)

	seedProvisionHost(t, store, "host-1", "https://sub2api.example.com")

	host := &fakeHostAdapter{
		batchAccounts: []sub2api.AccountRef{{ID: "account_1", Name: "deepseek-01"}},
		testResults: map[string]sub2api.ProbeResult{
			"account_1": {OK: true, Status: "passed"},
		},
		models: map[string][]sub2api.AccountModel{
			"account_1": {{ID: "deepseek-chat"}},
		},
		gatewayResult: sub2api.GatewayAccessResult{OK: true, StatusCode: 200, HasExpectedModel: true, Models: []string{"deepseek-chat"}, CompletionOK: true, CompletionStatus: 200},
	}

	result, err := NewRuntimeImportService(store, host).Import(context.Background(), RuntimeImportRequest{
		HostID:      "host-1",
		HostBaseURL: "https://sub2api.example.com",
		Pack: pack.LoadedPack{
			Manifest: pack.Manifest{PackID: "openai-cn-pack", Version: "1.0.0", TargetHost: "sub2api", MinHostVersion: "0.1.126", MaxHostVersion: "0.2.x"},
			Checksum: "checksum-1",
		},
		Provider: sampleProviderManifest(),
		Mode:     ImportModePartial,
		Keys:     []string{"key-1"},
		Access: AccessRequest{
			Mode:        AccessModeSelfService,
			ProbeAPIKey: "user-probe-key",
		},
	})
	if err != nil {
		t.Fatalf("RuntimeImportService.Import() error = %v", err)
	}

	closures, err := store.AccessClosures().GetByBatchID(context.Background(), result.BatchID)
	if err != nil {
		t.Fatalf("AccessClosures().GetByBatchID() error = %v", err)
	}
	if len(closures) != 1 {
		t.Fatalf("access closures = %d, want 1", len(closures))
	}
	var payload map[string]any
	if err := json.Unmarshal([]byte(closures[0].DetailsJSON), &payload); err != nil {
		t.Fatalf("decode access closure details: %v", err)
	}
	if got, _ := payload["probe_api_key"].(string); got != "user-probe-key" {
		t.Fatalf("probe_api_key = %q, want user-probe-key", got)
	}
	if got, _ := payload["requested_probe_api_key"].(string); got != "user-probe-key" {
		t.Fatalf("requested_probe_api_key = %q, want user-probe-key", got)
	}
	if got, _ := payload["effective_probe_key_source"].(string); got != "requested_probe_api_key" {
		t.Fatalf("effective_probe_key_source = %q, want requested_probe_api_key", got)
	}
	if got, _ := payload["effective_probe_key_fingerprint"].(string); got != fingerprintSecret("user-probe-key") {
		t.Fatalf("effective_probe_key_fingerprint = %q, want hashed user-probe-key", got)
	}
}

func TestRuntimeImportServicePersistsSubscriptionMetadataInAccessClosure(t *testing.T) {
	store := openProvisionTestStore(t)
	defer closeProvisionTestStore(t, store)

	seedProvisionHost(t, store, "host-1", "https://sub2api.example.com")

	host := &fakeHostAdapter{
		batchAccounts: []sub2api.AccountRef{{ID: "account_1", Name: "deepseek-01"}},
		testResults: map[string]sub2api.ProbeResult{
			"account_1": {OK: true, Status: "passed"},
		},
		models: map[string][]sub2api.AccountModel{
			"account_1": {{ID: "deepseek-chat"}},
		},
		gatewayResult: sub2api.GatewayAccessResult{OK: true, StatusCode: 200, HasExpectedModel: true, Models: []string{"deepseek-chat"}, CompletionOK: true, CompletionStatus: 200},
	}

	result, err := NewRuntimeImportService(store, host).Import(context.Background(), RuntimeImportRequest{
		HostID:      "host-1",
		HostBaseURL: "https://sub2api.example.com",
		Pack: pack.LoadedPack{
			Manifest: pack.Manifest{PackID: "openai-cn-pack", Version: "1.0.0", TargetHost: "sub2api", MinHostVersion: "0.1.126", MaxHostVersion: "0.2.x"},
			Checksum: "checksum-1",
		},
		Provider: sampleProviderManifest(),
		Mode:     ImportModePartial,
		Keys:     []string{"key-1"},
		Access: AccessRequest{
			Mode:          AccessModeSubscription,
			Subscriptions: []SubscriptionTarget{{UserID: "user-42", DurationDays: 30}},
		},
	})
	if err != nil {
		t.Fatalf("RuntimeImportService.Import() error = %v", err)
	}

	closures, err := store.AccessClosures().GetByBatchID(context.Background(), result.BatchID)
	if err != nil {
		t.Fatalf("AccessClosures().GetByBatchID() error = %v", err)
	}
	if len(closures) != 1 {
		t.Fatalf("access closures = %d, want 1", len(closures))
	}
	var payload map[string]any
	if err := json.Unmarshal([]byte(closures[0].DetailsJSON), &payload); err != nil {
		t.Fatalf("decode access closure details: %v", err)
	}
	users, _ := payload["subscription_users"].([]any)
	if len(users) != 1 || users[0] != "user-42" {
		t.Fatalf("subscription_users = %#v, want [user-42]", users)
	}
	if got, _ := payload["subscription_days"].(float64); int(got) != 30 {
		t.Fatalf("subscription_days = %v, want 30", got)
	}
	if _, ok := payload["probe_api_key"]; ok {
		t.Fatalf("probe_api_key should be omitted for subscription closure, got %#v", payload["probe_api_key"])
	}
	if got, _ := payload["effective_probe_key_source"].(string); got != "managed_subscription" {
		t.Fatalf("effective_probe_key_source = %q, want managed_subscription", got)
	}
	if got, _ := payload["effective_probe_key_fingerprint"].(string); got != fingerprintSecret("managed-subscription-key") {
		t.Fatalf("effective_probe_key_fingerprint = %q, want managed-subscription-key fingerprint", got)
	}
}

func TestRuntimeImportServiceRepeatedImportReusesManagedResources(t *testing.T) {
	store := openProvisionTestStore(t)
	defer closeProvisionTestStore(t, store)

	seedProvisionHost(t, store, "host-1", "https://sub2api.example.com")

	host := &fakeHostAdapter{
		batchAccounts: []sub2api.AccountRef{{ID: "account_1", Name: "key-1"}},
		testResults: map[string]sub2api.ProbeResult{
			"account_1": {OK: true, Status: "passed"},
		},
		models: map[string][]sub2api.AccountModel{
			"account_1": {{ID: "deepseek-chat"}},
		},
		gatewayResult: sub2api.GatewayAccessResult{OK: true, StatusCode: 200, HasExpectedModel: true, Models: []string{"deepseek-chat"}},
	}

	svc := NewRuntimeImportService(store, host)
	request := RuntimeImportRequest{
		HostID:      "host-1",
		HostBaseURL: "https://sub2api.example.com",
		Pack: pack.LoadedPack{
			Manifest: pack.Manifest{PackID: "openai-cn-pack", Version: "1.0.0", TargetHost: "sub2api", MinHostVersion: "0.1.126", MaxHostVersion: "0.2.x"},
			Checksum: "checksum-1",
		},
		Provider: sampleProviderManifest(),
		Mode:     ImportModePartial,
		Keys:     []string{"key-1"},
		Access: AccessRequest{
			Mode:        AccessModeSelfService,
			ProbeAPIKey: "user-key",
		},
	}

	first, err := svc.Import(context.Background(), request)
	if err != nil {
		t.Fatalf("first Import() error = %v", err)
	}
	second, err := svc.Import(context.Background(), request)
	if err != nil {
		t.Fatalf("second Import() error = %v", err)
	}
	if second.BatchID <= first.BatchID {
		t.Fatalf("second BatchID = %d, want > first BatchID %d", second.BatchID, first.BatchID)
	}
	if got := queryCount(t, store.SQLDB(), "managed_resources"); got != 3 {
		t.Fatalf("managed_resources row count = %d, want 3 after reused import", got)
	}
	if got := queryCount(t, store.SQLDB(), "import_batches"); got != 2 {
		t.Fatalf("import_batches row count = %d, want 2", got)
	}
}

func TestRuntimeImportServiceResolvesHostByBaseURL(t *testing.T) {
	store := openProvisionTestStore(t)
	defer closeProvisionTestStore(t, store)

	seedProvisionHost(t, store, "host-1", "https://sub2api.example.com")

	host := &fakeHostAdapter{
		batchAccounts: []sub2api.AccountRef{{ID: "account_1"}},
		testResults: map[string]sub2api.ProbeResult{
			"account_1": {OK: true, Status: "passed"},
		},
		models: map[string][]sub2api.AccountModel{
			"account_1": {{ID: "deepseek-chat"}},
		},
		gatewayResult: sub2api.GatewayAccessResult{
			OK:               true,
			StatusCode:       200,
			HasExpectedModel: true,
			Models:           []string{"deepseek-chat"},
			CompletionOK:     true,
			CompletionStatus: 200,
		},
	}

	result, err := NewRuntimeImportService(store, host).Import(context.Background(), RuntimeImportRequest{
		HostBaseURL: "https://sub2api.example.com",
		Pack: pack.LoadedPack{
			Manifest: pack.Manifest{PackID: "openai-cn-pack", Version: "1.0.0", TargetHost: "sub2api", MinHostVersion: "0.1.126", MaxHostVersion: "0.2.x"},
			Checksum: "checksum-1",
		},
		Provider: sampleProviderManifest(),
		Mode:     ImportModePartial,
		Keys:     []string{"key-1"},
		Access: AccessRequest{
			Mode:        AccessModeSelfService,
			ProbeAPIKey: "user-key",
		},
	})
	if err != nil {
		t.Fatalf("RuntimeImportService.Import() error = %v", err)
	}
	if result.BatchID <= 0 {
		t.Fatalf("BatchID = %d, want positive id", result.BatchID)
	}
}

func TestRuntimeImportServiceRejectsUnregisteredHostBaseURL(t *testing.T) {
	store := openProvisionTestStore(t)
	defer closeProvisionTestStore(t, store)

	_, err := NewRuntimeImportService(store, &fakeHostAdapter{}).Import(context.Background(), RuntimeImportRequest{
		HostBaseURL: "https://missing.example.com",
		Pack: pack.LoadedPack{
			Manifest: pack.Manifest{PackID: "openai-cn-pack", Version: "1.0.0", TargetHost: "sub2api", MinHostVersion: "0.1.126", MaxHostVersion: "0.2.x"},
			Checksum: "checksum-1",
		},
		Provider: sampleProviderManifest(),
		Mode:     ImportModePartial,
		Keys:     []string{"key-1"},
		Access: AccessRequest{
			Mode:        AccessModeSelfService,
			ProbeAPIKey: "user-key",
		},
	})
	if err == nil || !strings.Contains(err.Error(), `host_id is required for unregistered host_base_url "https://missing.example.com"`) {
		t.Fatalf("RuntimeImportService.Import() error = %v, want unregistered host_base_url error", err)
	}
}

func TestRuntimeImportServiceRejectsHostBaseURLMismatch(t *testing.T) {
	store := openProvisionTestStore(t)
	defer closeProvisionTestStore(t, store)

	seedProvisionHost(t, store, "host-1", "https://sub2api.example.com")

	_, err := NewRuntimeImportService(store, &fakeHostAdapter{}).Import(context.Background(), RuntimeImportRequest{
		HostID:      "host-1",
		HostBaseURL: "https://other.example.com",
		Pack: pack.LoadedPack{
			Manifest: pack.Manifest{PackID: "openai-cn-pack", Version: "1.0.0", TargetHost: "sub2api", MinHostVersion: "0.1.126", MaxHostVersion: "0.2.x"},
			Checksum: "checksum-1",
		},
		Provider: sampleProviderManifest(),
		Mode:     ImportModePartial,
		Keys:     []string{"key-1"},
		Access: AccessRequest{
			Mode:        AccessModeSelfService,
			ProbeAPIKey: "user-key",
		},
	})
	if err == nil || err.Error() != `host "host-1" base_url mismatch: registered=https://sub2api.example.com runtime=https://other.example.com` {
		t.Fatalf("RuntimeImportService.Import() error = %v, want base_url mismatch", err)
	}
}

func TestRuntimeImportServiceImportReconcilesExistingChannelConfiguration(t *testing.T) {
	store := openProvisionTestStore(t)
	defer closeProvisionTestStore(t, store)

	seedProvisionHost(t, store, "host-1", "https://sub2api.example.com")

	host := &fakeHostAdapter{
		batchAccounts: []sub2api.AccountRef{{ID: "account_1", Name: "minimax-01"}},
		testResults: map[string]sub2api.ProbeResult{
			"account_1": {OK: true, Status: "passed"},
		},
		models: map[string][]sub2api.AccountModel{
			"account_1": {{ID: "MiniMax-M2.7-highspeed"}},
		},
		gatewayResult: sub2api.GatewayAccessResult{OK: true, StatusCode: 200, HasExpectedModel: true, Models: []string{"MiniMax-M2.5-highspeed", "MiniMax-M2.7-highspeed"}},
		managedSnapshot: sub2api.ManagedResourceSnapshot{
			Groups:   []sub2api.NamedResource{{ID: "group_existing", Name: "MiniMax 默认分组-self-service"}},
			Channels: []sub2api.NamedResource{{ID: "channel_existing", Name: "MiniMax 默认渠道-self-service"}},
			Plans:    []sub2api.NamedResource{{ID: "plan_existing", Name: "MiniMax 默认套餐-self-service"}},
		},
	}

	provider := sampleProviderManifest()
	provider.ProviderID = "minimax"
	provider.DisplayName = "MiniMax OpenAI Compatible"
	provider.BaseURL = "https://v2.aicodee.com/v1"
	provider.DefaultModels = []string{"MiniMax-M2.5-highspeed", "MiniMax-M2.7-highspeed"}
	provider.SmokeTestModel = "MiniMax-M2.7-highspeed"
	provider.GroupTemplate.Name = "MiniMax 默认分组"
	provider.ChannelTemplate = pack.ChannelTemplate{
		Name:         "MiniMax 默认渠道",
		ModelMapping: map[string]string{"MiniMax-M2.5-highspeed": "MiniMax-M2.5-highspeed", "MiniMax-M2.7-highspeed": "MiniMax-M2.7-highspeed"},
	}
	provider.PlanTemplate.Name = "MiniMax 默认套餐"

	svc := NewRuntimeImportService(store, host)
	result, err := svc.Import(context.Background(), RuntimeImportRequest{
		HostID:      "host-1",
		HostBaseURL: "https://sub2api.example.com",
		Pack: pack.LoadedPack{
			Manifest: pack.Manifest{PackID: "openai-cn-pack", Version: "1.0.0", TargetHost: "sub2api", MinHostVersion: "0.1.126", MaxHostVersion: "0.2.x"},
			Checksum: "checksum-1",
		},
		Provider: provider,
		Mode:     ImportModePartial,
		Keys:     []string{"key-1"},
		Access: AccessRequest{
			Mode:        AccessModeSelfService,
			ProbeAPIKey: "user-key",
		},
	})
	if err != nil {
		t.Fatalf("RuntimeImportService.Import() error = %v", err)
	}
	if result.Report.Channel.ID != "channel_existing" {
		t.Fatalf("Channel.ID = %q, want reused channel_existing", result.Report.Channel.ID)
	}
	if host.createChannelCalls != 0 {
		t.Fatalf("CreateChannel() calls = %d, want 0 when channel already exists", host.createChannelCalls)
	}
	if host.updateChannelCalls != 1 {
		t.Fatalf("UpdateChannel() calls = %d, want 1", host.updateChannelCalls)
	}
	if host.updateChannelID != "channel_existing" {
		t.Fatalf("UpdateChannel() id = %q, want channel_existing", host.updateChannelID)
	}
	if len(host.updateChannelReq.ModelPricing) != 1 {
		t.Fatalf("UpdateChannel().ModelPricing len = %d, want 1", len(host.updateChannelReq.ModelPricing))
	}
	if got := host.updateChannelReq.ModelPricing[0].Models; len(got) != 2 || got[0] != "MiniMax-M2.5-highspeed" || got[1] != "MiniMax-M2.7-highspeed" {
		t.Fatalf("UpdateChannel().ModelPricing[0].Models = %v, want minimax default models", got)
	}
	if host.updateChannelReq.ModelPricing[0].BillingMode != "token" {
		t.Fatalf("UpdateChannel().ModelPricing[0].BillingMode = %q, want token", host.updateChannelReq.ModelPricing[0].BillingMode)
	}
}

func openProvisionTestStore(t *testing.T) *sqlite.DB {
	t.Helper()
	return testutil.OpenSQLiteStore(t, testutil.SQLiteTestDSN(t, "state.db", true))
}

func closeProvisionTestStore(t *testing.T, store *sqlite.DB) {
	t.Helper()
	testutil.CloseSQLiteStore(t, store)
}

func seedProvisionHost(t *testing.T, store *sqlite.DB, hostID, baseURL string) int64 {
	t.Helper()
	id, err := store.Hosts().Create(context.Background(), sqlite.Host{
		HostID:      hostID,
		BaseURL:     baseURL,
		HostVersion: "0.1.126",
		AuthType:    "apikey",
		AuthToken:   "test-host-token",
	})
	if err != nil {
		t.Fatalf("Hosts().Create() error = %v", err)
	}
	return id
}

func queryCount(t *testing.T, db *sql.DB, table string) int {
	t.Helper()
	var count int
	if err := db.QueryRowContext(context.Background(), "SELECT COUNT(*) FROM "+table).Scan(&count); err != nil {
		t.Fatalf("count rows for %s: %v", table, err)
	}
	return count
}
