package sqlite

import (
	"context"
	"database/sql"
	"errors"
	"testing"
)

func TestPacksRepoCreateAndGet(t *testing.T) {
	store := openTestDB(t)

	id, err := store.Packs().Create(context.Background(), Pack{
		PackID:         "test-pack",
		Version:        "1.0.0",
		Checksum:       "abc123",
		Vendor:         "test-vendor",
		TargetHost:     "sub2api",
		MinHostVersion: "0.1.0",
		MaxHostVersion: "0.2.x",
		ManifestJSON:   `{"name":"test"}`,
	})
	if err != nil {
		t.Fatalf("Create() error = %v", err)
	}
	if id <= 0 {
		t.Fatalf("Create() id = %d, want positive", id)
	}

	got, err := store.Packs().GetByID(context.Background(), id)
	if err != nil {
		t.Fatalf("GetByID() error = %v", err)
	}
	if got.PackID != "test-pack" || got.Version != "1.0.0" {
		t.Fatalf("GetByID() = %+v, want pack test-pack", got)
	}

	got2, err := store.Packs().GetByPackID(context.Background(), "test-pack")
	if err != nil {
		t.Fatalf("GetByPackID() error = %v", err)
	}
	if got2.ID != id {
		t.Fatalf("GetByPackID() id = %d, want %d", got2.ID, id)
	}
}

func TestPacksRepoCreateDefaultsManifestJSON(t *testing.T) {
	store := openTestDB(t)

	id, err := store.Packs().Create(context.Background(), Pack{
		PackID:   "no-manifest",
		Version:  "1.0.0",
		Checksum: "chk",
	})
	if err != nil {
		t.Fatalf("Create() error = %v", err)
	}
	got, err := store.Packs().GetByID(context.Background(), id)
	if err != nil {
		t.Fatalf("GetByID() error = %v", err)
	}
	if got.ManifestJSON != "{}" {
		t.Fatalf("ManifestJSON = %q, want {}", got.ManifestJSON)
	}
}

func TestPacksRepoUpsertCreatesNew(t *testing.T) {
	store := openTestDB(t)

	id, err := store.Packs().Upsert(context.Background(), Pack{
		PackID:   "upsert-pack",
		Version:  "1.0.0",
		Checksum: "chk1",
	})
	if err != nil {
		t.Fatalf("Upsert() error = %v", err)
	}
	if id <= 0 {
		t.Fatalf("Upsert() id = %d, want positive", id)
	}
}

func TestPacksRepoUpsertUpdatesExisting(t *testing.T) {
	store := openTestDB(t)

	id1, err := store.Packs().Upsert(context.Background(), Pack{
		PackID:   "upsert-pack",
		Version:  "1.0.0",
		Checksum: "chk1",
	})
	if err != nil {
		t.Fatalf("Upsert() create error = %v", err)
	}

	id2, err := store.Packs().Upsert(context.Background(), Pack{
		PackID:   "upsert-pack",
		Version:  "2.0.0",
		Checksum: "chk2",
	})
	if err != nil {
		t.Fatalf("Upsert() update error = %v", err)
	}
	if id2 != id1 {
		t.Fatalf("Upsert() update returned id %d, want original %d", id2, id1)
	}

	got, err := store.Packs().GetByID(context.Background(), id1)
	if err != nil {
		t.Fatalf("GetByID() error = %v", err)
	}
	if got.Version != "2.0.0" {
		t.Fatalf("Version after upsert = %q, want 2.0.0", got.Version)
	}
}

func TestPacksRepoValidationErrors(t *testing.T) {
	store := openTestDB(t)

	tests := []struct {
		name string
		pack Pack
	}{
		{"empty pack_id", Pack{Version: "v", Checksum: "c"}},
		{"empty version", Pack{PackID: "p", Checksum: "c"}},
		{"empty checksum", Pack{PackID: "p", Version: "v"}},
	}
	for _, tt := range tests {
		t.Run(tt.name, func(t *testing.T) {
			_, err := store.Packs().Create(context.Background(), tt.pack)
			if err == nil {
				t.Fatal("Create() error = nil, want validation error")
			}
		})
	}
}

func TestPacksRepoGetByIDNotFound(t *testing.T) {
	store := openTestDB(t)
	_, err := store.Packs().GetByID(context.Background(), 999)
	if !errors.Is(err, sql.ErrNoRows) {
		t.Fatalf("GetByID(999) error = %v, want sql.ErrNoRows", err)
	}
}

func TestPacksRepoGetByPackIDEmptyError(t *testing.T) {
	store := openTestDB(t)
	_, err := store.Packs().GetByPackID(context.Background(), "")
	if err == nil {
		t.Fatal("GetByPackID('') error = nil, want error")
	}
}

func TestPacksRepoGetByPackIDNotFound(t *testing.T) {
	store := openTestDB(t)
	_, err := store.Packs().GetByPackID(context.Background(), "nonexistent")
	if !errors.Is(err, sql.ErrNoRows) {
		t.Fatalf("GetByPackID() error = %v, want sql.ErrNoRows", err)
	}
}

func TestPacksRepoListAll(t *testing.T) {
	store := openTestDB(t)

	packs, err := store.Packs().ListAll(context.Background())
	if err != nil {
		t.Fatalf("ListAll() empty error = %v", err)
	}
	if len(packs) != 0 {
		t.Fatalf("ListAll() empty len = %d, want 0", len(packs))
	}

	if _, err := store.Packs().Create(context.Background(), Pack{PackID: "pack-a", Version: "1.0.0", Checksum: "chk-a"}); err != nil {
		t.Fatalf("Create(pack-a) error = %v", err)
	}
	if _, err := store.Packs().Create(context.Background(), Pack{PackID: "pack-b", Version: "1.1.0", Checksum: "chk-b"}); err != nil {
		t.Fatalf("Create(pack-b) error = %v", err)
	}

	packs, err = store.Packs().ListAll(context.Background())
	if err != nil {
		t.Fatalf("ListAll() error = %v", err)
	}
	if len(packs) != 2 {
		t.Fatalf("ListAll() len = %d, want 2", len(packs))
	}
	if packs[0].PackID != "pack-a" || packs[1].PackID != "pack-b" {
		t.Fatalf("ListAll() ids = [%q %q], want [pack-a pack-b]", packs[0].PackID, packs[1].PackID)
	}
}
