package awardref

import (
	"context"
	"path/filepath"
	"testing"

	"hamlog/internal/db"
)

func renameRepo(t *testing.T) *Repo {
	t.Helper()
	conn, err := db.Open(filepath.Join(t.TempDir(), "a.db"))
	if err != nil {
		t.Fatalf("open: %v", err)
	}
	t.Cleanup(func() { conn.Close() })
	return NewRepo(conn)
}

// Correcting a reference's number must keep the reference.
//
// WAJA shipped numbered by the Japanese state instead of by the JARL, and until
// now the only way to fix that was to delete all 47 references and import a new
// list — losing anything the operator had adjusted. A rename keeps the pattern,
// the entity list and the validity window, because a wrong NUMBER is all that
// was wrong.
func TestRenameKeepsEverythingButTheCode(t *testing.T) {
	r := renameRepo(t)
	ctx := context.Background()
	if err := r.Upsert(ctx, "WAJA", Ref{
		Code: "13", Name: "Tokyo", Pattern: `\bTok[iy]o\b`, Valid: true,
		DXCCList: []int{339}, ValidFrom: "1970-01-01",
	}); err != nil {
		t.Fatalf("seed: %v", err)
	}

	if err := r.Rename(ctx, "WAJA", "13", "10"); err != nil {
		t.Fatalf("rename: %v", err)
	}

	refs, err := r.List(ctx, "WAJA")
	if err != nil {
		t.Fatalf("list: %v", err)
	}
	if len(refs) != 1 {
		t.Fatalf("WAJA holds %d references after a rename, want 1 — it was copied, not renamed", len(refs))
	}
	got := refs[0]
	if got.Code != "10" {
		t.Errorf("code = %q, want 10", got.Code)
	}
	if got.Name != "Tokyo" || got.Pattern != `\bTok[iy]o\b` {
		t.Errorf("the reference lost what it carried: name=%q pattern=%q", got.Name, got.Pattern)
	}
	if len(got.DXCCList) != 1 || got.DXCCList[0] != 339 || got.ValidFrom != "1970-01-01" {
		t.Errorf("the reference lost its entity list or dates: %+v", got)
	}
}

// A number already in use must be refused. Left to REPLACE, the rename would
// take the other reference's name, pattern and dates with it — one entry
// silently swallowing another, found much later as a prefecture that has
// quietly gone missing from the list.
func TestRenameRefusesANumberAlreadyTaken(t *testing.T) {
	r := renameRepo(t)
	ctx := context.Background()
	if err := r.Upsert(ctx, "WAJA", Ref{Code: "10", Name: "Gunma", Valid: true}); err != nil {
		t.Fatalf("seed: %v", err)
	}
	if err := r.Upsert(ctx, "WAJA", Ref{Code: "13", Name: "Tokyo", Valid: true}); err != nil {
		t.Fatalf("seed: %v", err)
	}

	if err := r.Rename(ctx, "WAJA", "13", "10"); err == nil {
		t.Fatal("renaming onto an existing number was accepted — one reference would have eaten the other")
	}
	refs, _ := r.List(ctx, "WAJA")
	if len(refs) != 2 {
		t.Fatalf("WAJA holds %d references, want both still there", len(refs))
	}
}

// Renaming something that is not there is an error, not a silent no-op: it
// means the editor and the store disagree about what the award holds.
func TestRenameAnUnknownReferenceFails(t *testing.T) {
	r := renameRepo(t)
	if err := r.Rename(context.Background(), "WAJA", "99", "10"); err == nil {
		t.Error("renaming a reference the award does not have was accepted")
	}
}

// Codes are stored upper-cased, so a rename must compare the same way — else
// "eu-048" onto "EU-048" looks like a move and is really the same reference,
// which the collision check has to catch.
func TestRenameIsCaseInsensitive(t *testing.T) {
	r := renameRepo(t)
	ctx := context.Background()
	if err := r.Upsert(ctx, "IOTA", Ref{Code: "EU-048", Name: "Belle-Ile", Valid: true}); err != nil {
		t.Fatalf("seed: %v", err)
	}
	if err := r.Rename(ctx, "iota", "eu-048", "eu-048"); err != nil {
		t.Errorf("renaming a reference to itself in another case failed: %v", err)
	}
	if err := r.Rename(ctx, "IOTA", "eu-048", "eu-049"); err != nil {
		t.Fatalf("rename: %v", err)
	}
	refs, _ := r.List(ctx, "IOTA")
	if len(refs) != 1 || refs[0].Code != "EU-049" {
		t.Errorf("references = %+v, want the one renamed to EU-049 and upper-cased", refs)
	}
}
