package repository

import (
	"context"
	"testing"
	"time"

	"github.com/user-management-system/internal/domain"
)

func setupWebhookRepository(t *testing.T) *WebhookRepository {
	t.Helper()

	db := openTestDB(t)
	if err := db.AutoMigrate(&domain.Webhook{}, &domain.WebhookDelivery{}); err != nil {
		t.Fatalf("migrate webhook tables failed: %v", err)
	}

	return NewWebhookRepository(db)
}

func newWebhookFixture(name string, createdBy int64, status domain.WebhookStatus) *domain.Webhook {
	return &domain.Webhook{
		Name:       name,
		URL:        "https://example.com/webhook",
		Secret:     "secret-demo",
		Events:     `["user.registered"]`,
		Status:     status,
		MaxRetries: 3,
		TimeoutSec: 10,
		CreatedBy:  createdBy,
	}
}

func TestWebhookRepositoryCreateGetUpdateAndDelete(t *testing.T) {
	repo := setupWebhookRepository(t)
	ctx := context.Background()

	webhook := newWebhookFixture("alpha", 101, domain.WebhookStatusActive)
	if err := repo.Create(ctx, webhook); err != nil {
		t.Fatalf("Create failed: %v", err)
	}
	if webhook.ID == 0 {
		t.Fatal("expected webhook id to be assigned")
	}

	loaded, err := repo.GetByID(ctx, webhook.ID)
	if err != nil {
		t.Fatalf("GetByID failed: %v", err)
	}
	if loaded.Name != "alpha" {
		t.Fatalf("expected loaded webhook name alpha, got %q", loaded.Name)
	}

	if err := repo.Update(ctx, webhook.ID, map[string]interface{}{
		"name":   "alpha-updated",
		"status": domain.WebhookStatusInactive,
	}); err != nil {
		t.Fatalf("Update failed: %v", err)
	}

	updated, err := repo.GetByID(ctx, webhook.ID)
	if err != nil {
		t.Fatalf("GetByID after update failed: %v", err)
	}
	if updated.Name != "alpha-updated" {
		t.Fatalf("expected updated name alpha-updated, got %q", updated.Name)
	}
	if updated.Status != domain.WebhookStatusInactive {
		t.Fatalf("expected updated status inactive, got %d", updated.Status)
	}

	if err := repo.Delete(ctx, webhook.ID); err != nil {
		t.Fatalf("Delete failed: %v", err)
	}

	if _, err := repo.GetByID(ctx, webhook.ID); err == nil {
		t.Fatal("expected deleted webhook lookup to fail")
	}
}

func TestWebhookRepositoryListsByCreatorAndActiveStatus(t *testing.T) {
	repo := setupWebhookRepository(t)
	ctx := context.Background()

	fixtures := []*domain.Webhook{
		newWebhookFixture("creator-1-active", 1, domain.WebhookStatusActive),
		newWebhookFixture("creator-1-inactive", 1, domain.WebhookStatusInactive),
		newWebhookFixture("creator-2-active", 2, domain.WebhookStatusActive),
	}

	for _, webhook := range fixtures {
		if err := repo.Create(ctx, webhook); err != nil {
			t.Fatalf("Create(%s) failed: %v", webhook.Name, err)
		}
	}

	creatorOneHooks, err := repo.ListByCreator(ctx, 1)
	if err != nil {
		t.Fatalf("ListByCreator(1) failed: %v", err)
	}
	if len(creatorOneHooks) != 2 {
		t.Fatalf("expected 2 hooks for creator 1, got %d", len(creatorOneHooks))
	}

	allHooks, err := repo.ListByCreator(ctx, 0)
	if err != nil {
		t.Fatalf("ListByCreator(0) failed: %v", err)
	}
	if len(allHooks) != 3 {
		t.Fatalf("expected 3 hooks when listing all creators, got %d", len(allHooks))
	}

	activeHooks, err := repo.ListActive(ctx)
	if err != nil {
		t.Fatalf("ListActive failed: %v", err)
	}
	if len(activeHooks) != 2 {
		t.Fatalf("expected 2 active hooks, got %d", len(activeHooks))
	}
	for _, hook := range activeHooks {
		if hook.Status != domain.WebhookStatusActive {
			t.Fatalf("expected active hook status, got %d", hook.Status)
		}
	}
}

func TestWebhookRepositoryCreateAndListDeliveries(t *testing.T) {
	repo := setupWebhookRepository(t)
	ctx := context.Background()

	webhook := newWebhookFixture("delivery-hook", 7, domain.WebhookStatusActive)
	if err := repo.Create(ctx, webhook); err != nil {
		t.Fatalf("Create webhook failed: %v", err)
	}

	olderTime := time.Now().Add(-time.Minute)
	newerTime := time.Now()

	firstDelivery := &domain.WebhookDelivery{
		WebhookID:    webhook.ID,
		EventType:    domain.EventUserRegistered,
		Payload:      `{"user":"older"}`,
		StatusCode:   200,
		ResponseBody: `{"ok":true}`,
		Attempt:      1,
		Success:      true,
		CreatedAt:    olderTime,
	}
	secondDelivery := &domain.WebhookDelivery{
		WebhookID:    webhook.ID,
		EventType:    domain.EventUserLogin,
		Payload:      `{"user":"newer"}`,
		StatusCode:   500,
		ResponseBody: `{"ok":false}`,
		Attempt:      2,
		Success:      false,
		Error:        "delivery failed",
		CreatedAt:    newerTime,
	}

	if err := repo.CreateDelivery(ctx, firstDelivery); err != nil {
		t.Fatalf("CreateDelivery(first) failed: %v", err)
	}
	if err := repo.CreateDelivery(ctx, secondDelivery); err != nil {
		t.Fatalf("CreateDelivery(second) failed: %v", err)
	}

	latestOnly, err := repo.ListDeliveries(ctx, webhook.ID, 1)
	if err != nil {
		t.Fatalf("ListDeliveries(limit=1) failed: %v", err)
	}
	if len(latestOnly) != 1 {
		t.Fatalf("expected 1 latest delivery, got %d", len(latestOnly))
	}
	if latestOnly[0].ID != secondDelivery.ID {
		t.Fatalf("expected latest delivery id %d, got %d", secondDelivery.ID, latestOnly[0].ID)
	}

	allDeliveries, err := repo.ListDeliveries(ctx, webhook.ID, 10)
	if err != nil {
		t.Fatalf("ListDeliveries(limit=10) failed: %v", err)
	}
	if len(allDeliveries) != 2 {
		t.Fatalf("expected 2 deliveries, got %d", len(allDeliveries))
	}
	if allDeliveries[0].ID != secondDelivery.ID || allDeliveries[1].ID != firstDelivery.ID {
		t.Fatal("expected deliveries to be returned in reverse created_at order")
	}
}
