package services

// Live-DB integration tests for the Composer seeding flow (t-paliad-313
// Slice A). Skipped when TEST_DATABASE_URL is unset, mirroring the
// other live-DB tests (see cansee_test.go for the bootstrap pattern).
//
// Covers:
//   1. Mig 146 seeded the catalog: hlc-letterhead + neutral both
//      resolve via GetBySlug and carry 10 section defaults each.
//   2. BaseService.GetDefaultForCode picks the firm-matched base for a
//      canonical submission_code (e.g. de.inf.lg.erwidg) — Slice A
//      contract that drives new-draft seeding.
//   3. SubmissionDraftService.Create on a fresh draft seeds base_id +
//      10 submission_sections rows in one transaction, with order_index
//      ascending and bilingual labels populated.

import (
	"context"
	"os"
	"testing"

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

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

func TestComposerSeedFlow(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()

	bases := NewBaseService(pool)

	t.Run("seed catalog: hlc-letterhead has 10 default sections", func(t *testing.T) {
		b, err := bases.GetBySlug(ctx, "hlc-letterhead")
		if err != nil {
			t.Fatalf("GetBySlug(hlc-letterhead): %v", err)
		}
		if got := len(b.SectionSpec.Defaults); got != 10 {
			t.Errorf("len(Defaults) = %d; want 10", got)
		}
		if b.SectionSpec.Stylemap["heading_1"] != "HLpat-Heading-H1" {
			t.Errorf("Stylemap[heading_1] = %q; want HLpat-Heading-H1", b.SectionSpec.Stylemap["heading_1"])
		}
		// Verify the section order is strictly ascending.
		prev := 0
		for _, d := range b.SectionSpec.Defaults {
			if d.OrderIndex <= prev {
				t.Errorf("non-ascending order_index: %d (prev=%d) at %s", d.OrderIndex, prev, d.SectionKey)
			}
			prev = d.OrderIndex
		}
	})

	t.Run("seed catalog: neutral exists with universal stylemap", func(t *testing.T) {
		b, err := bases.GetBySlug(ctx, "neutral")
		if err != nil {
			t.Fatalf("GetBySlug(neutral): %v", err)
		}
		if b.SectionSpec.Stylemap["heading_1"] != "Heading 1" {
			t.Errorf("neutral Stylemap[heading_1] = %q; want \"Heading 1\"", b.SectionSpec.Stylemap["heading_1"])
		}
	})

	t.Run("GetDefaultForCode firm match", func(t *testing.T) {
		// HLC + de.inf.lg.erwidg → hlc-letterhead (firm-matched).
		b, err := bases.GetDefaultForCode(ctx, "HLC", "de.inf.lg.erwidg")
		if err != nil {
			t.Fatalf("GetDefaultForCode HLC: %v", err)
		}
		if b.Slug != "hlc-letterhead" {
			t.Errorf("Slug = %q; want hlc-letterhead", b.Slug)
		}
	})

	t.Run("GetDefaultForCode falls back to neutral when no firm hint", func(t *testing.T) {
		b, err := bases.GetDefaultForCode(ctx, "", "de.inf.lg.erwidg")
		if err != nil {
			t.Fatalf("GetDefaultForCode no-firm: %v", err)
		}
		// Without a firm hint, the fallback chain skips firm-matched
		// queries and lands on the firm-NULL neutral base.
		if b.Slug != "neutral" {
			t.Errorf("Slug = %q; want neutral (firm-NULL fallback)", b.Slug)
		}
	})

	// Section seeding via SubmissionDraftService.Create — exercises the
	// transactional INSERT path. Requires a real auth.users + paliad.users
	// row because submission_drafts.user_id is FK-constrained.
	t.Run("SubmissionDraftService.Create seeds 10 section rows", func(t *testing.T) {
		userID := uuid.New()
		cleanup := func() {
			pool.ExecContext(ctx, `DELETE FROM paliad.submission_sections WHERE draft_id IN (SELECT id FROM paliad.submission_drafts WHERE user_id = $1)`, userID)
			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)
		}
		cleanup()
		defer cleanup()

		email := "composer-seed-" + userID.String()[:8] + "@hlc.com"
		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, 'Composer Seed', '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)
		sections := NewSectionService(pool)
		drafts.AttachComposer(bases, sections, "HLC")

		d, err := drafts.Create(ctx, userID, nil, "de.inf.lg.erwidg", "de")
		if err != nil {
			t.Fatalf("Create: %v", err)
		}
		if d.BaseID == nil {
			t.Fatalf("BaseID = nil; want seeded base reference")
		}
		// hlc-letterhead is the firm default for HLC.
		base, _ := bases.GetByID(ctx, *d.BaseID)
		if base == nil || base.Slug != "hlc-letterhead" {
			t.Errorf("seeded base slug = %v; want hlc-letterhead", base)
		}

		secs, err := sections.ListForDraft(ctx, d.ID)
		if err != nil {
			t.Fatalf("ListForDraft: %v", err)
		}
		if len(secs) != 10 {
			t.Errorf("section count = %d; want 10", len(secs))
		}
		// Verify section_key set + bilingual labels populated.
		wantKeys := map[string]bool{
			"letterhead": false, "caption": false, "introduction": false,
			"requests": false, "facts": false, "legal_argument": false,
			"evidence": false, "exhibits": false, "closing": false, "signature": false,
		}
		prev := 0
		for _, sec := range secs {
			wantKeys[sec.SectionKey] = true
			if sec.OrderIndex <= prev {
				t.Errorf("non-ascending order_index: %d (prev=%d) at %s", sec.OrderIndex, prev, sec.SectionKey)
			}
			prev = sec.OrderIndex
			if sec.LabelDE == "" || sec.LabelEN == "" {
				t.Errorf("section %s missing bilingual label: de=%q en=%q", sec.SectionKey, sec.LabelDE, sec.LabelEN)
			}
		}
		for k, seen := range wantKeys {
			if !seen {
				t.Errorf("missing seeded section_key: %s", k)
			}
		}
	})
}
