/**
 * Tests d'intégration pour l'authentification Keycloak
 * Vérifie que l'intégration fonctionne correctement avec l'application existante
 */

import React from 'react';
import { render, screen, waitFor, fireEvent } from '@testing-library/react';
import { useRouter } from 'next/navigation';
import { AuthProvider, useAuth } from '../../contexts/AuthContext';
import ProtectedRoute from '../../components/auth/ProtectedRoute';
import ProtectedLayout from '../../components/ProtectedLayout';

// Mock Next.js router
jest.mock('next/navigation', () => ({
  useRouter: jest.fn(),
  useSearchParams: jest.fn(() => ({
    get: jest.fn(() => null)
  }))
}));

// Mock Keycloak
const mockKeycloak = {
  init: jest.fn(() => Promise.resolve(false)),
  login: jest.fn(),
  logout: jest.fn(),
  updateToken: jest.fn(() => Promise.resolve(false)),
  authenticated: false,
  token: null,
  refreshToken: null,
  tokenParsed: null,
  onTokenExpired: null,
  onAuthRefreshSuccess: null,
  onAuthRefreshError: null,
  onAuthLogout: null,
};

jest.mock('../../config/keycloak', () => ({
  keycloak: mockKeycloak,
  keycloakInitOptions: {},
  KEYCLOAK_TIMEOUTS: {
    TOKEN_REFRESH_BEFORE_EXPIRY: 30,
    SESSION_CHECK_INTERVAL: 60,
    SILENT_CHECK_SSO_TIMEOUT: 5000,
  },
  RoleUtils: {
    hasRole: jest.fn(() => false),
    hasPermission: jest.fn(() => false),
    hasAnyRole: jest.fn(() => false),
    getHighestRole: jest.fn(() => 'USER'),
  }
}));

// Mock PrimeReact components
jest.mock('primereact/api', () => ({
  PrimeReactProvider: ({ children }: { children: React.ReactNode }) => <div>{children}</div>
}));

// Mock Layout components
jest.mock('../../layout/context/layoutcontext', () => ({
  LayoutProvider: ({ children }: { children: React.ReactNode }) => <div>{children}</div>
}));

const mockPush = jest.fn();
const mockBack = jest.fn();

beforeEach(() => {
  jest.clearAllMocks();
  (useRouter as jest.Mock).mockReturnValue({
    push: mockPush,
    back: mockBack,
    pathname: '/dashboard',
    query: {},
  });
});

// Composant de test pour vérifier l'authentification
const TestAuthComponent = () => {
  const { isAuthenticated, isLoading, user, login, logout } = useAuth();

  if (isLoading) {
    return <div data-testid="loading">Chargement...</div>;
  }

  return (
    <div>
      <div data-testid="auth-status">
        {isAuthenticated ? 'Authentifié' : 'Non authentifié'}
      </div>
      {user && (
        <div data-testid="user-info">
          {user.username} - {user.email}
        </div>
      )}
      <button data-testid="login-btn" onClick={login}>
        Se connecter
      </button>
      <button data-testid="logout-btn" onClick={logout}>
        Se déconnecter
      </button>
    </div>
  );
};

// Composant de test pour vérifier la protection des routes
const TestProtectedComponent = () => {
  return <div data-testid="protected-content">Contenu protégé</div>;
};

