package store

import (
	"context"
	"errors"
	"time"

	"github.com/jackc/pgx/v5"
	"github.com/jackc/pgx/v5/pgconn"
)

const transactionRollbackTimeout = 5 * time.Second

type Tx interface {
	Exec(ctx context.Context, sql string, arguments ...any) (pgconn.CommandTag, error)
	QueryRow(ctx context.Context, sql string, args ...any) pgx.Row
}

func (s *Store) InTx(ctx context.Context, fn func(Tx) error) error {
	return s.beginTransaction(ctx, func(tx pgx.Tx) error {
		return fn(tx)
	})
}

func (s *Store) beginTransaction(ctx context.Context, fn func(pgx.Tx) error) (err error) {
	tx, err := s.pool.Begin(ctx)
	if err != nil {
		return err
	}
	// A Worker shutdown cancels ctx before in-flight callbacks return. Use an
	// independent bounded context so rollback still reaches PostgreSQL.
	defer func() {
		rollbackCtx, cancel := context.WithTimeout(context.Background(), transactionRollbackTimeout)
		defer cancel()
		rollbackErr := tx.Rollback(rollbackCtx)
		if err == nil && rollbackErr != nil && !errors.Is(rollbackErr, pgx.ErrTxClosed) {
			err = rollbackErr
		}
	}()
	if err = fn(tx); err != nil {
		return err
	}
	return tx.Commit(ctx)
}

func rollbackTransaction(tx pgx.Tx) {
	if tx == nil {
		return
	}
	ctx, cancel := context.WithTimeout(context.Background(), transactionRollbackTimeout)
	defer cancel()
	_ = tx.Rollback(ctx)
}
