package packstate

import (
	"context"
	"database/sql/driver"
	"fmt"
	"strings"
	"testing"

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

func TestListConnectorPacksForInboundWorkersIncludesWorkItemBacklog(t *testing.T) {
	db, mock, err := sqlmock.New(sqlmock.QueryMatcherOption(sqlmock.QueryMatcherFunc(func(expectedSQL, actualSQL string) error {
		if !strings.Contains(actualSQL, "_System_ConnectorWorkItem") {
			return fmt.Errorf("worker pack lister must include WorkItem backlog predicate: %s", actualSQL)
		}
		if !strings.Contains(actualSQL, "'processing'") {
			return fmt.Errorf("worker pack lister must keep lease-free dependency suspensions draining: %s", actualSQL)
		}
		activeGuard := strings.Index(actualSQL, "FROM _System_InstalledPack active_pack")
		historicalGuard := strings.Index(actualSQL, "FROM _System_InstalledPack other_historical_pack")
		workItemBacklog := strings.Index(actualSQL, "FROM _System_ConnectorWorkItem w")
		if activeGuard < 0 || historicalGuard < 0 || workItemBacklog < 0 || workItemBacklog < historicalGuard {
			return fmt.Errorf("WorkItem backlog predicate must be inside historical drain-only sibling guards: %s", actualSQL)
		}
		return nil
	})))
	if err != nil {
		t.Fatalf("sqlmock.New: %v", err)
	}
	defer db.Close()

	mock.ExpectQuery("worker pack lister").
		WithArgs(
			InstalledPackStatusActive,
			string(ConnectorPack),
			InstalledPackStatusActive,
			InstalledPackStatusActive,
			InstalledPackStatusActive,
			InstalledPackStatusActive,
		).
		WillReturnRows(sqlmock.NewRows([]string{
			"Id",
			"PackInstallId",
			"Name",
			"Category",
			"Status",
			"AppliedPayloadHash",
			"DrainOnly",
		}).AddRow("pack-row-1", "install-with-workitems", "connector-pack", string(ConnectorPack), "inactive", "", 1))

	rows, err := ListConnectorPacksForInboundWorkers(context.Background(), db)
	if err != nil {
		t.Fatalf("ListConnectorPacksForInboundWorkers: %v", err)
	}
	if len(rows) != 1 {
		t.Fatalf("rows len = %d, want 1", len(rows))
	}
	if rows[0].InstallID != "install-with-workitems" || !rows[0].DrainOnly {
		t.Fatalf("row = %+v, want drain-only WorkItem backlog pack", rows[0])
	}
	if err := mock.ExpectationsWereMet(); err != nil {
		t.Fatalf("sqlmock expectations: %v", err)
	}
}

func TestListConnectorPacksForInboundWorkersFallsBackWhenWorkItemTableMissing(t *testing.T) {
	queryCount := 0
	db, mock, err := sqlmock.New(sqlmock.QueryMatcherOption(sqlmock.QueryMatcherFunc(func(expectedSQL, actualSQL string) error {
		queryCount++
		switch queryCount {
		case 1:
			if !strings.Contains(actualSQL, "_System_ConnectorWorkItem") {
				return fmt.Errorf("first query must probe WorkItem backlog predicate: %s", actualSQL)
			}
		case 2:
			if strings.Contains(actualSQL, "_System_ConnectorWorkItem") {
				return fmt.Errorf("fallback query must omit WorkItem backlog predicate: %s", actualSQL)
			}
		default:
			return fmt.Errorf("unexpected query %d: %s", queryCount, actualSQL)
		}
		return nil
	})))
	if err != nil {
		t.Fatalf("sqlmock.New: %v", err)
	}
	defer db.Close()

	args := []driver.Value{
		InstalledPackStatusActive,
		string(ConnectorPack),
		InstalledPackStatusActive,
		InstalledPackStatusActive,
		InstalledPackStatusActive,
		InstalledPackStatusActive,
	}
	mock.ExpectQuery("worker pack lister").WithArgs(args...).
		WillReturnError(fmt.Errorf("Error 1146: Table 'nexuscrm._System_ConnectorWorkItem' doesn't exist"))
	mock.ExpectQuery("worker pack lister fallback").WithArgs(args...).
		WillReturnRows(sqlmock.NewRows([]string{
			"Id",
			"PackInstallId",
			"Name",
			"Category",
			"Status",
			"AppliedPayloadHash",
			"DrainOnly",
		}).AddRow("pack-row-1", "install-legacy", "connector-pack", string(ConnectorPack), InstalledPackStatusActive, "hash", 0))

	rows, err := ListConnectorPacksForInboundWorkers(context.Background(), db)
	if err != nil {
		t.Fatalf("ListConnectorPacksForInboundWorkers: %v", err)
	}
	if len(rows) != 1 || rows[0].InstallID != "install-legacy" || rows[0].DrainOnly {
		t.Fatalf("rows = %+v, want active legacy pack from fallback query", rows)
	}
	if err := mock.ExpectationsWereMet(); err != nil {
		t.Fatalf("sqlmock expectations: %v", err)
	}
}
