package db

import (
	"path/filepath"
	"testing"
	"time"

	"github.com/glebarez/sqlite"
	"gorm.io/gorm"
)

type legacyHookDeliveryWithWebhook struct {
	ID           uint       `gorm:"primaryKey;autoIncrement"`
	WebhookID    uint       `gorm:"index;not null"`
	Webhook      Webhook    `gorm:"foreignKey:WebhookID"`
	RepositoryID uint       `gorm:"index;not null"`
	Repository   Repository `gorm:"foreignKey:RepositoryID"`
	GUID         string     `gorm:"size:64;index;not null"`
	Event        string     `gorm:"size:64;index;not null"`
	Status       string     `gorm:"size:32;not null;default:'pending'"`
	CreatedAt    time.Time
	UpdatedAt    time.Time
}

func (legacyHookDeliveryWithWebhook) TableName() string {
	return "hook_deliveries"
}

func openHookDeliveryMigrationDB(t *testing.T) *gorm.DB {
	t.Helper()
	database, err := gorm.Open(sqlite.Open(filepath.Join(t.TempDir(), "hook-delivery-migration.db")+"?_pragma=foreign_keys(1)"), &gorm.Config{})
	if err != nil {
		t.Fatalf("open database: %v", err)
	}
	return database
}

const hookDeliveryWebhookConstraint = "fk_hook_deliveries_webhook"

func TestHookDeliveryFreshSchemaRetainsLiveWebhookConstraint(t *testing.T) {
	database := openHookDeliveryMigrationDB(t)
	if err := database.AutoMigrate(&Repository{}, &Webhook{}, &HookDelivery{}); err != nil {
		t.Fatalf("AutoMigrate: %v", err)
	}
	if !database.Migrator().HasConstraint(&HookDelivery{}, hookDeliveryWebhookConstraint) {
		t.Fatal("fresh hook delivery schema did not retain the live webhook foreign key")
	}
	if !database.Migrator().HasColumn(&HookDelivery{}, "snapshot_version") ||
		!database.Migrator().HasIndex(&HookDelivery{}, "idx_hook_delivery_durable_claim") {
		t.Fatal("fresh hook delivery schema did not create the durable recovery provenance gate")
	}
}

func TestMigrateHookDeliveryAddsSnapshotColumnsWithoutDroppingWebhookConstraint(t *testing.T) {
	database := openHookDeliveryMigrationDB(t)
	if err := database.AutoMigrate(&User{}, &Repository{}, &Webhook{}, &legacyHookDeliveryWithWebhook{}); err != nil {
		t.Fatalf("create legacy schema: %v", err)
	}
	if !database.Migrator().HasConstraint(&legacyHookDeliveryWithWebhook{}, hookDeliveryWebhookConstraint) {
		t.Fatal("legacy fixture did not create the webhook foreign key")
	}
	owner := User{ID: 1, Login: "owner"}
	if err := database.Create(&owner).Error; err != nil {
		t.Fatalf("create repository owner: %v", err)
	}
	repository := Repository{ID: 1, OwnerID: owner.ID, Name: "repo", FullName: "owner/repo", DefaultBranch: "main"}
	if err := database.Create(&repository).Error; err != nil {
		t.Fatalf("create repository: %v", err)
	}
	hook := Webhook{ID: 1, RepositoryID: repository.ID, Name: "web", Active: true}
	if err := database.Create(&hook).Error; err != nil {
		t.Fatalf("create webhook: %v", err)
	}
	legacy := legacyHookDeliveryWithWebhook{WebhookID: hook.ID, RepositoryID: repository.ID, GUID: "legacy-pending", Event: "push", Status: "pending"}
	if err := database.Create(&legacy).Error; err != nil {
		t.Fatalf("create legacy pending delivery: %v", err)
	}
	if err := database.AutoMigrate(&HookDelivery{}); err != nil {
		t.Fatalf("upgrade hook delivery schema: %v", err)
	}
	if !database.Migrator().HasConstraint(&HookDelivery{}, hookDeliveryWebhookConstraint) {
		t.Fatal("upgrade dropped the legacy webhook foreign key")
	}
	for _, column := range []string{"operation_id", "snapshot_version", "request_url", "lease_token"} {
		if !database.Migrator().HasColumn(&HookDelivery{}, column) {
			t.Fatalf("upgrade did not add hook_deliveries.%s", column)
		}
	}
	if !database.Migrator().HasIndex(&HookDelivery{}, "idx_hook_delivery_durable_claim") {
		t.Fatal("upgrade did not add the durable webhook claim index")
	}
	var upgraded HookDelivery
	if err := database.First(&upgraded, legacy.ID).Error; err != nil {
		t.Fatalf("load upgraded legacy delivery: %v", err)
	}
	if upgraded.SnapshotVersion != 0 || upgraded.OperationID != nil || upgraded.OperationEventKey != nil ||
		upgraded.Status != "pending" || upgraded.DeliveredAt != nil || upgraded.LastError != "" || upgraded.LeaseToken != "" || upgraded.Attempts != 0 ||
		!upgraded.UpdatedAt.Equal(legacy.UpdatedAt) {
		t.Fatalf("upgrade changed legacy pending delivery provenance or state: %+v", upgraded)
	}
	if err := database.Delete(&hook).Error; err == nil {
		t.Fatal("upgraded schema allowed deleting a webhook referenced by historical delivery")
	}
}
