package services

// Live-DB test for the user-replaceable filename keyword
// (t-paliad-354). Skipped without TEST_DATABASE_URL.
//
// Exercises the real Update → Get code path against Postgres: setting the
// override merges into composer_meta.filename_keyword without clobbering
// other composer keys, clearing it removes only that key, and the value
// reads back through the same jsonb decode the export handler relies on.

import (
	"context"
	"os"
	"testing"

	"github.com/google/uuid"
	"github.com/jmoiron/sqlx"
	_ "github.com/lib/pq"

	"mgit.msbls.de/m/paliad/internal/db"
)

func TestSubmissionDraft_FilenameKeyword_Live(t *testing.T) {
	url := os.Getenv("TEST_DATABASE_URL")
	if url == "" {
		t.Skip("TEST_DATABASE_URL not set — skipping live DB test")
	}
	if err := db.ApplyMigrations(url); err != nil {
		t.Fatalf("apply migrations: %v", err)
	}
	pool, err := sqlx.Connect("postgres", url)
	if err != nil {
		t.Fatalf("connect: %v", err)
	}
	defer pool.Close()
	ctx := context.Background()

	userID := uuid.New()
	email := "kw-" + userID.String()[:8] + "@hlc.com"
	cleanup := func() {
		pool.ExecContext(ctx, `DELETE FROM paliad.submission_drafts WHERE user_id = $1`, userID)
		pool.ExecContext(ctx, `DELETE FROM paliad.users WHERE id = $1`, userID)
		pool.ExecContext(ctx, `DELETE FROM auth.users WHERE id = $1`, userID)
	}
	defer cleanup()
	if _, err := pool.ExecContext(ctx, `INSERT INTO auth.users (id, email) VALUES ($1, $2)`, userID, email); err != nil {
		t.Fatalf("seed auth.users: %v", err)
	}
	if _, err := pool.ExecContext(ctx,
		`INSERT INTO paliad.users (id, email, display_name, office, global_role, lang)
		 VALUES ($1, $2, 'Keyword Tester', 'munich', 'standard', 'de')`, userID, email); err != nil {
		t.Fatalf("seed paliad.users: %v", err)
	}

	users := NewUserService(pool)
	projects := NewProjectService(pool, users)
	parties := NewPartyService(pool, projects)
	vars := NewSubmissionVarsService(pool, projects, parties, users)
	renderer := NewSubmissionRenderer()
	drafts := NewSubmissionDraftService(pool, projects, vars, renderer)

	// A project-less draft is the simplest fixture — no project tree
	// needed to exercise composer_meta persistence.
	d, err := drafts.Create(ctx, userID, nil, "upc.inf.cfi.sod", "de")
	if err != nil {
		t.Fatalf("create draft: %v", err)
	}

	// Pre-seed an unrelated composer_meta key to prove the merge/delete
	// only touches filename_keyword.
	if _, err := pool.ExecContext(ctx,
		`UPDATE paliad.submission_drafts SET composer_meta = '{"other":"keep-me"}'::jsonb WHERE id = $1`,
		d.ID); err != nil {
		t.Fatalf("seed composer_meta: %v", err)
	}

	// Set the override.
	kw := "Replik Hauptantrag"
	got, err := drafts.Update(ctx, userID, d.ID, DraftPatch{FilenameKeyword: &kw})
	if err != nil {
		t.Fatalf("update set keyword: %v", err)
	}
	if v, _ := got.ComposerMeta["filename_keyword"].(string); v != kw {
		t.Fatalf("after set: filename_keyword = %q, want %q", v, kw)
	}
	if v, _ := got.ComposerMeta["other"].(string); v != "keep-me" {
		t.Fatalf("after set: unrelated key 'other' = %q, want %q (merge clobbered it)", v, "keep-me")
	}

	// Read back through Get (the path the export handler uses).
	reload, err := drafts.Get(ctx, userID, d.ID)
	if err != nil {
		t.Fatalf("get after set: %v", err)
	}
	if v, _ := reload.ComposerMeta["filename_keyword"].(string); v != kw {
		t.Fatalf("reload: filename_keyword = %q, want %q", v, kw)
	}

	// Clear the override (empty string) — only filename_keyword should go.
	empty := ""
	cleared, err := drafts.Update(ctx, userID, d.ID, DraftPatch{FilenameKeyword: &empty})
	if err != nil {
		t.Fatalf("update clear keyword: %v", err)
	}
	if _, present := cleared.ComposerMeta["filename_keyword"]; present {
		t.Fatalf("after clear: filename_keyword still present: %v", cleared.ComposerMeta)
	}
	if v, _ := cleared.ComposerMeta["other"].(string); v != "keep-me" {
		t.Fatalf("after clear: unrelated key 'other' = %q, want %q (delete removed too much)", v, "keep-me")
	}
}
