package packs

import (
	"context"
	"strings"
	"testing"

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

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

	packName := "oauth-client-pack"
	declared := []sourceBackedConnectedAppDeclarationRow{{
		ID:                sourceBackedConnectedAppDeclarationID(packName, "kept-client"),
		InstalledPackName: packName,
		ConnectedAppID:    "kept-client",
		PayloadHash:       "kept-hash",
		IsDeleted:         0,
	}}
	stale := sourceBackedConnectedAppDeclarationRow{
		ID:                sourceBackedConnectedAppDeclarationID(packName, "stale-client"),
		InstalledPackName: packName,
		ConnectedAppID:    "stale-client",
		PayloadHash:       "stale-hash",
		IsDeleted:         0,
	}

	mock.ExpectQuery("SELECT Id, InstalledPackName, ConnectedAppId, PayloadHash").
		WithArgs(packName).
		WillReturnRows(sqlmock.NewRows([]string{
			"Id",
			"InstalledPackName",
			"ConnectedAppId",
			"PayloadHash",
			"IsDeleted",
		}).
			AddRow(declared[0].ID, packName, "kept-client", "kept-hash", 0).
			AddRow(stale.ID, packName, "stale-client", "stale-hash", 0))
	mock.ExpectExec("UPDATE _System_ConnectedAppStage").
		WithArgs(stale.PayloadHash, nil, 1, stale.ID, packName, "stale-client").
		WillReturnResult(sqlmock.NewResult(0, 0))
	expectConnectedAppStageExists(mock, sourceBackedConnectedAppStageRow{
		ID:                stale.ID,
		InstalledPackName: packName,
		ConnectedAppID:    "stale-client",
	}, false)
	mock.ExpectExec("INSERT INTO _System_ConnectedAppStage").
		WithArgs(stale.ID, packName, "stale-client", stale.PayloadHash, nil, 1).
		WillReturnResult(sqlmock.NewResult(1, 1))

	if err := applySourceBackedConnectedAppPackSweep(context.Background(), ApplyDeps{DB: db}, packName, declared); err != nil {
		t.Fatalf("applySourceBackedConnectedAppPackSweep: %v", err)
	}
	if err := mock.ExpectationsWereMet(); err != nil {
		t.Fatalf("sqlmock expectations: %v", err)
	}
}

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

	packName := "oauth-client-pack"
	staleID := sourceBackedConnectedAppDeclarationID(packName, "stale-client")
	mock.ExpectQuery("SELECT Id, InstalledPackName, ConnectedAppId, PayloadHash").
		WithArgs(packName).
		WillReturnRows(sqlmock.NewRows([]string{
			"Id",
			"InstalledPackName",
			"ConnectedAppId",
			"PayloadHash",
			"IsDeleted",
		}).AddRow(staleID, packName, "stale-client", "stale-hash", 0))
	expectConnectedAppPackStatus(mock, packName, "active")

	err = verifySourceBackedConnectedAppPackSweep(context.Background(), db, packName, nil)
	if err == nil {
		t.Fatal("verifySourceBackedConnectedAppPackSweep accepted undeclared live declaration")
	}
	if !strings.Contains(err.Error(), "undeclared connected_app declaration") {
		t.Fatalf("verify error = %v, want undeclared connected_app declaration", err)
	}
	if err := mock.ExpectationsWereMet(); err != nil {
		t.Fatalf("sqlmock expectations: %v", err)
	}
}

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

	packName := "oauth-client-pack"
	staleID := sourceBackedConnectedAppDeclarationID(packName, "stale-client")
	mock.ExpectQuery("SELECT Id, InstalledPackName, ConnectedAppId, PayloadHash").
		WithArgs(packName).
		WillReturnRows(sqlmock.NewRows([]string{
			"Id",
			"InstalledPackName",
			"ConnectedAppId",
			"PayloadHash",
			"IsDeleted",
		}).AddRow(staleID, packName, "stale-client", "stale-hash", 0))
	expectConnectedAppPackStatus(mock, packName, "installing")
	expectConnectedAppStageQuery(mock, sourceBackedConnectedAppStageRow{
		ID:                staleID,
		InstalledPackName: packName,
		ConnectedAppID:    "stale-client",
		PayloadHash:       "stale-hash",
		IsDeleted:         1,
	})

	if err := verifySourceBackedConnectedAppPackSweep(context.Background(), db, packName, nil); err != nil {
		t.Fatalf("verifySourceBackedConnectedAppPackSweep: %v", err)
	}
	if err := mock.ExpectationsWereMet(); err != nil {
		t.Fatalf("sqlmock expectations: %v", err)
	}
}
