package services

import (
	"context"
	"os"
	"testing"

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

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

// TestProjectList_OrderedByPath pins the t-paliad-125 contract: ProjectService.List
// returns rows ordered by `path`, so any descendant immediately follows its
// ancestor — the same ordering BuildTree produces. Picker dropdowns rely on
// this so they can render the project tree as a flat indented list. A previous
// `ORDER BY updated_at DESC` interleaved cousins by recency and broke the
// visual hierarchy in the /events Project filter (m's report 2026-05-04).
//
// Skipped when TEST_DATABASE_URL is unset.
func TestProjectList_OrderedByPath(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()

	adminID := uuid.New()
	// Two siblings (clientA, clientB) chosen so clientA's UUID sorts BEFORE
	// clientB's — this lets us assert that an updated child of clientA still
	// appears between clientA and clientB rather than floating to the top by
	// recency. We retry until we get suitable IDs (UUIDv4 ordering is random).
	var clientA, clientB uuid.UUID
	for {
		a, b := uuid.New(), uuid.New()
		if a.String() < b.String() {
			clientA, clientB = a, b
			break
		}
	}
	caseUnderA := uuid.New()
	caseUnderB := uuid.New()

	cleanup := func() {
		for _, pid := range []uuid.UUID{caseUnderA, caseUnderB, clientA, clientB} {
			pool.ExecContext(ctx, `DELETE FROM paliad.project_events WHERE project_id = $1`, pid)
			pool.ExecContext(ctx, `DELETE FROM paliad.project_teams  WHERE project_id = $1`, pid)
			pool.ExecContext(ctx, `DELETE FROM paliad.projects       WHERE id = $1`, pid)
		}
		pool.ExecContext(ctx, `DELETE FROM paliad.users WHERE id = $1`, adminID)
		pool.ExecContext(ctx, `DELETE FROM auth.users  WHERE id = $1`, adminID)
	}
	cleanup()
	defer cleanup()

	if _, err := pool.ExecContext(ctx,
		`INSERT INTO auth.users (id, email) VALUES ($1, $2)`,
		adminID, "list-order@hlc.com"); 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, 'List Order', 'munich', 'global_admin', 'de')`,
		adminID, "list-order@hlc.com"); err != nil {
		t.Fatalf("seed paliad.users: %v", err)
	}

	// Insert order is (B, caseB, A, caseA) so updated_at DESC would put A's
	// branch first across both clients — interleaving caseUnderA between
	// clientB rows. Path order must override that and group each branch.
	rows := []struct {
		id        uuid.UUID
		typ       string
		parent    *uuid.UUID
		title     string
		reference string
	}{
		{clientB, "client", nil, "Bravo Corp", "2026/9101"},
		{caseUnderB, "case", &clientB, "Bravo Case", "2026/9102"},
		{clientA, "client", nil, "Alpha Corp", "2026/9103"},
		{caseUnderA, "case", &clientA, "Alpha Case", "2026/9104"},
	}
	for _, p := range rows {
		var parent any
		if p.parent != nil {
			parent = *p.parent
		}
		if _, err := pool.ExecContext(ctx,
			`INSERT INTO paliad.projects (id, type, parent_id, path, title, reference, status, created_by)
			 VALUES ($1, $2, $3, $4, $5, $6, 'active', $7)`,
			p.id, p.typ, parent, p.id.String(), p.title, p.reference, adminID); err != nil {
			t.Fatalf("seed paliad.projects %s: %v", p.id, err)
		}
	}

	users := NewUserService(pool)
	projects := NewProjectService(pool, users)

	got, err := projects.List(ctx, adminID, ProjectFilter{})
	if err != nil {
		t.Fatalf("List: %v", err)
	}

	// Restrict to the seed set to ignore unrelated rows in the shared dev DB.
	seed := map[uuid.UUID]bool{clientA: true, caseUnderA: true, clientB: true, caseUnderB: true}
	var ids []uuid.UUID
	for _, p := range got {
		if seed[p.ID] {
			ids = append(ids, p.ID)
		}
	}
	want := []uuid.UUID{clientA, caseUnderA, clientB, caseUnderB}
	if len(ids) != len(want) {
		t.Fatalf("List returned %d seed rows, want %d (got=%v)", len(ids), len(want), ids)
	}
	for i, id := range want {
		if ids[i] != id {
			t.Errorf("List[%d] = %s, want %s — order is not path-based", i, ids[i], id)
		}
	}
}
