package orderLogic

import (
	"context"
	"fmt"
	"strings"
	"testing"

	"github.com/DATA-DOG/go-sqlmock"
	"github.com/perfect-panel/server/internal/svc"
	"gorm.io/driver/mysql"
	"gorm.io/gorm"
)

func TestReleaseClaimOnlyReleasesClaimedStatus(t *testing.T) {
	const orderNo = "ORD-CLAIMED-1"

	db, mock, cleanup := newOrderQueueTestDB(t)
	defer cleanup()

	mock.ExpectBegin()
	mock.ExpectExec("UPDATE `order`").
		WithArgs(OrderStatusPaid, sqlmock.AnyArg(), orderNo, OrderStatusClaimed).
		WillReturnResult(sqlmock.NewResult(0, 1))
	mock.ExpectCommit()

	logic := NewActivateOrderLogic(&svc.ServiceContext{DB: db})
	if err := logic.releaseClaim(context.Background(), orderNo); err != nil {
		t.Fatalf("releaseClaim error: %v", err)
	}
	assertOrderQueueExpectations(t, mock)
}

func TestStuckOrderRecoveryScansOnlyClaimedStatus(t *testing.T) {
	db, mock, cleanup := newOrderQueueTestDB(t)
	defer cleanup()

	mock.ExpectQuery("FROM `order`").
		WithArgs(OrderStatusClaimed, sqlmock.AnyArg()).
		WillReturnRows(sqlmock.NewRows([]string{"id", "order_no", "status"}))

	logic := NewStuckOrderRecoveryLogic(&svc.ServiceContext{DB: db})
	if err := logic.ProcessTask(context.Background(), nil); err != nil {
		t.Fatalf("ProcessTask error: %v", err)
	}
	assertOrderQueueExpectations(t, mock)
}

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

	sqlDB, mock, err := sqlmock.New(sqlmock.QueryMatcherOption(sqlmock.QueryMatcherFunc(func(expectedSQL, actualSQL string) error {
		if strings.Contains(actualSQL, expectedSQL) {
			return nil
		}
		return fmt.Errorf("actual sql %q does not contain %q", actualSQL, expectedSQL)
	})))
	if err != nil {
		t.Fatalf("create sqlmock: %v", err)
	}

	db, err := gorm.Open(mysql.New(mysql.Config{Conn: sqlDB, SkipInitializeWithVersion: true}), &gorm.Config{})
	if err != nil {
		_ = sqlDB.Close()
		t.Fatalf("open gorm db: %v", err)
	}

	return db, mock, func() {
		_ = sqlDB.Close()
	}
}

func assertOrderQueueExpectations(t *testing.T, mock sqlmock.Sqlmock) {
	t.Helper()
	if err := mock.ExpectationsWereMet(); err != nil {
		t.Fatalf("unmet sql expectations: %v", err)
	}
}
