package packs

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

	"github.com/DATA-DOG/go-sqlmock"
	"github.com/nexuscrm/shared/pkg/constants"
	seedrows "github.com/pingcap-inc/nexuscrm-contract/pkg/seedrows"
)

func TestSeedSystemPermissionRowsRefusesForeignOwnerBeforeWrite(t *testing.T) {
	tests := []struct {
		name     string
		isActive int
	}{
		{name: "active foreign owner", isActive: 1},
		{name: "inactive foreign owner", isActive: 0},
	}

	for _, tc := range tests {
		t.Run(tc.name, func(t *testing.T) {
			db, mock, err := sqlmock.New()
			if err != nil {
				t.Fatalf("sqlmock.New: %v", err)
			}
			defer db.Close()

			pack := []seedrows.StandardPermissionRow{
				permissionRow("ViewSetup"),
			}
			expectPermissionOwnerPreflight(mock, []string{"ViewSetup"},
				sqlmock.NewRows([]string{"ApiName", "OwnerPack", "IsActive"}).
					AddRow("ViewSetup", "other-pack", tc.isActive),
			)

			err = seedSystemPermissionRows(context.Background(), db, pack, false, "platform-policy")
			if err == nil {
				t.Fatal("seedSystemPermissionRows() error = nil, want foreign OwnerPack refusal")
			}
			for _, want := range []string{`permission ViewSetup`, `OwnerPack = "other-pack"`, `want "platform-policy"`} {
				if !strings.Contains(err.Error(), want) {
					t.Fatalf("error = %v, want substring %q", err, want)
				}
			}
			if err := mock.ExpectationsWereMet(); err != nil {
				t.Fatalf("sql expectations: %v", err)
			}
		})
	}
}

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

	pack := []seedrows.StandardPermissionRow{
		permissionRow("BrandNewPermission"),
		permissionRow("SharedPermission"),
	}
	expectPermissionOwnerPreflight(mock, []string{"BrandNewPermission", "SharedPermission"},
		sqlmock.NewRows([]string{"ApiName", "OwnerPack", "IsActive"}).
			AddRow("SharedPermission", "other-pack", 1),
	)

	err = seedSystemPermissionRows(context.Background(), db, pack, true, "platform-policy")
	if err == nil {
		t.Fatal("seedSystemPermissionRows() error = nil, want foreign OwnerPack refusal")
	}
	if !strings.Contains(err.Error(), `permission SharedPermission`) {
		t.Fatalf("error = %v, want conflicting permission detail", err)
	}
	if err := mock.ExpectationsWereMet(); err != nil {
		t.Fatalf("sql expectations: %v", err)
	}
}

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

	pack := []seedrows.StandardPermissionRow{
		permissionRow("ViewSetup"),
	}
	expectPermissionOwnerPreflight(mock, []string{"ViewSetup"},
		sqlmock.NewRows([]string{"ApiName", "OwnerPack", "IsActive"}).
			AddRow("ViewSetup", "platform-policy", 0),
	)
	expectPermissionCatalogUpsert(mock)
	expectNoDefaultProfilePermissionSets(mock)

	if err := seedSystemPermissionRows(context.Background(), db, pack, false, "platform-policy"); err != nil {
		t.Fatalf("seedSystemPermissionRows() error = %v, want same-owner inactive row revived", err)
	}
	if err := mock.ExpectationsWereMet(); err != nil {
		t.Fatalf("sql expectations: %v", err)
	}
}

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

	pack := []seedrows.StandardPermissionRow{
		permissionRow("BrandNewPermission"),
	}
	expectPermissionOwnerPreflight(mock, []string{"BrandNewPermission"},
		sqlmock.NewRows([]string{"ApiName", "OwnerPack", "IsActive"}),
	)
	expectPermissionCatalogUpsert(mock)
	expectNoDefaultProfilePermissionSets(mock)

	if err := seedSystemPermissionRows(context.Background(), db, pack, false, "platform-policy"); err != nil {
		t.Fatalf("seedSystemPermissionRows() error = %v, want new permission allowed", err)
	}
	if err := mock.ExpectationsWereMet(); err != nil {
		t.Fatalf("sql expectations: %v", err)
	}
}

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

	pack := []seedrows.StandardPermissionRow{
		permissionRow("Manage"),
	}
	expectPermissionOwnerPreflight(mock, []string{"Manage"},
		sqlmock.NewRows([]string{"ApiName", "OwnerPack", "IsActive"}),
	)
	expectPermissionCatalogUpsert(mock)
	expectNoDefaultProfilePermissionSets(mock)

	if err := seedSystemPermissionRows(context.Background(), db, pack, false, "platform-policy"); err != nil {
		t.Fatalf("seedSystemPermissionRows() error = %v, want prefix-only collision ignored", err)
	}
	if err := mock.ExpectationsWereMet(); err != nil {
		t.Fatalf("sql expectations: %v", err)
	}
}

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

	payload := []byte(`[{"apiName":"RunReports","label":"Run Reports","category":"System","description":"Run Reports permission"}]`)
	component, err := newSourceBackedPermissionComponent("platform-policy", ManifestComponent{
		Kind:        ComponentKindPermission,
		BusinessKey: "feature-permissions",
		Payload:     payload,
	}, payload)
	if err != nil {
		t.Fatalf("newSourceBackedPermissionComponent: %v", err)
	}

	expectPermissionOwnerPreflight(mock, []string{"RunReports"},
		sqlmock.NewRows([]string{"ApiName", "OwnerPack", "IsActive"}).
			AddRow("RunReports", "other-pack", 1),
	)

	err = component.Apply(context.Background(), ApplyDeps{DB: db})
	if err == nil {
		t.Fatal("source-backed permission Apply() error = nil, want foreign OwnerPack refusal")
	}
	if !strings.Contains(err.Error(), `permission RunReports OwnerPack = "other-pack"`) {
		t.Fatalf("error = %v, want foreign OwnerPack detail", err)
	}
	if err := mock.ExpectationsWereMet(); err != nil {
		t.Fatalf("sql expectations: %v", err)
	}
}

