package br

import (
	"context"
	"testing"
	"time"

	sqlmock "github.com/DATA-DOG/go-sqlmock"
	"github.com/stretchr/testify/require"
	"gorm.io/driver/mysql"
	"gorm.io/gorm"
	"gorm.io/gorm/logger"

	brrepo "github.com/tidbcloud/tidb-management-service/nextgen-global-service/domain/repository/br"
	"github.com/tidbcloud/tidb-management-service/nextgen-global-service/infra/repoimpl/schema"
)

func TestBackupRepoUnscopedQueryDefaultOrderByCreatedAtDesc(t *testing.T) {
	db, mock := newBackupRepoMockDB(t)
	repo := &backupRepoImpl{provider: backupRepoTestProvider{db: db}}

	mock.ExpectQuery("SELECT \\* FROM `backups` ORDER BY created_at DESC").
		WillReturnRows(sqlmock.NewRows([]string{"id"}))

	_, err := repo.UnscopedQuery(context.Background(), &brrepo.QueryBackupsFilter{}, nil)
	require.NoError(t, err)
	require.NoError(t, mock.ExpectationsWereMet())
}

func TestBackupRepoUnscopedQueryExplicitOrderByOverridesDefault(t *testing.T) {
	db, mock := newBackupRepoMockDB(t)
	repo := &backupRepoImpl{provider: backupRepoTestProvider{db: db}}
	orderBy := "backup_time DESC, id DESC"

	mock.ExpectQuery("SELECT \\* FROM `backups` ORDER BY backup_time DESC, id DESC").
		WillReturnRows(sqlmock.NewRows([]string{"id"}))

	_, err := repo.UnscopedQuery(context.Background(), &brrepo.QueryBackupsFilter{OrderBy: &orderBy}, nil)
	require.NoError(t, err)
	require.NoError(t, mock.ExpectationsWereMet())
}

func TestBackupRepoUnscopedQueryExcludesExpiredAutomaticBackups(t *testing.T) {
	db, mock := newBackupRepoMockDB(t)
	repo := &backupRepoImpl{provider: backupRepoTestProvider{db: db}}
	now := time.Date(2026, time.July, 31, 0, 0, 0, 0, time.UTC)

	mock.ExpectQuery("SELECT \\* FROM `backups` WHERE \\(trigger_type != \\? OR expiration_time > \\?\\) ORDER BY created_at DESC").
		WithArgs(schema.BackupTriggerTypeAuto, now).
		WillReturnRows(sqlmock.NewRows([]string{"id"}))

	_, err := repo.UnscopedQuery(context.Background(), &brrepo.QueryBackupsFilter{
		ExcludeExpiredAutoBackupsAt: &now,
	}, nil)
	require.NoError(t, err)
	require.NoError(t, mock.ExpectationsWereMet())
}

func TestBackupRepoUnscopedQueryFiltersAutoBackupTypes(t *testing.T) {
	db, mock := newBackupRepoMockDB(t)
	repo := &backupRepoImpl{provider: backupRepoTestProvider{db: db}}

	mock.ExpectQuery("SELECT \\* FROM `backups` WHERE auto_backup_type IN \\(\\?\\) ORDER BY created_at DESC").
		WithArgs(schema.AutoBackupTypeDaily).
		WillReturnRows(sqlmock.NewRows([]string{"id"}))

	_, err := repo.UnscopedQuery(context.Background(), &brrepo.QueryBackupsFilter{
		AutoBackupTypes: []schema.AutoBackupType{schema.AutoBackupTypeDaily},
	}, nil)
	require.NoError(t, err)
	require.NoError(t, mock.ExpectationsWereMet())
}

func TestNormalizeAutoBackupType(t *testing.T) {
	tests := []struct {
		name         string
		backup       *schema.Backup
		expectedType schema.AutoBackupType
	}{
		{
			name:         "empty type becomes unknown",
			backup:       &schema.Backup{},
			expectedType: schema.AutoBackupTypeUnknown,
		},
		{
			name:         "daily type is preserved",
			backup:       &schema.Backup{AutoBackupType: schema.AutoBackupTypeDaily},
			expectedType: schema.AutoBackupTypeDaily,
		},
	}

	for _, tt := range tests {
		t.Run(tt.name, func(t *testing.T) {
			normalizeAutoBackupType(tt.backup)
			require.Equal(t, tt.expectedType, tt.backup.AutoBackupType)
		})
	}
}

type backupRepoTestProvider struct {
	db *gorm.DB
}

func (p backupRepoTestProvider) ReadDB(context.Context) *gorm.DB {
	return p.db
}

func (p backupRepoTestProvider) WriteDB(context.Context) *gorm.DB {
	return p.db
}

func newBackupRepoMockDB(t *testing.T) (*gorm.DB, sqlmock.Sqlmock) {
	t.Helper()

	sqlDB, mock, err := sqlmock.New(sqlmock.QueryMatcherOption(sqlmock.QueryMatcherRegexp))
	require.NoError(t, err)
	t.Cleanup(func() {
		_ = sqlDB.Close()
	})

	db, err := gorm.Open(mysql.New(mysql.Config{
		Conn:                      sqlDB,
		SkipInitializeWithVersion: true,
	}), &gorm.Config{
		SkipDefaultTransaction: true,
		Logger:                 logger.Default.LogMode(logger.Silent),
	})
	require.NoError(t, err)
	return db, mock
}
