package policy

import (
	"context"
	"testing"

	"codeberg.org/VARASYS/ZDDC/zddc/internal/zddc"
)

// TestAllowActionFromChainP_TruthTable pins the principal-aware decider
// across the full {elevated × admin-at-level-N × action} cross-product.
// This is the single bypass site that consolidates every former
// scattered IsAdmin/IsSubtreeAdmin/CanEditZddc check in handler code,
// so its semantics must be locked in by an exhaustive table.
//
// Invariants pinned:
//
//  1. Admin bypass requires BOTH (Email in admins:) AND Elevated.
//     - In admins + elevated → bypass (any action returns true)
//     - In admins + un-elevated → no bypass (falls through to ACL)
//     - Not in admins + elevated → no bypass
//     - Empty email + elevated → no bypass (gate() rejects empty)
//
//  2. Bypass is action-agnostic: ActionRead, ActionWrite, ActionCreate,
//     ActionDelete, ActionAdmin all behave the same way under bypass.
//
//  3. Admin authority at ANY level on the chain confers bypass
//     (root admin gets bypass even on deep paths; subtree admin
//     declared at level N gets bypass for level ≥ N).
//
//  4. With no bypass, the cascade ACL governs:
//     - rwcd grant → ActionRead/Write/Create/Delete succeed, ActionAdmin denied
//     - no grant + has_any_file → all actions denied
//     - empty chain → all actions allowed (public default)
func TestAllowActionFromChainP_TruthTable(t *testing.T) {
	// Chain shape used throughout: root admins:[root@example.com] +
	// level 1 admins:[sub@example.com] + level 1 ACL allowing
	// staff@example.com rwcd.
	chain := zddc.PolicyChain{
		HasAnyFile: true,
		Levels: []zddc.ZddcFile{
			{Admins: []string{"root@example.com"}},
			{
				Admins: []string{"sub@example.com"},
				ACL: zddc.ACLRules{Permissions: map[string]string{
					"staff@example.com": "rwcd",
				}},
			},
		},
	}

	type want struct {
		read, write, create, deleteV, adminV bool
	}
	allActions := want{true, true, true, true, true}
	noAdmin := want{true, true, true, true, false} // staff has rwcd but no `a`

	cases := []struct {
		name     string
		email    string
		elevated bool
		want     want
	}{
		// ─── BYPASS PATH ────────────────────────────────────────────
		{
			name:     "root admin elevated → bypass on every action",
			email:    "root@example.com",
			elevated: true,
			want:     allActions,
		},
		{
			name:     "subtree admin elevated → bypass on every action",
			email:    "sub@example.com",
			elevated: true,
			want:     allActions,
		},

		// ─── ELEVATION GATE ─────────────────────────────────────────
		// An admin who hasn't elevated MUST be treated as a normal
		// user. They don't carry any baseline ACL grant in this
		// fixture, so every action is denied.
		{
			name:     "root admin NOT elevated → no bypass, no ACL grant → all denied",
			email:    "root@example.com",
			elevated: false,
			want:     want{},
		},
		{
			name:     "subtree admin NOT elevated → no bypass, no ACL grant → all denied",
			email:    "sub@example.com",
			elevated: false,
			want:     want{},
		},

		// ─── NON-ADMIN PATHS ────────────────────────────────────────
		{
			name:     "non-admin with rwcd grant → ACL governs, admin denied",
			email:    "staff@example.com",
			elevated: false,
			want:     noAdmin,
		},
		{
			name:     "non-admin elevated → elevation alone confers nothing",
			email:    "staff@example.com",
			elevated: true,
			want:     noAdmin,
		},
		{
			name:     "stranger denied across the board",
			email:    "rando@example.com",
			elevated: false,
			want:     want{},
		},
		{
			name:     "stranger elevated still denied",
			email:    "rando@example.com",
			elevated: true,
			want:     want{},
		},

		// ─── ANONYMOUS / DEGENERATE ─────────────────────────────────
		{
			name:     "empty email + elevated → gate rejects, no bypass",
			email:    "",
			elevated: true,
			want:     want{},
		},
		{
			name:     "empty email + not elevated → denied",
			email:    "",
			elevated: false,
			want:     want{},
		},
	}

	d := &InternalDecider{}
	ctx := context.Background()
	for _, tc := range cases {
		t.Run(tc.name, func(t *testing.T) {
			p := zddc.Principal{Email: tc.email, Elevated: tc.elevated}
			check := func(action string, want bool) {
				t.Helper()
				got, err := AllowActionFromChainP(ctx, d, chain, p, "/sub/file", action)
				if err != nil {
					t.Fatalf("%s: unexpected error: %v", action, err)
				}
				if got != want {
					t.Errorf("%s: got %v, want %v", action, got, want)
				}
			}
			check(ActionRead, tc.want.read)
			check(ActionWrite, tc.want.write)
			check(ActionCreate, tc.want.create)
			check(ActionDelete, tc.want.deleteV)
			check(ActionAdmin, tc.want.adminV)
		})
	}
}

