package sqlite

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

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

	ctx := context.Background()
	store := openTestDB(t)

	run := ImportRun{
		RunID:                 "run-1",
		HostID:                "host-1",
		Mode:                  "strict",
		AccessMode:            "subscription",
		SubscriptionUsersJSON: `["user-1"]`,
		SubscriptionDays:      30,
		ProbeAPIKey:           "probe-key",
		State:                 "running",
		TotalItems:            1,
	}
	if err := store.ImportRuns().Create(ctx, run); err != nil {
		t.Fatalf("ImportRuns().Create() error = %v", err)
	}

	run.State = "completed_with_warnings"
	run.CompletedItems = 1
	run.ActiveItems = 1
	run.WarningItems = 1
	run.FinishedAt = "2026-05-22T12:00:00Z"
	if err := store.ImportRuns().Update(ctx, run); err != nil {
		t.Fatalf("ImportRuns().Update() error = %v", err)
	}

	gotRun, err := store.ImportRuns().GetByRunID(ctx, "run-1")
	if err != nil {
		t.Fatalf("ImportRuns().GetByRunID() error = %v", err)
	}
	if gotRun.State != "completed_with_warnings" {
		t.Fatalf("run.State = %q, want completed_with_warnings", gotRun.State)
	}
	if gotRun.WarningItems != 1 {
		t.Fatalf("run.WarningItems = %d, want 1", gotRun.WarningItems)
	}
	if gotRun.HostID != "host-1" {
		t.Fatalf("run.HostID = %q, want host-1", gotRun.HostID)
	}
	if gotRun.SubscriptionUsersJSON != `["user-1"]` {
		t.Fatalf("run.SubscriptionUsersJSON = %q, want persisted subscription users", gotRun.SubscriptionUsersJSON)
	}
	if gotRun.SubscriptionDays != 30 {
		t.Fatalf("run.SubscriptionDays = %d, want 30", gotRun.SubscriptionDays)
	}
	if gotRun.ProbeAPIKey != "probe-key" {
		t.Fatalf("run.ProbeAPIKey = %q, want probe-key", gotRun.ProbeAPIKey)
	}

	legacyBatchID := int64(88)
	reusedAccountID := int64(321)
	channelID := int64(66)
	accountID := int64(77)

	item := ImportRunItem{
		ItemID:                "item-1",
		RunID:                 "run-1",
		BaseURL:               "https://api.deepseek.com/v1",
		ProviderID:            "api-deepseek-12345678",
		APIKeyFingerprint:     "fp_abc123",
		RequestedModelsJSON:   `["kimi 2.6"]`,
		RawModelsJSON:         `["kimi-k2.6"]`,
		NormalizedModelsJSON:  `["kimi-k2.6"]`,
		CanonicalFamiliesJSON: `["kimi-2.6"]`,
		RecommendedModelsJSON: `["kimi-k2.6"]`,
		CurrentStage:          "confirm",
		ConfirmationStatus:    "pending",
		AccessStatus:          "unknown",
		MatchedAccountState:   "deprecated",
		AccountResolution:     "reactivated",
		ProvisionReused:       true,
		ReusedFromProviderID:  "api-deepseek-87654321",
		ReusedFromAccountID:   &reusedAccountID,
		ChannelID:             &channelID,
		AccountID:             &accountID,
		RetryCount:            2,
		ConfirmationAttempts:  3,
		LastRetryAt:           "2026-05-22T12:01:00Z",
		NextRetryAt:           "2026-05-22T12:02:00Z",
		LeaseOwner:            "worker-1",
		LeaseUntil:            "2026-05-22T12:03:00Z",
		AdvisoryMessagesJSON:  `["warmup"]`,
		LastErrorStage:        "confirm",
		LastError:             "no available accounts",
		LegacyBatchID:         &legacyBatchID,
		LegacyProviderID:      "legacy-provider",
		CapabilityProfileJSON: `{"transport_profile":{"supports_openai_chat_completions":true}}`,
		ResolvedSmokeModel:    "kimi-k2.6",
	}

	if err := store.ImportRunItems().Upsert(ctx, item); err != nil {
		t.Fatalf("ImportRunItems().Upsert() error = %v", err)
	}

	gotItem, err := store.ImportRunItems().GetByItemID(ctx, "item-1")
	if err != nil {
		t.Fatalf("ImportRunItems().GetByItemID() error = %v", err)
	}
	if gotItem.APIKeyFingerprint != "fp_abc123" {
		t.Fatalf("item.APIKeyFingerprint = %q, want fp_abc123", gotItem.APIKeyFingerprint)
	}
	if gotItem.CanonicalFamiliesJSON != `["kimi-2.6"]` {
		t.Fatalf("item.CanonicalFamiliesJSON = %q, want canonical families json", gotItem.CanonicalFamiliesJSON)
	}
	if gotItem.MatchedAccountState != "deprecated" {
		t.Fatalf("item.MatchedAccountState = %q, want deprecated", gotItem.MatchedAccountState)
	}
	if gotItem.AccountResolution != "reactivated" {
		t.Fatalf("item.AccountResolution = %q, want reactivated", gotItem.AccountResolution)
	}
	if !gotItem.ProvisionReused {
		t.Fatal("item.ProvisionReused = false, want true")
	}
	if gotItem.ReusedFromProviderID != "api-deepseek-87654321" {
		t.Fatalf("item.ReusedFromProviderID = %q, want reused provider id", gotItem.ReusedFromProviderID)
	}
	if gotItem.ReusedFromAccountID == nil || *gotItem.ReusedFromAccountID != reusedAccountID {
		t.Fatalf("item.ReusedFromAccountID = %#v, want %d", gotItem.ReusedFromAccountID, reusedAccountID)
	}
	if gotItem.LeaseOwner != "worker-1" || gotItem.LeaseUntil != "2026-05-22T12:03:00Z" {
		t.Fatalf("lease = (%q, %q), want persisted lease fields", gotItem.LeaseOwner, gotItem.LeaseUntil)
	}

	event := ImportRunItemEvent{
		EventID:     "evt-1",
		RunID:       "run-1",
		ItemID:      "item-1",
		EventType:   "retry_scheduled",
		Stage:       "confirm",
		Attempt:     2,
		Message:     "retry after warmup",
		PayloadJSON: `{"next_retry_at":"2026-05-22T12:02:00Z"}`,
	}
	if err := store.ImportRunEvents().Append(ctx, event); err != nil {
		t.Fatalf("ImportRunEvents().Append() error = %v", err)
	}

	events, err := store.ImportRunEvents().ListByItemID(ctx, "item-1")
	if err != nil {
		t.Fatalf("ImportRunEvents().ListByItemID() error = %v", err)
	}
	if len(events) != 1 {
		t.Fatalf("len(events) = %d, want 1", len(events))
	}
	if events[0].EventType != "retry_scheduled" {
		t.Fatalf("events[0].EventType = %q, want retry_scheduled", events[0].EventType)
	}

	items, err := store.ImportRunItems().ListByRunID(ctx, "run-1")
	if err != nil {
		t.Fatalf("ImportRunItems().ListByRunID() error = %v", err)
	}
	if len(items) != 1 {
		t.Fatalf("len(items) = %d, want 1", len(items))
	}
	if !reflect.DeepEqual(items[0].AdvisoryMessagesJSON, `["warmup"]`) {
		t.Fatalf("items[0].AdvisoryMessagesJSON = %q, want advisory json", items[0].AdvisoryMessagesJSON)
	}
}

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

	ctx := context.Background()
	store := openTestDB(t)

	for _, run := range []ImportRun{
		{RunID: "run-a", HostID: "host-a", Mode: "strict", AccessMode: "subscription", State: "running"},
		{RunID: "run-b", HostID: "host-b", Mode: "partial", AccessMode: "self_service", State: "completed"},
	} {
		if err := store.ImportRuns().Create(ctx, run); err != nil {
			t.Fatalf("ImportRuns().Create(%q) error = %v", run.RunID, err)
		}
	}

	runs, err := store.ImportRuns().List(ctx, 1)
	if err != nil {
		t.Fatalf("ImportRuns().List(limit=1) error = %v", err)
	}
	if len(runs) != 1 {
		t.Fatalf("ImportRuns().List(limit=1) len = %d, want 1", len(runs))
	}

	runs, err = store.ImportRuns().List(ctx, 0)
	if err != nil {
		t.Fatalf("ImportRuns().List(limit=0) error = %v", err)
	}
	if len(runs) != 2 {
		t.Fatalf("ImportRuns().List(limit=0) len = %d, want 2", len(runs))
	}
}

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

	ctx := context.Background()
	store := openTestDB(t)

	run := ImportRun{RunID: "run-lease", HostID: "host-lease", Mode: "strict", AccessMode: "subscription", State: "running"}
	if err := store.ImportRuns().Create(ctx, run); err != nil {
		t.Fatalf("ImportRuns().Create() error = %v", err)
	}

	item := ImportRunItem{
		ItemID:               "item-lease",
		RunID:                "run-lease",
		BaseURL:              "https://api.example.com/v1",
		ProviderID:           "provider-lease",
		APIKeyFingerprint:    "fp_lease",
		CurrentStage:         "confirm",
		ConfirmationStatus:   "pending",
		AccessStatus:         "unknown",
		MatchedAccountState:  "active",
		AccountResolution:    "created",
		RequestedModelsJSON:  `["model-a"]`,
		AdvisoryMessagesJSON: `["first"]`,
	}
	if err := store.ImportRunItems().Create(ctx, item); err != nil {
		t.Fatalf("ImportRunItems().Create() error = %v", err)
	}

	item.ConfirmationStatus = "confirmed"
	item.CurrentStage = "validate"
	item.AccessStatus = "active"
	item.AdvisoryMessagesJSON = `["updated"]`
	if err := store.ImportRunItems().Update(ctx, item); err != nil {
		t.Fatalf("ImportRunItems().Update() error = %v", err)
	}

	got, err := store.ImportRunItems().GetByItemID(ctx, "item-lease")
	if err != nil {
		t.Fatalf("ImportRunItems().GetByItemID() error = %v", err)
	}
	if got.CurrentStage != "validate" || got.ConfirmationStatus != "confirmed" {
		t.Fatalf("updated item = %+v, want validate/confirmed", got)
	}

	leaseItem := item
	leaseItem.ItemID = "item-pending"
	leaseItem.CurrentStage = "confirm"
	leaseItem.ConfirmationStatus = "pending"
	leaseItem.AccessStatus = "unknown"
	if err := store.ImportRunItems().Create(ctx, leaseItem); err != nil {
		t.Fatalf("ImportRunItems().Create(item-pending) error = %v", err)
	}

	now := time.Date(2026, 5, 23, 1, 2, 3, 0, time.UTC)
	leased, ok, err := store.ImportRunItems().TryAcquireConfirmationLease(ctx, "item-pending", "worker-1", now, 2*time.Minute)
	if err != nil {
		t.Fatalf("TryAcquireConfirmationLease() error = %v", err)
	}
	if !ok {
		t.Fatal("TryAcquireConfirmationLease() ok = false, want true")
	}
	if leased.LeaseOwner != "worker-1" {
		t.Fatalf("LeaseOwner = %q, want worker-1", leased.LeaseOwner)
	}
	if leased.ConfirmationAttempts != 1 {
		t.Fatalf("ConfirmationAttempts = %d, want 1", leased.ConfirmationAttempts)
	}

	_, ok, err = store.ImportRunItems().TryAcquireConfirmationLease(ctx, "item-pending", "worker-2", now.Add(30*time.Second), time.Minute)
	if err != nil {
		t.Fatalf("TryAcquireConfirmationLease(second) error = %v", err)
	}
	if ok {
		t.Fatal("TryAcquireConfirmationLease(second) ok = true, want false while lease active")
	}
}

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

	ctx := context.Background()
	store := openTestDB(t)

	if err := store.ImportRuns().Create(ctx, ImportRun{
		RunID:      "run-events",
		HostID:     "host-events",
		Mode:       "strict",
		AccessMode: "subscription",
		State:      "running",
	}); err != nil {
		t.Fatalf("ImportRuns().Create() error = %v", err)
	}
	if err := store.ImportRunItems().Create(ctx, ImportRunItem{
		ItemID:              "item-events",
		RunID:               "run-events",
		BaseURL:             "https://api.example.com/v1",
		ProviderID:          "provider-events",
		APIKeyFingerprint:   "fp_events",
		CurrentStage:        "confirm",
		ConfirmationStatus:  "pending",
		AccessStatus:        "unknown",
		MatchedAccountState: "active",
		AccountResolution:   "created",
	}); err != nil {
		t.Fatalf("ImportRunItems().Create() error = %v", err)
	}

	event := ImportRunItemEvent{
		EventID:   "evt-create",
		RunID:     "run-events",
		ItemID:    "item-events",
		EventType: "confirmation",
		Stage:     "confirm",
		Message:   "created by wrapper",
	}
	if err := store.ImportRunEvents().Create(ctx, event); err != nil {
		t.Fatalf("ImportRunEvents().Create() error = %v", err)
	}
	if err := store.ImportRunItemEvents().Create(ctx, ImportRunItemEvent{
		EventID:   "evt-alias",
		RunID:     "run-events",
		ItemID:    "item-events",
		EventType: "alias",
		Stage:     "confirm",
		Message:   "created by alias accessor",
	}); err != nil {
		t.Fatalf("ImportRunItemEvents().Create() error = %v", err)
	}

	events, err := store.ImportRunEvents().ListByItemID(ctx, "item-events")
	if err != nil {
		t.Fatalf("ImportRunEvents().ListByItemID() error = %v", err)
	}
	if len(events) != 2 {
		t.Fatalf("ImportRunEvents().ListByItemID() len = %d, want 2", len(events))
	}

	nullable := sqlNullInt64{Int64: 42, Valid: true}
	if ptr := nullable.ptr(); ptr == nil || *ptr != 42 {
		t.Fatalf("sqlNullInt64.ptr() = %#v, want 42", ptr)
	}
	if ptr := (sqlNullInt64{}).ptr(); ptr != nil {
		t.Fatalf("sqlNullInt64{}.ptr() = %#v, want nil", ptr)
	}
}
