package domain

import (
	"testing"
	"time"

	"github.com/google/uuid"
)

func requireGeneratedUUID(t *testing.T, value string) {
	t.Helper()

	if value == "" {
		t.Fatalf("expected generated uuid")
	}
	if _, err := uuid.Parse(value); err != nil {
		t.Fatalf("expected valid uuid, got %q: %v", value, err)
	}
}

func TestBeforeCreateGeneratesMissingIDs(t *testing.T) {
	tests := []struct {
		name string
		run  func(t *testing.T)
	}{
		{
			name: "api key",
			run: func(t *testing.T) {
				model := ApiKey{}
				if err := model.BeforeCreate(nil); err != nil {
					t.Fatalf("unexpected error: %v", err)
				}
				requireGeneratedUUID(t, model.ID)
			},
		},
		{
			name: "client consent",
			run: func(t *testing.T) {
				model := ClientConsent{}
				if err := model.BeforeCreate(nil); err != nil {
					t.Fatalf("unexpected error: %v", err)
				}
				requireGeneratedUUID(t, model.ID)
			},
		},
		{
			name: "identity provider config",
			run: func(t *testing.T) {
				model := IdentityProviderConfig{}
				if err := model.BeforeCreate(nil); err != nil {
					t.Fatalf("unexpected error: %v", err)
				}
				requireGeneratedUUID(t, model.ID)
			},
		},
		{
			name: "keto outbox",
			run: func(t *testing.T) {
				model := KetoOutbox{}
				if err := model.BeforeCreate(nil); err != nil {
					t.Fatalf("unexpected error: %v", err)
				}
				requireGeneratedUUID(t, model.ID)
			},
		},
		{
			name: "tenant",
			run: func(t *testing.T) {
				model := Tenant{}
				if err := model.BeforeCreate(nil); err != nil {
					t.Fatalf("unexpected error: %v", err)
				}
				requireGeneratedUUID(t, model.ID)
			},
		},
		{
			name: "tenant domain",
			run: func(t *testing.T) {
				model := TenantDomain{}
				if err := model.BeforeCreate(nil); err != nil {
					t.Fatalf("unexpected error: %v", err)
				}
				requireGeneratedUUID(t, model.ID)
			},
		},
		{
			name: "user",
			run: func(t *testing.T) {
				model := User{}
				if err := model.BeforeCreate(nil); err != nil {
					t.Fatalf("unexpected error: %v", err)
				}
				requireGeneratedUUID(t, model.ID)
			},
		},
		{
			name: "user group",
			run: func(t *testing.T) {
				model := UserGroup{}
				if err := model.BeforeCreate(nil); err != nil {
					t.Fatalf("unexpected error: %v", err)
				}
				requireGeneratedUUID(t, model.ID)
			},
		},
		{
			name: "worksmobile resource mapping",
			run: func(t *testing.T) {
				model := WorksmobileResourceMapping{}
				if err := model.BeforeCreate(nil); err != nil {
					t.Fatalf("unexpected error: %v", err)
				}
				requireGeneratedUUID(t, model.ID)
			},
		},
	}

	for _, tc := range tests {
		t.Run(tc.name, tc.run)
	}
}

func TestBeforeCreatePreservesExistingIDs(t *testing.T) {
	tests := []struct {
		name string
		run  func(t *testing.T)
	}{
		{
			name: "api key",
			run: func(t *testing.T) {
				model := ApiKey{ID: "existing-id"}
				if err := model.BeforeCreate(nil); err != nil {
					t.Fatalf("unexpected error: %v", err)
				}
				if model.ID != "existing-id" {
					t.Fatalf("expected existing id to be preserved")
				}
			},
		},
		{
			name: "client consent",
			run: func(t *testing.T) {
				model := ClientConsent{ID: "existing-id"}
				if err := model.BeforeCreate(nil); err != nil {
					t.Fatalf("unexpected error: %v", err)
				}
				if model.ID != "existing-id" {
					t.Fatalf("expected existing id to be preserved")
				}
			},
		},
		{
			name: "identity provider config",
			run: func(t *testing.T) {
				model := IdentityProviderConfig{ID: "existing-id"}
				if err := model.BeforeCreate(nil); err != nil {
					t.Fatalf("unexpected error: %v", err)
				}
				if model.ID != "existing-id" {
					t.Fatalf("expected existing id to be preserved")
				}
			},
		},
		{
			name: "keto outbox",
			run: func(t *testing.T) {
				model := KetoOutbox{ID: "existing-id"}
				if err := model.BeforeCreate(nil); err != nil {
					t.Fatalf("unexpected error: %v", err)
				}
				if model.ID != "existing-id" {
					t.Fatalf("expected existing id to be preserved")
				}
			},
		},
		{
			name: "tenant",
			run: func(t *testing.T) {
				model := Tenant{ID: "existing-id"}
				if err := model.BeforeCreate(nil); err != nil {
					t.Fatalf("unexpected error: %v", err)
				}
				if model.ID != "existing-id" {
					t.Fatalf("expected existing id to be preserved")
				}
			},
		},
		{
			name: "tenant domain",
			run: func(t *testing.T) {
				model := TenantDomain{ID: "existing-id"}
				if err := model.BeforeCreate(nil); err != nil {
					t.Fatalf("unexpected error: %v", err)
				}
				if model.ID != "existing-id" {
					t.Fatalf("expected existing id to be preserved")
				}
			},
		},
		{
			name: "user",
			run: func(t *testing.T) {
				model := User{ID: "existing-id"}
				if err := model.BeforeCreate(nil); err != nil {
					t.Fatalf("unexpected error: %v", err)
				}
				if model.ID != "existing-id" {
					t.Fatalf("expected existing id to be preserved")
				}
			},
		},
		{
			name: "user group",
			run: func(t *testing.T) {
				model := UserGroup{ID: "existing-id"}
				if err := model.BeforeCreate(nil); err != nil {
					t.Fatalf("unexpected error: %v", err)
				}
				if model.ID != "existing-id" {
					t.Fatalf("expected existing id to be preserved")
				}
			},
		},
		{
			name: "worksmobile resource mapping",
			run: func(t *testing.T) {
				model := WorksmobileResourceMapping{ID: "existing-id"}
				if err := model.BeforeCreate(nil); err != nil {
					t.Fatalf("unexpected error: %v", err)
				}
				if model.ID != "existing-id" {
					t.Fatalf("expected existing id to be preserved")
				}
			},
		},
	}

	for _, tc := range tests {
		t.Run(tc.name, tc.run)
	}
}

