import { UnauthorizedException } from '@nestjs/common';
import { Test, TestingModule } from '@nestjs/testing';

import { beforeEach, describe, expect, it, vi } from 'vitest';

import { OidcAuthorizationService } from '@app/unraid-api/graph/resolvers/sso/auth/oidc-authorization.service.js';
import {
    AuthorizationOperator,
    AuthorizationRuleMode,
    OidcAuthorizationRule,
    OidcProvider,
} from '@app/unraid-api/graph/resolvers/sso/models/oidc-provider.model.js';

describe('OidcAuthorizationService', () => {
    let service: OidcAuthorizationService;
    let module: TestingModule;

    beforeEach(async () => {
        module = await Test.createTestingModule({
            providers: [OidcAuthorizationService],
        }).compile();

        service = module.get<OidcAuthorizationService>(OidcAuthorizationService);
    });

    describe('Authorization Rule Evaluation', () => {
        // Access the private method through any type casting for testing
        const evaluateRule = (rule: OidcAuthorizationRule, claims: any): boolean => {
            return (service as any).evaluateRule(rule, claims);
        };

        describe('EQUALS operator', () => {
            it('should return true when claim equals any value in the array', () => {
                const rule: OidcAuthorizationRule = {
                    claim: 'email',
                    operator: AuthorizationOperator.EQUALS,
                    value: ['user@example.com', 'admin@example.com'],
                };

                expect(evaluateRule(rule, { email: 'user@example.com' })).toBe(true);
                expect(evaluateRule(rule, { email: 'admin@example.com' })).toBe(true);
            });

            it('should return false when claim does not equal any value', () => {
                const rule: OidcAuthorizationRule = {
                    claim: 'email',
                    operator: AuthorizationOperator.EQUALS,
                    value: ['user@example.com'],
                };

                expect(evaluateRule(rule, { email: 'other@example.com' })).toBe(false);
            });

            it('should handle numeric values correctly', () => {
                const rule: OidcAuthorizationRule = {
                    claim: 'user_id',
                    operator: AuthorizationOperator.EQUALS,
                    value: ['12345'],
                };

                expect(evaluateRule(rule, { user_id: 12345 })).toBe(true);
                expect(evaluateRule(rule, { user_id: '12345' })).toBe(true);
            });
        });

        describe('CONTAINS operator', () => {
            it('should return true when claim contains any substring', () => {
                const rule: OidcAuthorizationRule = {
                    claim: 'email',
                    operator: AuthorizationOperator.CONTAINS,
                    value: ['@company.com', '@partner.org'],
                };

                expect(evaluateRule(rule, { email: 'john@company.com' })).toBe(true);
                expect(evaluateRule(rule, { email: 'jane@partner.org' })).toBe(true);
            });

            it('should return false when claim does not contain any substring', () => {
                const rule: OidcAuthorizationRule = {
                    claim: 'email',
                    operator: AuthorizationOperator.CONTAINS,
                    value: ['@company.com'],
                };

                expect(evaluateRule(rule, { email: 'user@other.org' })).toBe(false);
            });

            it('should be case sensitive', () => {
                const rule: OidcAuthorizationRule = {
                    claim: 'name',
                    operator: AuthorizationOperator.CONTAINS,
                    value: ['Admin'],
                };

                expect(evaluateRule(rule, { name: 'Administrator' })).toBe(true);
                expect(evaluateRule(rule, { name: 'administrator' })).toBe(false);
            });
        });

        describe('STARTS_WITH operator', () => {
            it('should return true when claim starts with any prefix', () => {
                const rule: OidcAuthorizationRule = {
                    claim: 'department',
                    operator: AuthorizationOperator.STARTS_WITH,
                    value: ['eng-', 'dev-'],
                };

                expect(evaluateRule(rule, { department: 'eng-backend' })).toBe(true);
                expect(evaluateRule(rule, { department: 'dev-frontend' })).toBe(true);
            });

            it('should return false when claim does not start with any prefix', () => {
                const rule: OidcAuthorizationRule = {
                    claim: 'department',
                    operator: AuthorizationOperator.STARTS_WITH,
                    value: ['eng-'],
                };

                expect(evaluateRule(rule, { department: 'marketing-team' })).toBe(false);
                expect(evaluateRule(rule, { department: 'backend-eng' })).toBe(false);
            });
        });

        describe('ENDS_WITH operator', () => {
            it('should return true when claim ends with any suffix', () => {
                const rule: OidcAuthorizationRule = {
                    claim: 'email',
                    operator: AuthorizationOperator.ENDS_WITH,
                    value: ['@gmail.com', '@company.com'],
                };

                expect(evaluateRule(rule, { email: 'user@gmail.com' })).toBe(true);
                expect(evaluateRule(rule, { email: 'admin@company.com' })).toBe(true);
            });

            it('should return false when claim does not end with any suffix', () => {
                const rule: OidcAuthorizationRule = {
                    claim: 'email',
                    operator: AuthorizationOperator.ENDS_WITH,
                    value: ['@company.com'],
                };

                expect(evaluateRule(rule, { email: 'user@other.org' })).toBe(false);
            });

            it('should handle domain validation correctly', () => {
                const rule: OidcAuthorizationRule = {
                    claim: 'hd',
                    operator: AuthorizationOperator.ENDS_WITH,
                    value: ['.edu'],
                };

                expect(evaluateRule(rule, { hd: 'university.edu' })).toBe(true);
                expect(evaluateRule(rule, { hd: 'college.edu' })).toBe(true);
                expect(evaluateRule(rule, { hd: 'company.com' })).toBe(false);
            });
        });

        describe('Missing or undefined claims', () => {
            it('should return false when claim is missing', () => {
                const rule: OidcAuthorizationRule = {
                    claim: 'email',
                    operator: AuthorizationOperator.EQUALS,
                    value: ['user@example.com'],
                };

                expect(evaluateRule(rule, {})).toBe(false);
                expect(evaluateRule(rule, { name: 'John' })).toBe(false);
            });

            it('should return false when claim is null', () => {
                const rule: OidcAuthorizationRule = {
                    claim: 'email',
                    operator: AuthorizationOperator.EQUALS,
                    value: ['user@example.com'],
                };

                expect(evaluateRule(rule, { email: null })).toBe(false);
            });

            it('should return false when claim is undefined', () => {
                const rule: OidcAuthorizationRule = {
                    claim: 'email',
                    operator: AuthorizationOperator.EQUALS,
                    value: ['user@example.com'],
                };

                expect(evaluateRule(rule, { email: undefined })).toBe(false);
            });
        });

        describe('Type coercion', () => {
            it('should convert values to strings for comparison', () => {
                const rule: OidcAuthorizationRule = {
                    claim: 'user_id',
                    operator: AuthorizationOperator.EQUALS,
                    value: ['12345'],
                };

                expect(evaluateRule(rule, { user_id: 12345 })).toBe(true);
                expect(evaluateRule(rule, { user_id: '12345' })).toBe(true);
            });

            it('should handle boolean values', () => {
                const rule: OidcAuthorizationRule = {
                    claim: 'is_admin',
                    operator: AuthorizationOperator.EQUALS,
                    value: ['true'],
                };

                expect(evaluateRule(rule, { is_admin: true })).toBe(true);
                expect(evaluateRule(rule, { is_admin: 'true' })).toBe(true);
                expect(evaluateRule(rule, { is_admin: false })).toBe(false);
            });
        });

        describe('Array claim handling', () => {
            it('should evaluate EQUALS operator on array claims', () => {
                const rule: OidcAuthorizationRule = {
                    claim: 'groups',
                    operator: AuthorizationOperator.EQUALS,
                    value: ['admin', 'developer'],
                };

                // Array with matching value
                expect(evaluateRule(rule, { groups: ['user', 'admin', 'viewer'] })).toBe(true);
                expect(evaluateRule(rule, { groups: ['developer', 'tester'] })).toBe(true);

                // Array without matching value
                expect(evaluateRule(rule, { groups: ['user', 'viewer'] })).toBe(false);

                // Empty array
                expect(evaluateRule(rule, { groups: [] })).toBe(false);
            });

            it('should evaluate CONTAINS operator on array claims', () => {
                const rule: OidcAuthorizationRule = {
                    claim: 'roles',
                    operator: AuthorizationOperator.CONTAINS,
                    value: ['admin', 'super'],
                };

                // Array elements containing the substring
                expect(evaluateRule(rule, { roles: ['superuser', 'viewer'] })).toBe(true);
                expect(evaluateRule(rule, { roles: ['admin-read', 'user'] })).toBe(true);
                expect(evaluateRule(rule, { roles: ['administrator'] })).toBe(true);

                // Array without matching substrings
                expect(evaluateRule(rule, { roles: ['user', 'viewer'] })).toBe(false);
            });

            it('should evaluate STARTS_WITH operator on array claims', () => {
                const rule: OidcAuthorizationRule = {
                    claim: 'permissions',
                    operator: AuthorizationOperator.STARTS_WITH,
                    value: ['read:', 'write:'],
                };

                // Array with elements starting with prefix
                expect(evaluateRule(rule, { permissions: ['read:users', 'update:settings'] })).toBe(
                    true
                );
                expect(evaluateRule(rule, { permissions: ['write:logs', 'delete:cache'] })).toBe(true);

                // Array without matching prefixes
                expect(evaluateRule(rule, { permissions: ['execute:scripts', 'admin:all'] })).toBe(
                    false
                );
            });

            it('should evaluate ENDS_WITH operator on array claims', () => {
                const rule: OidcAuthorizationRule = {
                    claim: 'emails',
                    operator: AuthorizationOperator.ENDS_WITH,
                    value: ['@company.com', '@partner.org'],
                };

                // Array with elements ending with suffix
                expect(evaluateRule(rule, { emails: ['user@company.com', 'test@other.com'] })).toBe(
                    true
                );
                expect(evaluateRule(rule, { emails: ['admin@partner.org'] })).toBe(true);

                // Array without matching suffixes
                expect(evaluateRule(rule, { emails: ['user@gmail.com', 'test@yahoo.com'] })).toBe(false);
            });

            it('should handle arrays with mixed types', () => {
                const rule: OidcAuthorizationRule = {
                    claim: 'mixed',
                    operator: AuthorizationOperator.EQUALS,
                    value: ['123', 'true'],
                };

                // Array with numbers and booleans that get converted to strings
                expect(evaluateRule(rule, { mixed: [123, 'text', true] })).toBe(true);
                expect(evaluateRule(rule, { mixed: [false, 456, 'true'] })).toBe(true);

                // Array with objects should skip them
                expect(evaluateRule(rule, { mixed: [{}, 'other', null] })).toBe(false);
            });

            it('should return true if ANY element in array matches (not ALL)', () => {
                const rule: OidcAuthorizationRule = {
                    claim: 'departments',
                    operator: AuthorizationOperator.EQUALS,
                    value: ['engineering'],
                };

                // Should pass if at least one element matches
                expect(evaluateRule(rule, { departments: ['marketing', 'engineering', 'sales'] })).toBe(
                    true
                );

                // Should fail if no elements match
                expect(evaluateRule(rule, { departments: ['marketing', 'sales'] })).toBe(false);
            });
        });

        describe('Multiple rules evaluation', () => {
            // Access the private method through any type casting for testing
            const evaluateAuthorizationRules = (
                rules: OidcAuthorizationRule[],
                claims: any,
                mode: AuthorizationRuleMode = AuthorizationRuleMode.OR
            ): boolean => {
                return (service as any).evaluateAuthorizationRules(rules, claims, mode);
            };

            it('should require ANY rule to pass (OR logic)', () => {
                const rules: OidcAuthorizationRule[] = [
                    {
                        claim: 'email',
                        operator: AuthorizationOperator.ENDS_WITH,
                        value: ['@company.com'],
                    },
                    {
                        claim: 'department',
                        operator: AuthorizationOperator.EQUALS,
                        value: ['engineering', 'development'],
                    },
                ];

                // Both rules pass
                expect(
                    evaluateAuthorizationRules(rules, {
                        email: 'user@company.com',
                        department: 'engineering',
                    })
                ).toBe(true);

                // Only email rule passes
                expect(
                    evaluateAuthorizationRules(rules, {
                        email: 'user@company.com',
                        department: 'marketing',
                    })
                ).toBe(true);

                // Only department rule passes
                expect(
                    evaluateAuthorizationRules(rules, {
                        email: 'user@other.com',
                        department: 'engineering',
                    })
                ).toBe(true);

                // Neither rule passes
                expect(
                    evaluateAuthorizationRules(rules, {
                        email: 'user@other.com',
                        department: 'marketing',
                    })
                ).toBe(false);
            });

            it('should return false when no rules are defined', () => {
                expect(evaluateAuthorizationRules([], { email: 'any@email.com' })).toBe(false);
            });

            describe('AND logic mode', () => {
                it('should require ALL rules to pass when using AND mode', () => {
                    const rules: OidcAuthorizationRule[] = [
                        {
                            claim: 'email',
                            operator: AuthorizationOperator.ENDS_WITH,
                            value: ['@company.com'],
                        },
                        {
                            claim: 'department',
                            operator: AuthorizationOperator.EQUALS,
                            value: ['engineering'],
                        },
                    ];

                    // Both rules pass - should return true
                    expect(
                        evaluateAuthorizationRules(
                            rules,
                            {
                                email: 'user@company.com',
                                department: 'engineering',
                            },
                            AuthorizationRuleMode.AND
                        )
                    ).toBe(true);

                    // Only email rule passes - should return false
                    expect(
                        evaluateAuthorizationRules(
                            rules,
                            {
                                email: 'user@company.com',
                                department: 'marketing',
                            },
                            AuthorizationRuleMode.AND
                        )
                    ).toBe(false);

                    // Only department rule passes - should return false
                    expect(
                        evaluateAuthorizationRules(
                            rules,
                            {
                                email: 'user@other.com',
                                department: 'engineering',
                            },
                            AuthorizationRuleMode.AND
                        )
                    ).toBe(false);

                    // Neither rule passes - should return false
                    expect(
                        evaluateAuthorizationRules(
                            rules,
                            {
                                email: 'user@other.com',
                                department: 'marketing',
                            },
                            AuthorizationRuleMode.AND
                        )
                    ).toBe(false);
                });

                it('should handle complex AND conditions with multiple operators', () => {
                    const rules: OidcAuthorizationRule[] = [
                        {
                            claim: 'email',
                            operator: AuthorizationOperator.ENDS_WITH,
                            value: ['@company.com'],
                        },
                        {
                            claim: 'name',
                            operator: AuthorizationOperator.STARTS_WITH,
                            value: ['John', 'Jane'],
                        },
                        {
                            claim: 'role',
                            operator: AuthorizationOperator.CONTAINS,
                            value: ['admin', 'manager'],
                        },
                    ];

                    // All rules pass
                    expect(
                        evaluateAuthorizationRules(
                            rules,
                            {
                                email: 'john.doe@company.com',
                                name: 'John Doe',
                                role: 'senior-admin',
                            },
                            AuthorizationRuleMode.AND
                        )
                    ).toBe(true);

                    // Missing one condition (role doesn't contain admin/manager)
                    expect(
                        evaluateAuthorizationRules(
                            rules,
                            {
                                email: 'john.doe@company.com',
                                name: 'John Doe',
                                role: 'developer',
                            },
                            AuthorizationRuleMode.AND
                        )
                    ).toBe(false);
                });

                it('should return true with single rule in AND mode', () => {
                    const rules: OidcAuthorizationRule[] = [
                        {
                            claim: 'email',
                            operator: AuthorizationOperator.EQUALS,
                            value: ['admin@company.com'],
                        },
                    ];

                    expect(
                        evaluateAuthorizationRules(
                            rules,
                            { email: 'admin@company.com' },
                            AuthorizationRuleMode.AND
                        )
                    ).toBe(true);

                    expect(
                        evaluateAuthorizationRules(
                            rules,
                            { email: 'user@company.com' },
                            AuthorizationRuleMode.AND
                        )
                    ).toBe(false);
                });
            });

            describe('OR logic mode (default)', () => {
                it('should handle complex OR conditions with multiple operators', () => {
                    const rules: OidcAuthorizationRule[] = [
                        {
                            claim: 'email',
                            operator: AuthorizationOperator.EQUALS,
                            value: ['admin@company.com', 'superuser@company.com'],
                        },
                        {
                            claim: 'groups',
                            operator: AuthorizationOperator.CONTAINS,
                            value: ['administrators', 'power-users'],
                        },
                        {
                            claim: 'department',
                            operator: AuthorizationOperator.STARTS_WITH,
                            value: ['IT-', 'SEC-'],
                        },
                    ];

                    // Multiple rules pass
                    expect(
                        evaluateAuthorizationRules(
                            rules,
                            {
                                email: 'admin@company.com',
                                groups: 'administrators',
                                department: 'IT-Support',
                            },
                            AuthorizationRuleMode.OR
                        )
                    ).toBe(true);

                    // Only one rule passes (department)
                    expect(
                        evaluateAuthorizationRules(
                            rules,
                            {
                                email: 'user@company.com',
                                groups: 'users',
                                department: 'SEC-Operations',
                            },
                            AuthorizationRuleMode.OR
                        )
                    ).toBe(true);

                    // No rules pass
                    expect(
                        evaluateAuthorizationRules(
                            rules,
                            {
                                email: 'user@company.com',
                                groups: 'users',
                                department: 'HR-Management',
                            },
                            AuthorizationRuleMode.OR
                        )
                    ).toBe(false);
                });

                it('should use OR mode as default when mode is not specified', () => {
                    const rules: OidcAuthorizationRule[] = [
                        {
                            claim: 'email',
                            operator: AuthorizationOperator.ENDS_WITH,
                            value: ['@company.com'],
                        },
                        {
                            claim: 'special_user',
                            operator: AuthorizationOperator.EQUALS,
                            value: ['true'],
                        },
                    ];

                    // Test without explicit mode (should default to OR)
                    expect(
                        evaluateAuthorizationRules(rules, {
                            email: 'user@other.com',
                            special_user: 'true',
                        })
                    ).toBe(true);
                });
            });

            describe('Edge cases and boundary conditions', () => {
                it('should handle empty rules array correctly', () => {
                    expect(
                        evaluateAuthorizationRules(
                            [],
                            { email: 'any@email.com' },
                            AuthorizationRuleMode.AND
                        )
                    ).toBe(false);
                    expect(
                        evaluateAuthorizationRules(
                            [],
                            { email: 'any@email.com' },
                            AuthorizationRuleMode.OR
                        )
                    ).toBe(false);
                });

                it('should handle rules with empty value arrays', () => {
                    const rules: OidcAuthorizationRule[] = [
                        {
                            claim: 'email',
                            operator: AuthorizationOperator.EQUALS,
                            value: [],
                        },
                    ];

                    expect(
                        evaluateAuthorizationRules(
                            rules,
                            { email: 'user@company.com' },
                            AuthorizationRuleMode.OR
                        )
                    ).toBe(false);

                    expect(
                        evaluateAuthorizationRules(
                            rules,
                            { email: 'user@company.com' },
                            AuthorizationRuleMode.AND
                        )
                    ).toBe(false);
                });

                it('should handle missing claims in AND mode', () => {
                    const rules: OidcAuthorizationRule[] = [
                        {
                            claim: 'email',
                            operator: AuthorizationOperator.ENDS_WITH,
                            value: ['@company.com'],
                        },
                        {
                            claim: 'department',
                            operator: AuthorizationOperator.EQUALS,
                            value: ['engineering'],
                        },
                    ];

                    // Missing department claim
                    expect(
                        evaluateAuthorizationRules(
                            rules,
                            { email: 'user@company.com' },
                            AuthorizationRuleMode.AND
                        )
                    ).toBe(false);
                });

                it('should handle missing claims in OR mode', () => {
                    const rules: OidcAuthorizationRule[] = [
                        {
                            claim: 'email',
                            operator: AuthorizationOperator.ENDS_WITH,
                            value: ['@company.com'],
                        },
                        {
                            claim: 'department',
                            operator: AuthorizationOperator.EQUALS,
                            value: ['engineering'],
                        },
                    ];

                    // Missing department claim but email passes
                    expect(
                        evaluateAuthorizationRules(
                            rules,
                            { email: 'user@company.com' },
                            AuthorizationRuleMode.OR
                        )
                    ).toBe(true);

                    // Missing both claims
                    expect(
                        evaluateAuthorizationRules(rules, { other: 'value' }, AuthorizationRuleMode.OR)
                    ).toBe(false);
                });

                it('should handle all claims missing in both modes', () => {
                    const rules: OidcAuthorizationRule[] = [
                        {
                            claim: 'email',
                            operator: AuthorizationOperator.EQUALS,
                            value: ['admin@company.com'],
                        },
                        {
                            claim: 'role',
                            operator: AuthorizationOperator.EQUALS,
                            value: ['admin'],
                        },
                    ];

                    const claimsWithoutRequired = { name: 'John', sub: '12345' };

                    expect(
                        evaluateAuthorizationRules(
                            rules,
                            claimsWithoutRequired,
                            AuthorizationRuleMode.AND
                        )
                    ).toBe(false);

                    expect(
                        evaluateAuthorizationRules(
                            rules,
                            claimsWithoutRequired,
                            AuthorizationRuleMode.OR
                        )
                    ).toBe(false);
                });
            });

            describe('Real-world authorization scenarios', () => {
                it('should handle Google Workspace domain + specific users (OR)', () => {
                    const rules: OidcAuthorizationRule[] = [
                        {
                            claim: 'hd',
                            operator: AuthorizationOperator.EQUALS,
                            value: ['company.com'],
                        },
                        {
                            claim: 'email',
                            operator: AuthorizationOperator.EQUALS,
                            value: ['contractor1@gmail.com', 'contractor2@outlook.com'],
                        },
                    ];

                    // Company domain user
                    expect(
                        evaluateAuthorizationRules(
                            rules,
                            { hd: 'company.com', email: 'employee@company.com' },
                            AuthorizationRuleMode.OR
                        )
                    ).toBe(true);

                    // Allowed contractor
                    expect(
                        evaluateAuthorizationRules(
                            rules,
                            { email: 'contractor1@gmail.com' },
                            AuthorizationRuleMode.OR
                        )
                    ).toBe(true);

                    // Unauthorized user
                    expect(
                        evaluateAuthorizationRules(
                            rules,
                            { email: 'random@gmail.com' },
                            AuthorizationRuleMode.OR
                        )
                    ).toBe(false);
                });

                it('should handle department + role requirements (AND)', () => {
                    const rules: OidcAuthorizationRule[] = [
                        {
                            claim: 'department',
                            operator: AuthorizationOperator.EQUALS,
                            value: ['engineering', 'devops'],
                        },
                        {
                            claim: 'role',
                            operator: AuthorizationOperator.CONTAINS,
                            value: ['senior', 'lead', 'manager'],
                        },
                    ];

                    // Senior engineer - both conditions met
                    expect(
                        evaluateAuthorizationRules(
                            rules,
                            { department: 'engineering', role: 'senior-engineer' },
                            AuthorizationRuleMode.AND
                        )
                    ).toBe(true);

                    // Junior engineer - department ok, but not senior
                    expect(
                        evaluateAuthorizationRules(
                            rules,
                            { department: 'engineering', role: 'junior-engineer' },
                            AuthorizationRuleMode.AND
                        )
                    ).toBe(false);

                    // Senior in wrong department
                    expect(
                        evaluateAuthorizationRules(
                            rules,
                            { department: 'marketing', role: 'senior-marketer' },
                            AuthorizationRuleMode.AND
                        )
                    ).toBe(false);
                });

                it('should handle email domain + group membership (AND)', () => {
                    const rules: OidcAuthorizationRule[] = [
                        {
                            claim: 'email',
                            operator: AuthorizationOperator.ENDS_WITH,
                            value: ['@trusted.com', '@partner.com'],
                        },
                        {
                            claim: 'groups',
                            operator: AuthorizationOperator.CONTAINS,
                            value: ['vpn-access'],
                        },
                    ];

                    // Trusted domain with VPN access
                    expect(
                        evaluateAuthorizationRules(
                            rules,
                            { email: 'user@trusted.com', groups: 'vpn-access,developers' },
                            AuthorizationRuleMode.AND
                        )
                    ).toBe(true);

                    // Trusted domain without VPN access
                    expect(
                        evaluateAuthorizationRules(
                            rules,
                            { email: 'user@trusted.com', groups: 'developers' },
                            AuthorizationRuleMode.AND
                        )
                    ).toBe(false);
                });
            });
        });

        describe('Real-world scenarios', () => {
            it('should authorize Google Workspace users by domain', () => {
                const rule: OidcAuthorizationRule = {
                    claim: 'hd',
                    operator: AuthorizationOperator.EQUALS,
                    value: ['company.com'],
                };

                expect(evaluateRule(rule, { hd: 'company.com', email: 'user@company.com' })).toBe(true);
                expect(evaluateRule(rule, { hd: 'other.com', email: 'user@other.com' })).toBe(false);
            });

            it('should authorize users by email domain pattern', () => {
                const rule: OidcAuthorizationRule = {
                    claim: 'email',
                    operator: AuthorizationOperator.ENDS_WITH,
                    value: ['@company.com', '@subsidiary.company.com'],
                };

                expect(evaluateRule(rule, { email: 'john@company.com' })).toBe(true);
                expect(evaluateRule(rule, { email: 'jane@subsidiary.company.com' })).toBe(true);
                expect(evaluateRule(rule, { email: 'external@gmail.com' })).toBe(false);
            });

            it('should authorize specific users by subject ID', () => {
                const rule: OidcAuthorizationRule = {
                    claim: 'sub',
                    operator: AuthorizationOperator.EQUALS,
                    value: ['user123', 'user456', 'user789'],
                };

                expect(evaluateRule(rule, { sub: 'user456' })).toBe(true);
                expect(evaluateRule(rule, { sub: 'user999' })).toBe(false);
            });

            it('should authorize users in specific groups', () => {
                const rule: OidcAuthorizationRule = {
                    claim: 'groups',
                    operator: AuthorizationOperator.CONTAINS,
                    value: ['admin', 'developer'],
                };

                // When groups is a string containing the role
                expect(evaluateRule(rule, { groups: 'user,admin,viewer' })).toBe(true);
                expect(evaluateRule(rule, { groups: 'developer,tester' })).toBe(true);
                expect(evaluateRule(rule, { groups: 'user,viewer' })).toBe(false);
            });
        });
    });

    describe('checkAuthorization', () => {
        // Access the private method through any type casting for testing
        const checkAuthorization = async (provider: OidcProvider, claims: any): Promise<void> => {
            return (service as any).checkAuthorization(provider, claims);
        };

        it('should throw error when no authorization rules are configured', async () => {
            const provider: OidcProvider = {
                id: 'test',
                name: 'Test Provider',
                clientId: 'test-client',
                issuer: 'https://test.com',
                scopes: ['openid'],
                authorizationRules: [],
            } as OidcProvider;

            await expect(checkAuthorization(provider, { sub: 'user123' })).rejects.toThrow();
        });

        it('should throw error when authorization rules do not match', async () => {
            const provider: OidcProvider = {
                id: 'test',
                name: 'Test Provider',
                clientId: 'test-client',
                issuer: 'https://test.com',
                scopes: ['openid'],
                authorizationRules: [
                    {
                        claim: 'email',
                        operator: AuthorizationOperator.ENDS_WITH,
                        value: ['@company.com'],
                    },
                ],
            } as OidcProvider;

            await expect(checkAuthorization(provider, { email: 'user@other.com' })).rejects.toThrow(
                new UnauthorizedException(
                    'Access denied: Your account does not meet the authorization requirements for Test Provider.'
                )
            );
        });

        it('should not throw when authorization rules match', async () => {
            const provider: OidcProvider = {
                id: 'test',
                name: 'Test Provider',
                clientId: 'test-client',
                issuer: 'https://test.com',
                scopes: ['openid'],
                authorizationRules: [
                    {
                        claim: 'email',
                        operator: AuthorizationOperator.ENDS_WITH,
                        value: ['@company.com'],
                    },
                ],
            } as OidcProvider;

            await expect(
                checkAuthorization(provider, { email: 'user@company.com' })
            ).resolves.toBeUndefined();
        });

        it('should authorize when ANY rule matches (OR logic)', async () => {
            const provider: OidcProvider = {
                id: 'test',
                name: 'Test Provider',
                clientId: 'test-client',
                issuer: 'https://test.com',
                scopes: ['openid'],
                authorizationRules: [
                    {
                        claim: 'email',
                        operator: AuthorizationOperator.ENDS_WITH,
                        value: ['@company.com'],
                    },
                    {
                        claim: 'email',
                        operator: AuthorizationOperator.ENDS_WITH,
                        value: ['@partner.com'],
                    },
                    {
                        claim: 'sub',
                        operator: AuthorizationOperator.EQUALS,
                        value: ['specific-user-id'],
                    },
                ],
            } as OidcProvider;

            // Should pass with @partner.com email (second rule)
            await expect(
                checkAuthorization(provider, { email: 'user@partner.com', sub: 'other-id' })
            ).resolves.toBeUndefined();

            // Should pass with specific sub (third rule)
            await expect(
                checkAuthorization(provider, { email: 'user@external.com', sub: 'specific-user-id' })
            ).resolves.toBeUndefined();

            // Should fail when no rules match
            await expect(
                checkAuthorization(provider, { email: 'user@external.com', sub: 'other-id' })
            ).rejects.toThrow();
        });

        it('should authorize using AND mode when all rules match', async () => {
            const provider: OidcProvider = {
                id: 'test',
                name: 'Test Provider',
                clientId: 'test-client',
                issuer: 'https://test.com',
                scopes: ['openid'],
                authorizationRuleMode: AuthorizationRuleMode.AND,
                authorizationRules: [
                    {
                        claim: 'email',
                        operator: AuthorizationOperator.ENDS_WITH,
                        value: ['@company.com'],
                    },
                    {
                        claim: 'department',
                        operator: AuthorizationOperator.EQUALS,
                        value: ['engineering'],
                    },
                ],
            } as OidcProvider;

            // Should pass when both rules match
            await expect(
                checkAuthorization(provider, {
                    email: 'user@company.com',
                    department: 'engineering',
                })
            ).resolves.toBeUndefined();
        });

        it('should reject using AND mode when only some rules match', async () => {
            const provider: OidcProvider = {
                id: 'test',
                name: 'Test Provider',
                clientId: 'test-client',
                issuer: 'https://test.com',
                scopes: ['openid'],
                authorizationRuleMode: AuthorizationRuleMode.AND,
                authorizationRules: [
                    {
                        claim: 'email',
                        operator: AuthorizationOperator.ENDS_WITH,
                        value: ['@company.com'],
                    },
                    {
                        claim: 'department',
                        operator: AuthorizationOperator.EQUALS,
                        value: ['engineering'],
                    },
                ],
            } as OidcProvider;

            // Should fail when only first rule matches
            await expect(
                checkAuthorization(provider, {
                    email: 'user@company.com',
                    department: 'marketing',
                })
            ).rejects.toThrow();

            // Should fail when only second rule matches
            await expect(
                checkAuthorization(provider, {
                    email: 'user@external.com',
                    department: 'engineering',
                })
            ).rejects.toThrow();
        });

        it('should default to OR mode when authorizationRuleMode is not specified', async () => {
            const provider: OidcProvider = {
                id: 'test',
                name: 'Test Provider',
                clientId: 'test-client',
                issuer: 'https://test.com',
                scopes: ['openid'],
                // authorizationRuleMode not specified, should default to OR
                authorizationRules: [
                    {
                        claim: 'email',
                        operator: AuthorizationOperator.ENDS_WITH,
                        value: ['@company.com'],
                    },
                    {
                        claim: 'department',
                        operator: AuthorizationOperator.EQUALS,
                        value: ['engineering'],
                    },
                ],
            } as OidcProvider;

            // Should pass when only first rule matches (OR mode default)
            await expect(
                checkAuthorization(provider, {
                    email: 'user@company.com',
                    department: 'marketing',
                })
            ).resolves.toBeUndefined();
        });

        it('should work with OR mode explicitly set', async () => {
            const provider: OidcProvider = {
                id: 'test',
                name: 'Test Provider',
                clientId: 'test-client',
                issuer: 'https://test.com',
                scopes: ['openid'],
                authorizationRuleMode: AuthorizationRuleMode.OR,
                authorizationRules: [
                    {
                        claim: 'email',
                        operator: AuthorizationOperator.ENDS_WITH,
                        value: ['@company.com'],
                    },
                    {
                        claim: 'department',
                        operator: AuthorizationOperator.EQUALS,
                        value: ['engineering'],
                    },
                ],
            } as OidcProvider;

            // Should pass when only first rule matches
            await expect(
                checkAuthorization(provider, {
                    email: 'user@company.com',
                    department: 'marketing',
                })
            ).resolves.toBeUndefined();

            // Should pass when only second rule matches
            await expect(
                checkAuthorization(provider, {
                    email: 'user@external.com',
                    department: 'engineering',
                })
            ).resolves.toBeUndefined();
        });
    });
});
