package approvalir

import (
	"strings"
	"testing"
)

func TestSubmitterKindIsClosed(t *testing.T) {
	valid := []SubmitterKind{
		SubmitterKindAllInternalUsers,
		SubmitterKindRecordCreator,
		SubmitterKindRecordOwner,
		SubmitterKindUser,
		SubmitterKindRole,
		SubmitterKindRoleAndSubordinates,
	}
	for _, kind := range valid {
		if !kind.Valid() {
			t.Fatalf("defined kind %q reported invalid", kind)
		}
	}
	if SubmitterKind("").Valid() {
		t.Fatal("zero submitter kind reported valid")
	}
	if SubmitterKind("dialect_user").Valid() {
		t.Fatal("undefined submitter kind reported valid")
	}
}

func TestRecordEditabilityIsClosed(t *testing.T) {
	valid := []RecordEditability{
		RecordEditabilityAdminOnly,
		RecordEditabilityAdminOrCurrentApprover,
	}
	for _, editability := range valid {
		if !editability.Valid() {
			t.Fatalf("defined record editability %q reported invalid", editability)
		}
	}
	if RecordEditability("").Valid() {
		t.Fatal("zero record editability reported valid")
	}
	if RecordEditability("work_item_owner").Valid() {
		t.Fatal("undefined record editability reported valid")
	}
}

func TestApprovalPolicyValidateAcceptsClosedSubmitterSemantics(t *testing.T) {
	policy := &ApprovalPolicy{
		RecordEditability: RecordEditabilityAdminOrCurrentApprover,
		AllowedSubmitters: []AllowedSubmitter{
			{Kind: SubmitterKindAllInternalUsers},
			{Kind: SubmitterKindRecordCreator},
			{Kind: SubmitterKindRecordOwner},
			{Kind: SubmitterKindUser, Submitter: "user:123"},
			{Kind: SubmitterKindRole, Submitter: "role:reviewer"},
			{Kind: SubmitterKindRoleAndSubordinates, Submitter: "role:manager"},
		},
	}
	if err := policy.Validate(); err != nil {
		t.Fatalf("Validate() error = %v", err)
	}

	emptyPolicy := &ApprovalPolicy{RecordEditability: RecordEditabilityAdminOnly}
	if err := emptyPolicy.Validate(); err != nil {
		t.Fatalf("empty explicit policy Validate() error = %v", err)
	}
}

func TestApprovalPolicyValidateFailsClosed(t *testing.T) {
	validPolicy := func() *ApprovalPolicy {
		return &ApprovalPolicy{
			RecordEditability: RecordEditabilityAdminOnly,
			AllowedSubmitters: []AllowedSubmitter{
				{Kind: SubmitterKindUser, Submitter: "user:123"},
			},
		}
	}
	tests := []struct {
		name    string
		policy  func() *ApprovalPolicy
		wantErr string
	}{
		{
			name:    "nil policy",
			policy:  func() *ApprovalPolicy { return nil },
			wantErr: "approval policy is nil",
		},
		{
			name: "missing record editability",
			policy: func() *ApprovalPolicy {
				policy := validPolicy()
				policy.RecordEditability = ""
				return policy
			},
			wantErr: "invalid record editability",
		},
		{
			name: "unknown record editability",
			policy: func() *ApprovalPolicy {
				policy := validPolicy()
				policy.RecordEditability = "product_default"
				return policy
			},
			wantErr: "invalid record editability",
		},
		{
			name: "unknown submitter kind",
			policy: func() *ApprovalPolicy {
				policy := validPolicy()
				policy.AllowedSubmitters[0].Kind = "dialect_user"
				return policy
			},
			wantErr: "invalid kind",
		},
		{
			name: "intrinsic submitter with principal",
			policy: func() *ApprovalPolicy {
				policy := validPolicy()
				policy.AllowedSubmitters[0] = AllowedSubmitter{Kind: SubmitterKindRecordOwner, Submitter: "ignored"}
				return policy
			},
			wantErr: "must not carry a principal reference",
		},
		{
			name: "referenced submitter without principal",
			policy: func() *ApprovalPolicy {
				policy := validPolicy()
				policy.AllowedSubmitters[0] = AllowedSubmitter{Kind: SubmitterKindRole}
				return policy
			},
			wantErr: "requires a principal reference",
		},
		{
			name: "referenced submitter with whitespace principal",
			policy: func() *ApprovalPolicy {
				policy := validPolicy()
				policy.AllowedSubmitters[0] = AllowedSubmitter{Kind: SubmitterKindUser, Submitter: " \t "}
				return policy
			},
			wantErr: "requires a principal reference",
		},
	}
	for _, test := range tests {
		t.Run(test.name, func(t *testing.T) {
			err := test.policy().Validate()
			if err == nil || !strings.Contains(err.Error(), test.wantErr) {
				t.Fatalf("Validate() error = %v, want containing %q", err, test.wantErr)
			}
		})
	}
}