func TestTableNames(t *testing.T) {
	tests := []struct {
		name     string
		got      string
		expected string
	}{
		{name: "keto outbox", got: (&KetoOutbox{}).TableName(), expected: "keto_outbox"},
		{name: "rp usage event", got: (&RPUsageEvent{}).TableName(), expected: "rp_usage_outbox"},
		{name: "rp user metadata", got: (RPUserMetadata{}).TableName(), expected: "rp_user_metadata"},
		{name: "user group", got: (&UserGroup{}).TableName(), expected: "user_groups"},
	}

	for _, tc := range tests {
		t.Run(tc.name, func(t *testing.T) {
			if tc.got != tc.expected {
				t.Fatalf("unexpected table name: got=%s expected=%s", tc.got, tc.expected)
			}
		})
	}
}

func TestTenantIsActive(t *testing.T) {
	tests := []struct {
		name     string
		status   string
		expected bool
	}{
		{name: "active", status: TenantStatusActive, expected: true},
		{name: "pending", status: TenantStatusPending, expected: false},
		{name: "suspended", status: TenantStatusSuspended, expected: false},
		{name: "deleted", status: TenantStatusDeleted, expected: false},
	}

	for _, tc := range tests {
		t.Run(tc.name, func(t *testing.T) {
			tenant := Tenant{Status: tc.status}
			if got := tenant.IsActive(); got != tc.expected {
				t.Fatalf("unexpected active state: got=%v expected=%v", got, tc.expected)
			}
		})
	}
}

func TestRPUsageEventBeforeCreateDefaults(t *testing.T) {
	event := RPUsageEvent{}

	if err := event.BeforeCreate(nil); err != nil {
		t.Fatalf("unexpected error: %v", err)
	}
	requireGeneratedUUID(t, event.ID)
	if event.Status != RPUsageOutboxStatusPending {
		t.Fatalf("unexpected status: %s", event.Status)
	}
	if event.OccurredAt.IsZero() {
		t.Fatalf("expected occurred_at default")
	}
	if event.Payload == nil {
		t.Fatalf("expected empty payload default")
	}
}

func TestRPUsageEventBeforeCreatePreservesExplicitValues(t *testing.T) {
	occurredAt := time.Date(2026, 5, 29, 1, 2, 3, 0, time.UTC)
	event := RPUsageEvent{
		ID:         "existing-id",
		Status:     RPUsageOutboxStatusProcessing,
		OccurredAt: occurredAt,
		Payload:    JSONMap{"source": "test"},
	}

	if err := event.BeforeCreate(nil); err != nil {
		t.Fatalf("unexpected error: %v", err)
	}
	if event.ID != "existing-id" {
		t.Fatalf("expected existing id to be preserved")
	}
	if event.Status != RPUsageOutboxStatusProcessing {
		t.Fatalf("expected status to be preserved")
	}
	if !event.OccurredAt.Equal(occurredAt) {
		t.Fatalf("expected occurred_at to be preserved")
	}
	if event.Payload["source"] != "test" {
		t.Fatalf("expected payload to be preserved")
	}
}

func TestWorksmobileOutboxBeforeCreateDefaults(t *testing.T) {
	outbox := WorksmobileOutbox{}

	if err := outbox.BeforeCreate(nil); err != nil {
		t.Fatalf("unexpected error: %v", err)
	}
	requireGeneratedUUID(t, outbox.ID)
	if outbox.Status != WorksmobileOutboxStatusPending {
		t.Fatalf("unexpected status: %s", outbox.Status)
	}
}

func TestWorksmobileOutboxBeforeCreatePreservesExplicitValues(t *testing.T) {
	outbox := WorksmobileOutbox{
		ID:     "existing-id",
		Status: WorksmobileOutboxStatusProcessing,
	}

	if err := outbox.BeforeCreate(nil); err != nil {
		t.Fatalf("unexpected error: %v", err)
	}
	if outbox.ID != "existing-id" {
		t.Fatalf("expected existing id to be preserved")
	}
	if outbox.Status != WorksmobileOutboxStatusProcessing {
		t.Fatalf("expected status to be preserved")
	}
}