describe('Intégration Authentification Keycloak', () => {
  describe('AuthProvider', () => {
    it('devrait initialiser Keycloak au montage', async () => {
      render(
        <AuthProvider>
          <TestAuthComponent />
        </AuthProvider>
      );

      await waitFor(() => {
        expect(mockKeycloak.init).toHaveBeenCalledWith(expect.any(Object));
      });
    });

    it('devrait afficher le statut de chargement initial', () => {
      render(
        <AuthProvider>
          <TestAuthComponent />
        </AuthProvider>
      );

      expect(screen.getByTestId('loading')).toBeInTheDocument();
    });

    it('devrait afficher le statut non authentifié après initialisation', async () => {
      mockKeycloak.authenticated = false;
      
      render(
        <AuthProvider>
          <TestAuthComponent />
        </AuthProvider>
      );

      await waitFor(() => {
        expect(screen.getByTestId('auth-status')).toHaveTextContent('Non authentifié');
      });
    });

    it('devrait appeler keycloak.login lors du clic sur connexion', async () => {
      mockKeycloak.authenticated = false;
      
      render(
        <AuthProvider>
          <TestAuthComponent />
        </AuthProvider>
      );

      await waitFor(() => {
        expect(screen.getByTestId('login-btn')).toBeInTheDocument();
      });

      fireEvent.click(screen.getByTestId('login-btn'));
      expect(mockKeycloak.login).toHaveBeenCalled();
    });
  });

  describe('ProtectedRoute', () => {
    it('devrait rediriger vers login si non authentifié', async () => {
      mockKeycloak.authenticated = false;

      render(
        <AuthProvider>
          <ProtectedRoute>
            <TestProtectedComponent />
          </ProtectedRoute>
        </AuthProvider>
      );

      await waitFor(() => {
        expect(mockPush).toHaveBeenCalledWith(expect.stringContaining('/auth/login'));
      });
    });

    it('devrait afficher le contenu si authentifié', async () => {
      mockKeycloak.authenticated = true;
      mockKeycloak.tokenParsed = {
        preferred_username: 'testuser',
        email: 'test@example.com',
        realm_access: { roles: ['USER'] }
      };

      render(
        <AuthProvider>
          <ProtectedRoute>
            <TestProtectedComponent />
          </ProtectedRoute>
        </AuthProvider>
      );

      await waitFor(() => {
        expect(screen.getByTestId('protected-content')).toBeInTheDocument();
      });
    });

    it('devrait rediriger vers forbidden si rôle insuffisant', async () => {
      mockKeycloak.authenticated = true;
      mockKeycloak.tokenParsed = {
        preferred_username: 'testuser',
        email: 'test@example.com',
        realm_access: { roles: ['USER'] }
      };

      render(
        <AuthProvider>
          <ProtectedRoute requiredRoles={['ADMIN']}>
            <TestProtectedComponent />
          </ProtectedRoute>
        </AuthProvider>
      );

      await waitFor(() => {
        expect(mockPush).toHaveBeenCalledWith('/auth/forbidden');
      });
    });
  });

  describe('ProtectedLayout', () => {
    it('devrait protéger le layout principal', async () => {
      mockKeycloak.authenticated = false;

      render(
        <AuthProvider>
          <ProtectedLayout>
            <div data-testid="layout-content">Contenu du layout</div>
          </ProtectedLayout>
        </AuthProvider>
      );

      await waitFor(() => {
        expect(mockPush).toHaveBeenCalledWith(expect.stringContaining('/auth/login'));
      });
    });

    it('devrait afficher le contenu du layout si authentifié', async () => {
      mockKeycloak.authenticated = true;
      mockKeycloak.tokenParsed = {
        preferred_username: 'testuser',
        email: 'test@example.com',
        realm_access: { roles: ['USER'] }
      };

      render(
        <AuthProvider>
          <ProtectedLayout>
            <div data-testid="layout-content">Contenu du layout</div>
          </ProtectedLayout>
        </AuthProvider>
      );

      await waitFor(() => {
        expect(screen.getByTestId('layout-content')).toBeInTheDocument();
      });
    });
  });

  describe('Gestion des erreurs', () => {
    it('devrait gérer les erreurs d\'initialisation Keycloak', async () => {
      mockKeycloak.init.mockRejectedValueOnce(new Error('Erreur Keycloak'));

      render(
        <AuthProvider>
          <TestAuthComponent />
        </AuthProvider>
      );

      await waitFor(() => {
        expect(screen.getByTestId('auth-status')).toHaveTextContent('Non authentifié');
      });
    });

    it('devrait gérer les erreurs de rafraîchissement de token', async () => {
      mockKeycloak.authenticated = true;
      mockKeycloak.updateToken.mockRejectedValueOnce(new Error('Token expiré'));

      render(
        <AuthProvider>
          <TestAuthComponent />
        </AuthProvider>
      );

      await waitFor(() => {
        expect(mockKeycloak.logout).toHaveBeenCalled();
      });
    });
  });
});

describe('Intégration avec les services API', () => {
  it('devrait ajouter le token Keycloak aux requêtes API', async () => {
    // Ce test nécessiterait de mocker axios et de vérifier que les interceptors
    // ajoutent correctement le token Keycloak aux en-têtes
    // Pour l'instant, nous vérifions juste que les services sont importables
    const { ApiService } = await import('../../services/ApiService');
    expect(ApiService).toBeDefined();
  });
});
