package services

// Live-DB tests for DashboardLayoutService. Skipped when TEST_DATABASE_URL
// is unset.

import (
	"context"
	"encoding/json"
	"os"
	"testing"

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

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

type dashboardLayoutTestEnv struct {
	t       *testing.T
	pool    *sqlx.DB
	svc     *DashboardLayoutService
	userID  uuid.UUID
	cleanup func()
}

func setupDashboardLayoutTest(t *testing.T) *dashboardLayoutTestEnv {
	t.Helper()
	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)
	}
	ctx := context.Background()

	userID := uuid.New()
	if _, err := pool.ExecContext(ctx,
		`INSERT INTO auth.users (id, email) VALUES ($1, $1::text || '@test.local')
		 ON CONFLICT (id) DO NOTHING`, userID); err != nil {
		t.Logf("skip auth.users seed: %v", err)
	}
	if _, err := pool.ExecContext(ctx,
		`INSERT INTO paliad.users (id, email, display_name, office, global_role)
		 VALUES ($1, $1::text || '@test.local', 'Dashboard Layout Test', 'munich', 'standard')
		 ON CONFLICT (id) DO NOTHING`, userID); err != nil {
		t.Fatalf("seed paliad.users: %v", err)
	}

	cleanup := func() {
		c := context.Background()
		pool.ExecContext(c, `DELETE FROM paliad.user_dashboard_layouts WHERE user_id = $1`, userID)
		pool.ExecContext(c, `DELETE FROM paliad.users WHERE id = $1`, userID)
		pool.ExecContext(c, `DELETE FROM auth.users  WHERE id = $1`, userID)
		pool.Close()
	}

	return &dashboardLayoutTestEnv{
		t:       t,
		pool:    pool,
		svc:     NewDashboardLayoutService(pool),
		userID:  userID,
		cleanup: cleanup,
	}
}

func TestDashboardLayoutService_GetOrSeedAutoSeeds(t *testing.T) {
	env := setupDashboardLayoutTest(t)
	defer env.cleanup()
	ctx := context.Background()

	spec, err := env.svc.GetOrSeed(ctx, env.userID)
	if err != nil {
		t.Fatalf("GetOrSeed: %v", err)
	}
	if spec.Version != LayoutSpecVersion {
		t.Errorf("seeded version=%d; want %d", spec.Version, LayoutSpecVersion)
	}
	if len(spec.Widgets) != len(KnownWidgetKeys) {
		t.Errorf("seeded widget count=%d; want %d", len(spec.Widgets), len(KnownWidgetKeys))
	}

	// Second call returns the same row, not a second seed.
	spec2, err := env.svc.GetOrSeed(ctx, env.userID)
	if err != nil {
		t.Fatalf("GetOrSeed second: %v", err)
	}
	if len(spec2.Widgets) != len(spec.Widgets) {
		t.Errorf("second call widget count drifted: %d vs %d", len(spec2.Widgets), len(spec.Widgets))
	}
}

func TestDashboardLayoutService_UpdateRoundTrips(t *testing.T) {
	env := setupDashboardLayoutTest(t)
	defer env.cleanup()
	ctx := context.Background()

	// Seed first so the row exists.
	if _, err := env.svc.GetOrSeed(ctx, env.userID); err != nil {
		t.Fatalf("GetOrSeed: %v", err)
	}

	// Custom layout: hide matter-summary, reorder.
	custom := DashboardLayoutSpec{
		Version: LayoutSpecVersion,
		Widgets: []DashboardWidgetRef{
			{Key: WidgetUpcomingDeadlines, Visible: true, Settings: json.RawMessage(`{"count": 5, "horizon_days": 14}`)},
			{Key: WidgetMatterSummary, Visible: false},
			{Key: WidgetDeadlineSummary, Visible: true},
		},
	}
	out, err := env.svc.Update(ctx, env.userID, custom)
	if err != nil {
		t.Fatalf("Update: %v", err)
	}
	if len(out.Widgets) != 3 {
		t.Fatalf("Update returned %d widgets; want 3", len(out.Widgets))
	}
	if out.Widgets[0].Key != WidgetUpcomingDeadlines {
		t.Errorf("Update returned widgets[0]=%q; want %q", out.Widgets[0].Key, WidgetUpcomingDeadlines)
	}
	if out.Widgets[1].Visible {
		t.Errorf("Update returned widgets[1].Visible=true; want false")
	}

	// Re-read confirms persistence.
	got, err := env.svc.GetOrSeed(ctx, env.userID)
	if err != nil {
		t.Fatalf("GetOrSeed after update: %v", err)
	}
	if len(got.Widgets) != 3 {
		t.Errorf("GetOrSeed after update: %d widgets; want 3", len(got.Widgets))
	}
}

func TestDashboardLayoutService_UpdateRejectsInvalid(t *testing.T) {
	env := setupDashboardLayoutTest(t)
	defer env.cleanup()
	ctx := context.Background()

	bad := DashboardLayoutSpec{
		Version: LayoutSpecVersion,
		Widgets: []DashboardWidgetRef{
			{Key: "fake-widget-key", Visible: true},
		},
	}
	if _, err := env.svc.Update(ctx, env.userID, bad); err == nil {
		t.Fatalf("Update accepted invalid layout")
	}
}

func TestDashboardLayoutService_ResetToDefault(t *testing.T) {
	env := setupDashboardLayoutTest(t)
	defer env.cleanup()
	ctx := context.Background()

	// Custom layout first.
	custom := DashboardLayoutSpec{
		Version: LayoutSpecVersion,
		Widgets: []DashboardWidgetRef{
			{Key: WidgetDeadlineSummary, Visible: true},
		},
	}
	if _, err := env.svc.Update(ctx, env.userID, custom); err != nil {
		t.Fatalf("Update: %v", err)
	}

	// Reset.
	reset, err := env.svc.ResetToDefault(ctx, env.userID)
	if err != nil {
		t.Fatalf("ResetToDefault: %v", err)
	}
	if len(reset.Widgets) != len(KnownWidgetKeys) {
		t.Errorf("reset widget count=%d; want %d", len(reset.Widgets), len(KnownWidgetKeys))
	}
}
