package database

import (
	"context"
	"testing"
	"time"

	"gorm.io/driver/sqlite"
	"gorm.io/gorm"
	"gorm.io/gorm/logger"
	"goyco/internal/middleware"
)

func TestNewGormDBMonitor(t *testing.T) {
	monitor := middleware.NewInMemoryDBMonitor()
	gormMonitor := NewGormDBMonitor(monitor)

	if gormMonitor.monitor != monitor {
		t.Error("Expected monitor to be set correctly")
	}
}

func TestGormDBMonitor_Name(t *testing.T) {
	monitor := middleware.NewInMemoryDBMonitor()
	gormMonitor := NewGormDBMonitor(monitor)

	if gormMonitor.Name() != "db_monitor" {
		t.Errorf("Expected name 'db_monitor', got '%s'", gormMonitor.Name())
	}
}

func TestGormDBMonitor_Initialize(t *testing.T) {
	monitor := middleware.NewInMemoryDBMonitor()
	gormMonitor := NewGormDBMonitor(monitor)

	db := newTestDB(t)
	defer func() {
		if sqlDB, err := db.DB(); err == nil {
			_ = sqlDB.Close()
		}
	}()

	err := gormMonitor.Initialize(db)
	if err != nil {
		t.Fatalf("Expected Initialize to succeed, got error: %v", err)
	}
}

func TestGormDBMonitor_InitializeWithNilMonitor(t *testing.T) {
	gormMonitor := NewGormDBMonitor(nil)

	db := newTestDB(t)
	defer func() {
		if sqlDB, err := db.DB(); err == nil {
			_ = sqlDB.Close()
		}
	}()

	err := gormMonitor.Initialize(db)
	if err != nil {
		t.Fatalf("Expected Initialize to succeed with nil monitor, got error: %v", err)
	}
}

func TestGormDBMonitor_Callbacks(t *testing.T) {
	monitor := middleware.NewInMemoryDBMonitor()
	gormMonitor := NewGormDBMonitor(monitor)

	db := newTestDB(t)
	defer func() {
		if sqlDB, err := db.DB(); err == nil {
			_ = sqlDB.Close()
		}
	}()

	err := gormMonitor.Initialize(db)
	if err != nil {
		t.Fatalf("Failed to initialize plugin: %v", err)
	}

	user := &User{
		Username:      "testuser",
		Email:         "test@example.com",
		Password:      "hashedpassword",
		EmailVerified: true,
	}

	if err := db.Create(user).Error; err != nil {
		t.Fatalf("Failed to create user: %v", err)
	}

	var foundUser User
	if err := db.First(&foundUser, user.ID).Error; err != nil {
		t.Fatalf("Failed to find user: %v", err)
	}

	foundUser.Username = "updateduser"
	if err := db.Save(&foundUser).Error; err != nil {
		t.Fatalf("Failed to update user: %v", err)
	}

	if err := db.Delete(&foundUser).Error; err != nil {
		t.Fatalf("Failed to delete user: %v", err)
	}
}

func TestGormDBMonitor_CallbacksWithNilMonitor(t *testing.T) {
	gormMonitor := NewGormDBMonitor(nil)

	db := newTestDB(t)
	defer func() {
		if sqlDB, err := db.DB(); err == nil {
			_ = sqlDB.Close()
		}
	}()

	err := gormMonitor.Initialize(db)
	if err != nil {
		t.Fatalf("Failed to initialize plugin: %v", err)
	}

	user := &User{
		Username:      "testuser",
		Email:         "test@example.com",
		Password:      "hashedpassword",
		EmailVerified: true,
	}

	if err := db.Create(user).Error; err != nil {
		t.Fatalf("Failed to create user: %v", err)
	}
}

func TestGormDBMonitor_BuildQueryString(t *testing.T) {
	monitor := middleware.NewInMemoryDBMonitor()
	gormMonitor := NewGormDBMonitor(monitor)

	db := newTestDB(t)
	defer func() {
		if sqlDB, err := db.DB(); err == nil {
			_ = sqlDB.Close()
		}
	}()

	err := gormMonitor.Initialize(db)
	if err != nil {
		t.Fatalf("Failed to initialize plugin: %v", err)
	}

	tests := []struct {
		name      string
		operation string
		table     string
		expected  string
	}{
		{
			name:      "create_operation",
			operation: "CREATE",
			table:     "users",
			expected:  "CREATE FROM users",
		},
		{
			name:      "select_operation",
			operation: "SELECT",
			table:     "posts",
			expected:  "SELECT FROM posts",
		},
		{
			name:      "update_operation",
			operation: "UPDATE",
			table:     "votes",
			expected:  "UPDATE FROM votes",
		},
	}

	for _, tt := range tests {
		t.Run(tt.name, func(t *testing.T) {

			stmt := &gorm.Statement{
				Table: tt.table,
			}

			mockDB := &gorm.DB{
				Statement: stmt,
			}

			result := gormMonitor.buildQueryString(mockDB, tt.operation)
			if result != tt.expected {
				t.Errorf("Expected '%s', got '%s'", tt.expected, result)
			}
		})
	}
}

