package bootstrap

import (
	"testing"

	"github.com/stretchr/testify/assert"
	"github.com/stretchr/testify/require"
)

// These tests pin the contract of the shipped permission pack. Since the
// pack is the sole source of truth for platform-shipped permission rows
// after the refactor (replacing the hardcoded AllSystemPermissions Go
// function), a regression here would silently ship a broken platform —
// e.g. a typo in an apiName, a dangling `requires` reference, or an
// audience name the seeder can't route to a profile.

func TestStandardPermissions_PackParses(t *testing.T) {
	rows, err := LoadStandardPermissions()
	require.NoError(t, err)
	assert.Greater(t, len(rows), 50, "platform ships well over 50 permissions — a dramatic count drop likely means the pack was truncated")
}

func TestStandardPermissions_EveryRowIsCoherent(t *testing.T) {
	rows, err := LoadStandardPermissions()
	require.NoError(t, err)

	for i, row := range rows {
		assert.NotEmpty(t, row.APIName, "row %d: apiName must be set", i)
		assert.NotEmpty(t, row.Label, "row %d (%s): label must be set", i, row.APIName)
		assert.NotEmpty(t, row.Category, "row %d (%s): category must be set", i, row.APIName)
		assert.NotEmpty(t, row.Description, "row %d (%s): description must be set", i, row.APIName)
	}
}

func TestStandardPermissions_APINamesAreUnique(t *testing.T) {
	rows, err := LoadStandardPermissions()
	require.NoError(t, err)

	seen := map[string]int{}
	for i, row := range rows {
		if prev, ok := seen[row.APIName]; ok {
			t.Errorf("duplicate apiName %q at rows %d and %d", row.APIName, prev, i)
		}
		seen[row.APIName] = i
	}
}

func TestStandardPermissions_RequiresChainsResolveToShippedPermissions(t *testing.T) {
	// If a row declares `requires: "Foo"`, "Foo" must be an apiName of
	// another row in the same pack. A dangling reference would leave the
	// prerequisite unenforceable (admin UI would display a missing
	// parent) and hint at a stale pack edit.
	rows, err := LoadStandardPermissions()
	require.NoError(t, err)

	available := map[string]struct{}{}
	for _, row := range rows {
		available[row.APIName] = struct{}{}
	}

	for _, row := range rows {
		if row.Requires == "" {
			continue
		}
		_, ok := available[row.Requires]
		assert.True(t, ok, "permission %q declares requires=%q but that permission is not in the pack", row.APIName, row.Requires)
	}
}

func TestStandardPermissions_DefaultGrantedToAudiencesAreKnown(t *testing.T) {
	// The seeder only knows how to resolve audience names to Profile IDs
	// for a fixed set of built-in audiences. A pack row referencing an
	// unknown audience would be silently dropped at seed time (and
	// logged as a warning). Fail loud in tests so the pack author sees
	// the problem immediately.
	rows, err := LoadStandardPermissions()
	require.NoError(t, err)

	known := map[string]struct{}{
		"SystemAdmin":  {},
		"StandardUser": {},
	}
	for _, row := range rows {
		for _, audience := range row.DefaultGrantedTo {
			_, ok := known[audience]
			assert.True(t, ok, "permission %q declares defaultGrantedTo audience %q which the seeder cannot route", row.APIName, audience)
		}
	}
}

func TestStandardPermissions_SystemAdminGrantedEverything(t *testing.T) {
	// Load-bearing invariant: System Admin gets every platform
	// permission on first install. Regression here would downgrade admin
	// capabilities silently. If we ever decide to narrow System Admin's
	// default grants, this test is the canary.
	rows, err := LoadStandardPermissions()
	require.NoError(t, err)

	for _, row := range rows {
		assert.Contains(t, row.DefaultGrantedTo, "SystemAdmin",
			"permission %q must ship with SystemAdmin in defaultGrantedTo", row.APIName)
	}
}

func TestStandardPermissions_StandardUserGetsOnlyCreateCustomizeReports(t *testing.T) {
	// The existing seeder gave StandardUser exactly one permission. The
	// pack preserves that. Changing this is a deliberate policy
	// decision; this test forces an explicit acknowledgement by failing
	// until someone updates both the pack and this assertion.
	rows, err := LoadStandardPermissions()
	require.NoError(t, err)

	standardUserGrants := []string{}
	for _, row := range rows {
		for _, audience := range row.DefaultGrantedTo {
			if audience == "StandardUser" {
				standardUserGrants = append(standardUserGrants, row.APIName)
			}
		}
	}
	assert.Equal(t, []string{"CreateCustomizeReports"}, standardUserGrants,
		"StandardUser's default grants changed — verify this is intentional and update the assertion")
}
