// Copyright 2025 The Forgejo Authors. All rights reserved.
// SPDX-License-Identifier: GPL-3.0-or-later
package unittest

import (
	"context"
	"errors"

	"xorm.io/xorm/contexts"
)

var (
	faultInjectorCount      int64
	faultInjectorNumQueries int64 = -1
	ErrFaultInjected              = errors.New("nobody expects a fault injection")
)

type faultInjectorHook struct{}

var _ contexts.Hook = &faultInjectorHook{}

func (faultInjectorHook) BeforeProcess(c *contexts.ContextHook) (context.Context, error) {
	if faultInjectorNumQueries == -1 {
		return c.Ctx, nil
	}

	// Always allow ROLLBACK, we always want to allow for transactions to get cancelled.
	if faultInjectorCount == faultInjectorNumQueries && c.SQL != "ROLLBACK" {
		return c.Ctx, ErrFaultInjected
	}

	faultInjectorCount++

	return c.Ctx, nil
}

func (faultInjectorHook) AfterProcess(*contexts.ContextHook) error {
	return nil
}

// Allow `numQueries` before all database queries will fail until the
// returning function is executed.
func SetFaultInjector(numQueries int64) func() {
	faultInjectorNumQueries = numQueries

	return func() {
		faultInjectorNumQueries = -1
		faultInjectorCount = 0
	}
}
