package controlplane

import (
	"context"
	"database/sql"
	"regexp"
	"testing"

	"github.com/DATA-DOG/go-sqlmock"
)

func expectScheduledRunReservationStampNotPresent(mock sqlmock.Sqlmock) {
	mock.ExpectQuery(regexp.QuoteMeta("SELECT 1 FROM _System_Migration WHERE Id = ?")).
		WithArgs(migrationIdBackfillScheduledRunReservationCompatibility).
		WillReturnRows(sqlmock.NewRows([]string{"_"}))
}

func expectScheduledRunReservationStampPresent(mock sqlmock.Sqlmock) {
	mock.ExpectQuery(regexp.QuoteMeta("SELECT 1 FROM _System_Migration WHERE Id = ?")).
		WithArgs(migrationIdBackfillScheduledRunReservationCompatibility).
		WillReturnRows(sqlmock.NewRows([]string{"_"}).AddRow(1))
}

func expectScheduledRunReservationStampInsert(mock sqlmock.Sqlmock) *sqlmock.ExpectedExec {
	return mock.ExpectExec(regexp.QuoteMeta("INSERT INTO _System_Migration")).
		WithArgs(
			migrationIdBackfillScheduledRunReservationCompatibility,
			1,
			sqlmock.AnyArg(),
		)
}

func expectScheduledRunReservationClosureCheck(mock sqlmock.Sqlmock, remaining int) {
	mock.ExpectQuery(regexp.QuoteMeta(`
SELECT COUNT(*)
FROM _System_ScheduledRunReservation
WHERE (` + "`WindowToken`" + ` IS NULL OR TRIM(` + "`WindowToken`" + `) = '')
   OR (` + "`Status`" + ` IS NULL OR TRIM(` + "`Status`" + `) = '')
   OR ` + "`ReservedAt`" + ` IS NULL`)).
		WillReturnRows(sqlmock.NewRows([]string{"count"}).AddRow(remaining))
}

func TestBackfillScheduledRunReservationCompatibility_StampGuardSkipsWhenApplied(t *testing.T) {
	db, mock, err := sqlmock.New()
	if err != nil {
		t.Fatalf("sqlmock.New: %v", err)
	}
	defer db.Close()

	expectScheduledRunReservationStampPresent(mock)

	touched, err := BackfillScheduledRunReservationCompatibility(context.Background(), db)
	if err != nil {
		t.Fatalf("BackfillScheduledRunReservationCompatibility = error %v, want nil", err)
	}
	if touched != 0 {
		t.Fatalf("BackfillScheduledRunReservationCompatibility touched = %d, want 0 on stamped migration", touched)
	}
	if err := mock.ExpectationsWereMet(); err != nil {
		t.Fatalf("sql expectations unmet: %v", err)
	}
}

func TestBackfillScheduledRunReservationCompatibility_SQLShape(t *testing.T) {
	db, mock, err := sqlmock.New()
	if err != nil {
		t.Fatalf("sqlmock.New: %v", err)
	}
	defer db.Close()

	expectScheduledRunReservationStampNotPresent(mock)
	mock.ExpectQuery(regexp.QuoteMeta(`
SELECT COUNT(*)
FROM INFORMATION_SCHEMA.COLUMNS
WHERE TABLE_SCHEMA = DATABASE()
  AND TABLE_NAME = '_System_ScheduledRunReservation'
  AND COLUMN_NAME IN ('ScheduleWindowKey', 'WindowToken', 'State', 'Status', 'ReservedAt', 'CreatedDate')`)).
		WillReturnRows(sqlmock.NewRows([]string{"count"}).AddRow(6))
	expected := regexp.MustCompile(
		`(?s)UPDATE _System_ScheduledRunReservation.*` +
			"`WindowToken` = CASE.*`ScheduleWindowKey`.*" +
			"`Status` = CASE.*LOWER\\(TRIM\\(`State`\\)\\).*" +
			"`ReservedAt` = CASE.*`CreatedDate`.*")
	mock.ExpectExec(expected.String()).
		WillReturnResult(sqlmock.NewResult(0, 2))
	expectScheduledRunReservationClosureCheck(mock, 0)
	expectScheduledRunReservationStampInsert(mock).WillReturnResult(sqlmock.NewResult(1, 1))

	touched, err := BackfillScheduledRunReservationCompatibility(context.Background(), db)
	if err != nil {
		t.Fatalf("BackfillScheduledRunReservationCompatibility = error %v, want nil", err)
	}
	if touched != 2 {
		t.Fatalf("BackfillScheduledRunReservationCompatibility touched = %d, want 2", touched)
	}
	if err := mock.ExpectationsWereMet(); err != nil {
		t.Fatalf("sql expectations unmet: %v", err)
	}
}

func TestBackfillScheduledRunReservationCompatibility_StampInsertFailureReturnsError(t *testing.T) {
	db, mock, err := sqlmock.New()
	if err != nil {
		t.Fatalf("sqlmock.New: %v", err)
	}
	defer db.Close()

	expectScheduledRunReservationStampNotPresent(mock)
	mock.ExpectQuery(regexp.QuoteMeta(`
SELECT COUNT(*)
FROM INFORMATION_SCHEMA.COLUMNS
WHERE TABLE_SCHEMA = DATABASE()
  AND TABLE_NAME = '_System_ScheduledRunReservation'
  AND COLUMN_NAME IN ('ScheduleWindowKey', 'WindowToken', 'State', 'Status', 'ReservedAt', 'CreatedDate')`)).
		WillReturnRows(sqlmock.NewRows([]string{"count"}).AddRow(6))
	mock.ExpectExec(regexp.QuoteMeta("UPDATE _System_ScheduledRunReservation")).
		WillReturnResult(sqlmock.NewResult(0, 1))
	expectScheduledRunReservationClosureCheck(mock, 0)
	expectScheduledRunReservationStampInsert(mock).WillReturnError(sql.ErrConnDone)

	touched, err := BackfillScheduledRunReservationCompatibility(context.Background(), db)
	if err == nil {
		t.Fatal("expected error from stamp insert failure, got nil")
	}
	if touched != 1 {
		t.Fatalf("BackfillScheduledRunReservationCompatibility touched = %d, want 1", touched)
	}
	if err := mock.ExpectationsWereMet(); err != nil {
		t.Fatalf("sql expectations unmet: %v", err)
	}
}
