import { describe, expect, it, vi } from 'vitest';
import { renderHook } from '@testing-library/react';
import { usePermissions } from './usePermissions';
import type { CurrentUser } from '@/shared/types/auth';

const mockUser: CurrentUser = {
  id: 'user-1',
  email: 'test@example.com',
  firstName: 'Test',
  lastName: 'User',
  permissions: ['READ_CAMPUSES', 'READ_DASHBOARD', 'READ_FRAME'],
  app_role: { name: 'teacher', globalAccess: false },
};

const mockGlobalUser: CurrentUser = {
  id: 'admin-1',
  email: 'admin@example.com',
  firstName: 'Admin',
  lastName: 'User',
  permissions: [],
  app_role: { name: 'super_admin', globalAccess: true },
};

// Mock the auth context
vi.mock('@/contexts/useAuth', () => ({
  useAuth: vi.fn(),
}));

import { useAuth } from '@/contexts/useAuth';

describe('usePermissions', () => {
  it('returns empty permissions array for null user', () => {
    vi.mocked(useAuth).mockReturnValue({
      user: null,
      profile: null,
      loading: false,
      isAuthenticated: false,
      signIn: vi.fn(),
      signUp: vi.fn(),
      signOut: vi.fn(),
      updateProfile: vi.fn(),
    });

    const { result } = renderHook(() => usePermissions());

    expect(result.current.permissions).toEqual([]);
  });

  it('returns user permissions array when user exists', () => {
    vi.mocked(useAuth).mockReturnValue({
      user: mockUser,
      profile: null,
      loading: false,
      isAuthenticated: true,
      signIn: vi.fn(),
      signUp: vi.fn(),
      signOut: vi.fn(),
      updateProfile: vi.fn(),
    });

    const { result } = renderHook(() => usePermissions());

    expect(result.current.permissions).toEqual(['READ_CAMPUSES', 'READ_DASHBOARD', 'READ_FRAME']);
  });

  describe('has()', () => {
    it('returns true for granted permission', () => {
      vi.mocked(useAuth).mockReturnValue({
        user: mockUser,
        profile: null,
        loading: false,
        isAuthenticated: true,
        signIn: vi.fn(),
        signUp: vi.fn(),
        signOut: vi.fn(),
        updateProfile: vi.fn(),
      });

      const { result } = renderHook(() => usePermissions());

      expect(result.current.has('READ_CAMPUSES')).toBe(true);
    });

    it('returns false for missing permission', () => {
      vi.mocked(useAuth).mockReturnValue({
        user: mockUser,
        profile: null,
        loading: false,
        isAuthenticated: true,
        signIn: vi.fn(),
        signUp: vi.fn(),
        signOut: vi.fn(),
        updateProfile: vi.fn(),
      });

      const { result } = renderHook(() => usePermissions());

      expect(result.current.has('DELETE_ORGANIZATIONS')).toBe(false);
    });

    it('returns true for any permission when user has globalAccess', () => {
      vi.mocked(useAuth).mockReturnValue({
        user: mockGlobalUser,
        profile: null,
        loading: false,
        isAuthenticated: true,
        signIn: vi.fn(),
        signUp: vi.fn(),
        signOut: vi.fn(),
        updateProfile: vi.fn(),
      });

      const { result } = renderHook(() => usePermissions());

      expect(result.current.has('DELETE_ORGANIZATIONS')).toBe(true);
      expect(result.current.has('UPDATE_USERS')).toBe(true);
    });

    it('returns false for null user', () => {
      vi.mocked(useAuth).mockReturnValue({
        user: null,
        profile: null,
        loading: false,
        isAuthenticated: false,
        signIn: vi.fn(),
        signUp: vi.fn(),
        signOut: vi.fn(),
        updateProfile: vi.fn(),
      });

      const { result } = renderHook(() => usePermissions());

      expect(result.current.has('READ_CAMPUSES')).toBe(false);
    });
  });

  describe('hasAny()', () => {
    it('returns true if any permission matches', () => {
      vi.mocked(useAuth).mockReturnValue({
        user: mockUser,
        profile: null,
        loading: false,
        isAuthenticated: true,
        signIn: vi.fn(),
        signUp: vi.fn(),
        signOut: vi.fn(),
        updateProfile: vi.fn(),
      });

      const { result } = renderHook(() => usePermissions());

      expect(result.current.hasAny(['READ_CAMPUSES', 'DELETE_ORGANIZATIONS'])).toBe(true);
    });

    it('returns false if no permissions match', () => {
      vi.mocked(useAuth).mockReturnValue({
        user: mockUser,
        profile: null,
        loading: false,
        isAuthenticated: true,
        signIn: vi.fn(),
        signUp: vi.fn(),
        signOut: vi.fn(),
        updateProfile: vi.fn(),
      });

      const { result } = renderHook(() => usePermissions());

      expect(result.current.hasAny(['DELETE_ORGANIZATIONS', 'UPDATE_USERS'])).toBe(false);
    });

    it('returns true for any permissions when user has globalAccess', () => {
      vi.mocked(useAuth).mockReturnValue({
        user: mockGlobalUser,
        profile: null,
        loading: false,
        isAuthenticated: true,
        signIn: vi.fn(),
        signUp: vi.fn(),
        signOut: vi.fn(),
        updateProfile: vi.fn(),
      });

      const { result } = renderHook(() => usePermissions());

      expect(result.current.hasAny(['DELETE_ORGANIZATIONS', 'UPDATE_USERS'])).toBe(true);
    });

    it('returns false for null user', () => {
      vi.mocked(useAuth).mockReturnValue({
        user: null,
        profile: null,
        loading: false,
        isAuthenticated: false,
        signIn: vi.fn(),
        signUp: vi.fn(),
        signOut: vi.fn(),
        updateProfile: vi.fn(),
      });

      const { result } = renderHook(() => usePermissions());

      expect(result.current.hasAny(['READ_CAMPUSES'])).toBe(false);
    });
  });

  describe('hasAll()', () => {
    it('returns true if all permissions match', () => {
      vi.mocked(useAuth).mockReturnValue({
        user: mockUser,
        profile: null,
        loading: false,
        isAuthenticated: true,
        signIn: vi.fn(),
        signUp: vi.fn(),
        signOut: vi.fn(),
        updateProfile: vi.fn(),
      });

      const { result } = renderHook(() => usePermissions());

      expect(result.current.hasAll(['READ_CAMPUSES', 'READ_DASHBOARD'])).toBe(true);
    });

    it('returns false if any permission is missing', () => {
      vi.mocked(useAuth).mockReturnValue({
        user: mockUser,
        profile: null,
        loading: false,
        isAuthenticated: true,
        signIn: vi.fn(),
        signUp: vi.fn(),
        signOut: vi.fn(),
        updateProfile: vi.fn(),
      });

      const { result } = renderHook(() => usePermissions());

      expect(result.current.hasAll(['READ_CAMPUSES', 'DELETE_ORGANIZATIONS'])).toBe(false);
    });

    it('returns true for all permissions when user has globalAccess', () => {
      vi.mocked(useAuth).mockReturnValue({
        user: mockGlobalUser,
        profile: null,
        loading: false,
        isAuthenticated: true,
        signIn: vi.fn(),
        signUp: vi.fn(),
        signOut: vi.fn(),
        updateProfile: vi.fn(),
      });

      const { result } = renderHook(() => usePermissions());

      expect(result.current.hasAll(['DELETE_ORGANIZATIONS', 'UPDATE_USERS'])).toBe(true);
    });

    it('returns false for null user', () => {
      vi.mocked(useAuth).mockReturnValue({
        user: null,
        profile: null,
        loading: false,
        isAuthenticated: false,
        signIn: vi.fn(),
        signUp: vi.fn(),
        signOut: vi.fn(),
        updateProfile: vi.fn(),
      });

      const { result } = renderHook(() => usePermissions());

      expect(result.current.hasAll(['READ_CAMPUSES'])).toBe(false);
    });
  });

  describe('memoization', () => {
    it('returns same object reference when user does not change', () => {
      vi.mocked(useAuth).mockReturnValue({
        user: mockUser,
        profile: null,
        loading: false,
        isAuthenticated: true,
        signIn: vi.fn(),
        signUp: vi.fn(),
        signOut: vi.fn(),
        updateProfile: vi.fn(),
      });

      const { result, rerender } = renderHook(() => usePermissions());

      const firstResult = result.current;
      rerender();
      const secondResult = result.current;

      expect(firstResult).toBe(secondResult);
    });
  });
});
