package session

import (
	"testing"
	"time"
)

func TestSessionHasPermission(t *testing.T) {
	session := &Session{
		SessionID:   "test-session-id",
		UserID:      "test-user-id",
		Email:       "test@example.com",
		UserType:    UserTypeEmployee,
		Permissions: []string{"grades:read", "grades:write", "attendance:read"},
	}

	tests := []struct {
		name       string
		permission string
		expected   bool
	}{
		{"has grades:read", "grades:read", true},
		{"has grades:write", "grades:write", true},
		{"missing users:manage", "users:manage", false},
		{"empty permission", "", false},
	}

	for _, tt := range tests {
		t.Run(tt.name, func(t *testing.T) {
			result := session.HasPermission(tt.permission)
			if result != tt.expected {
				t.Errorf("HasPermission(%q) = %v, want %v", tt.permission, result, tt.expected)
			}
		})
	}
}

func TestSessionHasAnyPermission(t *testing.T) {
	session := &Session{
		SessionID:   "test-session-id",
		UserID:      "test-user-id",
		Email:       "test@example.com",
		UserType:    UserTypeEmployee,
		Permissions: []string{"grades:read"},
	}

	tests := []struct {
		name        string
		permissions []string
		expected    bool
	}{
		{"has one of the permissions", []string{"grades:read", "grades:write"}, true},
		{"missing all permissions", []string{"users:manage", "audit:read"}, false},
		{"empty list", []string{}, false},
	}

	for _, tt := range tests {
		t.Run(tt.name, func(t *testing.T) {
			result := session.HasAnyPermission(tt.permissions)
			if result != tt.expected {
				t.Errorf("HasAnyPermission(%v) = %v, want %v", tt.permissions, result, tt.expected)
			}
		})
	}
}

func TestSessionHasAllPermissions(t *testing.T) {
	session := &Session{
		SessionID:   "test-session-id",
		UserID:      "test-user-id",
		Email:       "test@example.com",
		UserType:    UserTypeEmployee,
		Permissions: []string{"grades:read", "grades:write", "attendance:read"},
	}

	tests := []struct {
		name        string
		permissions []string
		expected    bool
	}{
		{"has all permissions", []string{"grades:read", "grades:write"}, true},
		{"missing one permission", []string{"grades:read", "users:manage"}, false},
		{"empty list", []string{}, true},
	}

	for _, tt := range tests {
		t.Run(tt.name, func(t *testing.T) {
			result := session.HasAllPermissions(tt.permissions)
			if result != tt.expected {
				t.Errorf("HasAllPermissions(%v) = %v, want %v", tt.permissions, result, tt.expected)
			}
		})
	}
}

func TestSessionHasRole(t *testing.T) {
	session := &Session{
		SessionID: "test-session-id",
		UserID:    "test-user-id",
		Email:     "test@example.com",
		UserType:  UserTypeEmployee,
		Roles:     []string{"teacher", "klassenlehrer"},
	}

	tests := []struct {
		name     string
		role     string
		expected bool
	}{
		{"has teacher role", "teacher", true},
		{"has klassenlehrer role", "klassenlehrer", true},
		{"missing admin role", "admin", false},
	}

	for _, tt := range tests {
		t.Run(tt.name, func(t *testing.T) {
			result := session.HasRole(tt.role)
			if result != tt.expected {
				t.Errorf("HasRole(%q) = %v, want %v", tt.role, result, tt.expected)
			}
		})
	}
}

func TestSessionIsEmployee(t *testing.T) {
	employeeSession := &Session{
		SessionID: "test",
		UserID:    "test",
		Email:     "test@test.com",
		UserType:  UserTypeEmployee,
	}

	customerSession := &Session{
		SessionID: "test",
		UserID:    "test",
		Email:     "test@test.com",
		UserType:  UserTypeCustomer,
	}

	if !employeeSession.IsEmployee() {
		t.Error("Employee session should return true for IsEmployee()")
	}
	if employeeSession.IsCustomer() {
		t.Error("Employee session should return false for IsCustomer()")
	}
	if !customerSession.IsCustomer() {
		t.Error("Customer session should return true for IsCustomer()")
	}
	if customerSession.IsEmployee() {
		t.Error("Customer session should return false for IsEmployee()")
	}
}

func TestDetermineUserType(t *testing.T) {
	tests := []struct {
		name     string
		roles    []string
		expected UserType
	}{
		{"teacher is employee", []string{"teacher"}, UserTypeEmployee},
		{"admin is employee", []string{"admin"}, UserTypeEmployee},
		{"klassenlehrer is employee", []string{"klassenlehrer"}, UserTypeEmployee},
		{"parent is customer", []string{"parent"}, UserTypeCustomer},
		{"student is customer", []string{"student"}, UserTypeCustomer},
		{"employee takes precedence", []string{"teacher", "parent"}, UserTypeEmployee},
		{"unknown role defaults to customer", []string{"unknown_role"}, UserTypeCustomer},
		{"empty roles defaults to customer", []string{}, UserTypeCustomer},
	}

	for _, tt := range tests {
		t.Run(tt.name, func(t *testing.T) {
			result := DetermineUserType(tt.roles)
			if result != tt.expected {
				t.Errorf("DetermineUserType(%v) = %v, want %v", tt.roles, result, tt.expected)
			}
		})
	}
}