// TestAllowActionFromChainP_AdminScopeDepth: admin authority at the
// root level cascades to every depth; subtree admin authority declared
// at level N applies only when level N is on the queried chain. The
// decider doesn't synthesise admin authority — it derives it from
// IsAdminForChain, which walks the chain it was given.
func TestAllowActionFromChainP_AdminScopeDepth(t *testing.T) {
	rootOnly := zddc.PolicyChain{
		HasAnyFile: true,
		Levels: []zddc.ZddcFile{
			{Admins: []string{"root@example.com"}},
		},
	}
	rootPlusProject := zddc.PolicyChain{
		HasAnyFile: true,
		Levels: []zddc.ZddcFile{
			{Admins: []string{"root@example.com"}},
			{Admins: []string{"alice@example.com"}},
		},
	}
	siblingChain := zddc.PolicyChain{
		HasAnyFile: true,
		Levels: []zddc.ZddcFile{
			{Admins: []string{"root@example.com"}},
			// Sibling project — alice is NOT in this chain's admins.
			{Admins: []string{"bob@example.com"}},
		},
	}

	d := &InternalDecider{}
	ctx := context.Background()

	cases := []struct {
		name    string
		chain   zddc.PolicyChain
		email   string
		path    string
		wantPut bool
	}{
		{
			name:    "root admin reaches a root-only path",
			chain:   rootOnly,
			email:   "root@example.com",
			path:    "/file",
			wantPut: true,
		},
		{
			name:    "root admin reaches a deep path",
			chain:   rootPlusProject,
			email:   "root@example.com",
			path:    "/Project-A/file",
			wantPut: true,
		},
		{
			name:    "subtree admin reaches their own subtree",
			chain:   rootPlusProject,
			email:   "alice@example.com",
			path:    "/Project-A/file",
			wantPut: true,
		},
		{
			name:    "subtree admin does NOT reach a sibling subtree",
			chain:   siblingChain,
			email:   "alice@example.com",
			path:    "/Project-B/file",
			wantPut: false,
		},
	}

	for _, tc := range cases {
		t.Run(tc.name, func(t *testing.T) {
			p := zddc.Principal{Email: tc.email, Elevated: true}
			got, _ := AllowActionFromChainP(ctx, d, tc.chain, p, tc.path, ActionWrite)
			if got != tc.wantPut {
				t.Errorf("AllowActionFromChainP write: got %v, want %v", got, tc.wantPut)
			}
		})
	}
}

// TestAllowActionFromChainP_BypassWinsOverWorm: an elevated admin's
// bypass fires before WORM evaluation, so a mis-filed document under
// received/ or issued/ can still be corrected. This is the explicit
// human escape hatch documented in the policy package comment.
func TestAllowActionFromChainP_BypassWinsOverWorm(t *testing.T) {
	trueP := true
	chain := zddc.PolicyChain{
		HasAnyFile: true,
		Levels: []zddc.ZddcFile{
			{Admins: []string{"root@example.com"}},
			{
				// WORM zone (received/issued style). Without admin bypass,
				// every write would be stripped.
				Worm:   []string{"_doc_controller"},
				ACL:    zddc.ACLRules{Inherit: &trueP},
			},
		},
	}
	d := &InternalDecider{}
	ctx := context.Background()

	p := zddc.Principal{Email: "root@example.com", Elevated: true}
	for _, action := range []string{ActionRead, ActionWrite, ActionCreate, ActionDelete, ActionAdmin} {
		t.Run("elevated admin in WORM zone — "+action, func(t *testing.T) {
			got, _ := AllowActionFromChainP(ctx, d, chain, p, "/received/x", action)
			if !got {
				t.Errorf("elevated admin %s denied inside WORM zone", action)
			}
		})
	}

	// Negative control: same principal un-elevated must NOT bypass WORM.
	pUn := zddc.Principal{Email: "root@example.com", Elevated: false}
	for _, action := range []string{ActionWrite, ActionDelete, ActionAdmin} {
		t.Run("un-elevated admin in WORM zone — "+action, func(t *testing.T) {
			got, _ := AllowActionFromChainP(ctx, d, chain, pUn, "/received/x", action)
			if got {
				t.Errorf("un-elevated admin %s allowed inside WORM zone (bypass leaked)", action)
			}
		})
	}
}