func TestGormDBMonitor_LogOperation(t *testing.T) {
	monitor := middleware.NewInMemoryDBMonitor()
	gormMonitor := NewGormDBMonitor(monitor)

	startTime := time.Now()
	ctx := context.WithValue(context.Background(), gormOperationStartKey, startTime)

	stmt := &gorm.Statement{
		Context: ctx,
		Table:   "users",
	}

	mockDB := &gorm.DB{
		Statement: stmt,
	}

	gormMonitor.logOperation(mockDB, "CREATE")

	gormMonitor.monitor = nil
	gormMonitor.logOperation(mockDB, "CREATE")
}

func TestGormDBMonitor_LogOperationWithoutStartTime(t *testing.T) {
	monitor := middleware.NewInMemoryDBMonitor()
	gormMonitor := NewGormDBMonitor(monitor)

	ctx := context.Background()

	stmt := &gorm.Statement{
		Context: ctx,
		Table:   "users",
	}

	mockDB := &gorm.DB{
		Statement: stmt,
	}

	gormMonitor.logOperation(mockDB, "CREATE")
}

func TestGormDBMonitor_AllCallbackMethods(t *testing.T) {
	monitor := middleware.NewInMemoryDBMonitor()
	gormMonitor := NewGormDBMonitor(monitor)

	gormMonitor.monitor = nil

	ctx := context.Background()
	stmt := &gorm.Statement{
		Context: ctx,
		Table:   "users",
	}

	mockDB := &gorm.DB{
		Statement: stmt,
	}

	gormMonitor.beforeCreate(mockDB)
	gormMonitor.beforeQuery(mockDB)
	gormMonitor.beforeUpdate(mockDB)
	gormMonitor.beforeDelete(mockDB)
	gormMonitor.beforeRow(mockDB)
	gormMonitor.beforeRaw(mockDB)

	gormMonitor.afterCreate(mockDB)
	gormMonitor.afterQuery(mockDB)
	gormMonitor.afterUpdate(mockDB)
	gormMonitor.afterDelete(mockDB)
	gormMonitor.afterRow(mockDB)
	gormMonitor.afterRaw(mockDB)
}

func TestGormDBMonitor_WithRealDatabase(t *testing.T) {
	monitor := middleware.NewInMemoryDBMonitor()
	gormMonitor := NewGormDBMonitor(monitor)

	dbName := "file:memdb_" + t.Name() + "?mode=memory&cache=private"
	db, err := gorm.Open(sqlite.Open(dbName), &gorm.Config{
		Logger: logger.Default.LogMode(logger.Silent),
	})
	if err != nil {
		t.Fatalf("Failed to open database: %v", err)
	}
	defer func() {
		if sqlDB, err := db.DB(); err == nil {
			_ = sqlDB.Close()
		}
	}()

	if err := db.AutoMigrate(&User{}); err != nil {
		t.Fatalf("Failed to migrate database: %v", err)
	}

	err = gormMonitor.Initialize(db)
	if err != nil {
		t.Fatalf("Failed to initialize plugin: %v", err)
	}

	user := &User{
		Username:      "testuser",
		Email:         "test@example.com",
		Password:      "hashedpassword",
		EmailVerified: true,
	}

	if err := db.Create(user).Error; err != nil {
		t.Fatalf("Failed to create user: %v", err)
	}

	var foundUser User
	if err := db.First(&foundUser, user.ID).Error; err != nil {
		t.Fatalf("Failed to find user: %v", err)
	}

	foundUser.Username = "updateduser"
	if err := db.Save(&foundUser).Error; err != nil {
		t.Fatalf("Failed to update user: %v", err)
	}

	if err := db.Delete(&foundUser).Error; err != nil {
		t.Fatalf("Failed to delete user: %v", err)
	}

	stats := monitor.GetStats()
	if stats.TotalQueries == 0 {
		t.Error("Expected monitor to have recorded some queries")
	}
}