func TestGetPermissionsForRoles(t *testing.T) {
	t.Run("employee gets employee permissions", func(t *testing.T) {
		permissions := GetPermissionsForRoles([]string{"teacher"}, UserTypeEmployee)

		hasGradesRead := false
		for _, p := range permissions {
			if p == "grades:read" {
				hasGradesRead = true
				break
			}
		}

		if !hasGradesRead {
			t.Error("Employee should have grades:read permission")
		}
	})

	t.Run("customer gets customer permissions", func(t *testing.T) {
		permissions := GetPermissionsForRoles([]string{"parent"}, UserTypeCustomer)

		hasChildrenRead := false
		for _, p := range permissions {
			if p == "children:read" {
				hasChildrenRead = true
				break
			}
		}

		if !hasChildrenRead {
			t.Error("Customer should have children:read permission")
		}
	})

	t.Run("admin gets admin permissions", func(t *testing.T) {
		permissions := GetPermissionsForRoles([]string{"admin"}, UserTypeEmployee)

		hasUsersManage := false
		for _, p := range permissions {
			if p == "users:manage" {
				hasUsersManage = true
				break
			}
		}

		if !hasUsersManage {
			t.Error("Admin should have users:manage permission")
		}
	})
}

func TestCheckResourceOwnership(t *testing.T) {
	userID := "user-123"
	adminSession := &Session{
		SessionID: "test",
		UserID:    "admin-456",
		Email:     "admin@test.com",
		UserType:  UserTypeEmployee,
		Roles:     []string{"admin"},
	}
	regularSession := &Session{
		SessionID: "test",
		UserID:    userID,
		Email:     "user@test.com",
		UserType:  UserTypeEmployee,
		Roles:     []string{"teacher"},
	}
	otherSession := &Session{
		SessionID: "test",
		UserID:    "other-789",
		Email:     "other@test.com",
		UserType:  UserTypeEmployee,
		Roles:     []string{"teacher"},
	}

	tests := []struct {
		name        string
		session     *Session
		resourceUID string
		allowAdmin  bool
		expected    bool
	}{
		{"owner can access", regularSession, userID, true, true},
		{"admin can access with allowAdmin", adminSession, userID, true, true},
		{"admin cannot access without allowAdmin", adminSession, userID, false, false},
		{"other user cannot access", otherSession, userID, true, false},
		{"nil session returns false", nil, userID, true, false},
	}

	for _, tt := range tests {
		t.Run(tt.name, func(t *testing.T) {
			result := CheckResourceOwnership(tt.session, tt.resourceUID, tt.allowAdmin)
			if result != tt.expected {
				t.Errorf("CheckResourceOwnership() = %v, want %v", result, tt.expected)
			}
		})
	}
}

func TestEmployeeRolesMap(t *testing.T) {
	expectedRoles := []string{
		"admin", "schul_admin", "teacher", "klassenlehrer",
		"fachlehrer", "sekretariat", "data_protection_officer",
	}

	for _, role := range expectedRoles {
		if !EmployeeRoles[role] {
			t.Errorf("Expected employee role %q not found in EmployeeRoles map", role)
		}
	}
}

func TestCustomerRolesMap(t *testing.T) {
	expectedRoles := []string{"parent", "student", "user"}

	for _, role := range expectedRoles {
		if !CustomerRoles[role] {
			t.Errorf("Expected customer role %q not found in CustomerRoles map", role)
		}
	}
}

func TestPermissionSlicesNotEmpty(t *testing.T) {
	if len(EmployeePermissions) == 0 {
		t.Error("EmployeePermissions should not be empty")
	}
	if len(CustomerPermissions) == 0 {
		t.Error("CustomerPermissions should not be empty")
	}
	if len(AdminPermissions) == 0 {
		t.Error("AdminPermissions should not be empty")
	}
}

func TestSessionTimestamps(t *testing.T) {
	now := time.Now().UTC()
	session := &Session{
		SessionID:      "test",
		UserID:         "test",
		Email:          "test@test.com",
		UserType:       UserTypeEmployee,
		CreatedAt:      now,
		LastActivityAt: now,
	}

	if session.CreatedAt.IsZero() {
		t.Error("CreatedAt should not be zero")
	}
	if session.LastActivityAt.IsZero() {
		t.Error("LastActivityAt should not be zero")
	}
	if session.CreatedAt.After(time.Now().UTC()) {
		t.Error("CreatedAt should not be in the future")
	}
}

func TestUserTypeConstants(t *testing.T) {
	if UserTypeEmployee != "employee" {
		t.Errorf("UserTypeEmployee = %q, want %q", UserTypeEmployee, "employee")
	}
	if UserTypeCustomer != "customer" {
		t.Errorf("UserTypeCustomer = %q, want %q", UserTypeCustomer, "customer")
	}
}