func permissionRow(apiName string) seedrows.StandardPermissionRow {
	return seedrows.StandardPermissionRow{
		APIName:     apiName,
		Label:       apiName,
		Category:    "System",
		Description: apiName + " permission",
	}
}

func expectPermissionOwnerPreflight(mock sqlmock.Sqlmock, apiNames []string, rows *sqlmock.Rows) {
	placeholders := strings.TrimRight(strings.Repeat("?,", len(apiNames)), ",")
	query := regexp.QuoteMeta(
		"SELECT `ApiName`, COALESCE(`OwnerPack`, ''), `IsActive` FROM `_System_Permission` WHERE `ApiName` IN (" + placeholders + ") ORDER BY `ApiName`",
	)
	args := make([]driver.Value, 0, len(apiNames))
	for _, apiName := range apiNames {
		args = append(args, apiName)
	}
	mock.ExpectQuery(query).WithArgs(args...).WillReturnRows(rows)
}

func expectPermissionCatalogUpsert(mock sqlmock.Sqlmock) {
	mock.ExpectExec(regexp.QuoteMeta("INSERT INTO `_System_Permission`")).
		WillReturnResult(sqlmock.NewResult(0, 1))
}

func expectNoDefaultProfilePermissionSets(mock sqlmock.Sqlmock) {
	query := regexp.QuoteMeta("SELECT `Id` FROM `PermissionSet` WHERE `ProfileId` = ? AND `IsOwnedByProfile` = 1 LIMIT 1")
	mock.ExpectQuery(query).WithArgs(constants.ProfileSystemAdmin).WillReturnError(sql.ErrNoRows)
	mock.ExpectQuery(query).WithArgs(constants.ProfileStandardUser).WillReturnError(sql.ErrNoRows)
}
