/**
 * Frontend Integration Tests - Module Integration
 *
 * Tests for module-specific integration:
 * - Module switching and data isolation
 * - Cross-module data sharing
 * - Module-specific permissions
 * - Module loading performance
 * - Error handling across modules
 *
 * Author: Claude
 */

import React from 'react';
import { render, screen, fireEvent, waitFor, act } from '@testing-library/react';
import '@testing-library/jest-dom';
import { BrowserRouter, Routes, Route, useNavigate } from 'react-router-dom';
import userEvent from '@testing-library/user-event';

// Mock services
const mockModuleService = {
  getModules: jest.fn(),
  activateModule: jest.fn(),
  deactivateModule: jest.fn(),
  getModuleData: jest.fn(),
  validateModuleAccess: jest.fn(),
};

const mockPermissionService = {
  checkPermission: jest.fn(),
  getUserPermissions: jest.fn(),
};

const mockDataService = {
  getTenantData: jest.fn(),
  getSharedData: jest.fn(),
  updateSharedData: jest.fn(),
};

// Mock components for different modules
const ModuleSwitcher = ({ currentModule, onModuleChange, modules }) => (
  <div data-testid="module-switcher">
    <div data-testid="current-module">{currentModule}</div>
    <div data-testid="available-modules">
      {modules.map((module) => (
        <button
          key={module.id}
          data-testid={`module-${module.code}`}
          onClick={() => onModuleChange(module.code)}
          disabled={!module.enabled}
        >
          {module.name}
        </button>
      ))}
    </div>
  </div>
);

const RetailModule = ({ tenantId, userData }) => (
  <div data-testid="retail-module">
    <h2>Retail Management</h2>
    <div data-testid="tenant-data">{tenantId}</div>
    <div data-testid="user-data">{userData?.name}</div>
    <button data-testid="add-product-btn">Add Product</button>
    <button data-testid="view-sales-btn">View Sales</button>
    <div data-testid="retail-stats">
      <div>Total Products: 150</div>
      <div>Today's Sales: RM 2,450</div>
    </div>
  </div>
);

const HealthcareModule = ({ tenantId, userData }) => (
  <div data-testid="healthcare-module">
    <h2>Healthcare Management</h2>
    <div data-testid="tenant-data">{tenantId}</div>
    <div data-testid="user-data">{userData?.name}</div>
    <button data-testid="add-patient-btn">Add Patient</button>
    <button data-testid="schedule-appointment-btn">Schedule Appointment</button>
    <div data-testid="healthcare-stats">
      <div>Total Patients: 250</div>
      <div>Today's Appointments: 12</div>
    </div>
  </div>
);

const EducationModule = ({ tenantId, userData }) => (
  <div data-testid="education-module">
    <h2>Education Management</h2>
    <div data-testid="tenant-data">{tenantId}</div>
    <div data-testid="user-data">{userData?.name}</div>
    <button data-testid="add-student-btn">Add Student</button>
    <button data-testid="manage-classes-btn">Manage Classes</button>
    <div data-testid="education-stats">
      <div>Total Students: 500</div>
      <div>Active Classes: 25</div>
    </div>
  </div>
);

const LogisticsModule = ({ tenantId, userData }) => (
  <div data-testid="logistics-module">
    <h2>Logistics Management</h2>
    <div data-testid="tenant-data">{tenantId}</div>
    <div data-testid="user-data">{userData?.name}</div>
    <button data-testid="add-shipment-btn">Add Shipment</button>
    <button data-testid="track-vehicles-btn">Track Vehicles</button>
    <div data-testid="logistics-stats">
      <div>Active Shipments: 45</div>
      <div>Vehicle Fleet: 12</div>
    </div>
  </div>
);

const BeautyModule = ({ tenantId, userData }) => (
  <div data-testid="beauty-module">
    <h2>Beauty Management</h2>
    <div data-testid="tenant-data">{tenantId}</div>
    <div data-testid="user-data">{userData?.name}</div>
    <button data-testid="add-client-btn">Add Client</button>
    <button data-testid="manage-services-btn">Manage Services</button>
    <div data-testid="beauty-stats">
      <div>Total Clients: 180</div>
      <div>Active Services: 35</div>
    </div>
  </div>
);

const SharedDataPanel = ({ sharedData, onUpdateSharedData }) => (
  <div data-testid="shared-data-panel">
    <h3>Shared Data</h3>
    <div data-testid="shared-business-name">{sharedData?.businessName}</div>
    <div data-testid="shared-contact-email">{sharedData?.contactEmail}</div>
    <button data-testid="update-shared-data-btn" onClick={onUpdateSharedData}>
      Update Shared Data
    </button>
  </div>
);

const PermissionAlert = ({ hasPermission, requiredPermission }) => (
  <div data-testid="permission-alert">
    {hasPermission ? (
      <div data-testid="permission-granted">Access granted for {requiredPermission}</div>
    ) : (
      <div data-testid="permission-denied">Access denied for {requiredPermission}</div>
    )}
  </div>
);

const LoadingSpinner = () => (
  <div data-testid="loading-spinner">Loading module...</div>
);

const ErrorBoundary = ({ children, onError }) => {
  const [hasError, setHasError] = React.useState(false);
  const [error, setError] = React.useState(null);

  React.useEffect(() => {
    const handleError = (event) => {
      setHasError(true);
      setError(event.error);
      onError?.(event.error);
    };

    window.addEventListener('error', handleError);
    return () => window.removeEventListener('error', handleError);
  }, [onError]);

  if (hasError) {
    return (
      <div data-testid="error-boundary">
        <h2>Something went wrong</h2>
        <div data-testid="error-message">{error?.message}</div>
        <button onClick={() => setHasError(false)}>Try Again</button>
      </div>
    );
  }

  return <>{children}</>;
};

// Test app with module routing
const ModuleApp = () => {
  const [currentModule, setCurrentModule] = React.useState('retail');
  const [modules, setModules] = React.useState([]);
  const [sharedData, setSharedData] = React.useState(null);
  const [loading, setLoading] = React.useState(true);
  const [error, setError] = React.useState(null);
  const navigate = useNavigate();

  React.useEffect(() => {
    loadModules();
    loadSharedData();
  }, []);

  const loadModules = async () => {
    try {
      setLoading(true);
      const response = await mockModuleService.getModules();
      setModules(response.data);
    } catch (err) {
      setError(err);
    } finally {
      setLoading(false);
    }
  };

  const loadSharedData = async () => {
    try {
      const response = await mockDataService.getSharedData();
      setSharedData(response.data);
    } catch (err) {
      console.error('Failed to load shared data:', err);
    }
  };

  const handleModuleChange = async (moduleCode) => {
    try {
      await mockModuleService.validateModuleAccess(moduleCode);
      setCurrentModule(moduleCode);
      navigate(`/${moduleCode}`);
    } catch (err) {
      setError(err);
    }
  };

  const handleUpdateSharedData = async () => {
    try {
      const updatedData = { ...sharedData, businessName: 'Updated Business Name' };
      await mockDataService.updateSharedData(updatedData);
      setSharedData(updatedData);
    } catch (err) {
      setError(err);
    }
  };

  if (loading) {
    return <LoadingSpinner />;
  }

  if (error) {
    return (
      <div data-testid="error-container">
        <div data-testid="error-message">{error.message}</div>
        <button onClick={() => setError(null)}>Retry</button>
      </div>
    );
  }

  return (
    <div data-testid="module-app">
      <ModuleSwitcher
        currentModule={currentModule}
        onModuleChange={handleModuleChange}
        modules={modules}
      />

      <SharedDataPanel
        sharedData={sharedData}
        onUpdateSharedData={handleUpdateSharedData}
      />

      <Routes>
        <Route path="/retail" element={
          <ErrorBoundary onError={setError}>
            <RetailModule tenantId="tenant-001" userData={{ name: 'Test User' }} />
          </ErrorBoundary>
        } />
        <Route path="/healthcare" element={
          <ErrorBoundary onError={setError}>
            <HealthcareModule tenantId="tenant-001" userData={{ name: 'Test User' }} />
          </ErrorBoundary>
        } />
        <Route path="/education" element={
          <ErrorBoundary onError={setError}>
            <EducationModule tenantId="tenant-001" userData={{ name: 'Test User' }} />
          </ErrorBoundary>
        } />
        <Route path="/logistics" element={
          <ErrorBoundary onError={setError}>
            <LogisticsModule tenantId="tenant-001" userData={{ name: 'Test User' }} />
          </ErrorBoundary>
        } />
        <Route path="/beauty" element={
          <ErrorBoundary onError={setError}>
            <BeautyModule tenantId="tenant-001" userData={{ name: 'Test User' }} />
          </ErrorBoundary>
        } />
      </Routes>
    </div>
  );
};

describe('Module Integration Tests', () => {
  const user = userEvent.setup();

  beforeEach(() => {
    jest.clearAllMocks();

    // Mock default responses
    mockModuleService.getModules.mockResolvedValue({
      data: [
        { id: 1, code: 'retail', name: 'Retail Management', enabled: true },
        { id: 2, code: 'healthcare', name: 'Healthcare Management', enabled: true },
        { id: 3, code: 'education', name: 'Education Management', enabled: true },
        { id: 4, code: 'logistics', name: 'Logistics Management', enabled: true },
        { id: 5, code: 'beauty', name: 'Beauty Management', enabled: true },
      ]
    });

    mockDataService.getSharedData.mockResolvedValue({
      data: {
        businessName: 'Test Business Sdn Bhd',
        contactEmail: 'contact@test.com',
        address: '123 Test Street',
        phone: '+60123456789'
      }
    });

    mockModuleService.validateModuleAccess.mockResolvedValue(true);
    mockPermissionService.checkPermission.mockResolvedValue(true);
  });

  describe('Module Switching', () => {
    test('successfully switches between modules', async () => {
      render(
        <BrowserRouter initialEntries={['/retail']}>
          <ModuleApp />
        </BrowserRouter>
      );

      // Wait for initial load
      await waitFor(() => {
        expect(screen.getByTestId('module-app')).toBeInTheDocument();
      });

      // Should start with retail module
      expect(screen.getByTestId('current-module')).toHaveTextContent('retail');
      expect(screen.getByTestId('retail-module')).toBeInTheDocument();

      // Switch to healthcare module
      const healthcareButton = screen.getByTestId('module-healthcare');
      await user.click(healthcareButton);

      await waitFor(() => {
        expect(screen.getByTestId('current-module')).toHaveTextContent('healthcare');
        expect(screen.getByTestId('healthcare-module')).toBeInTheDocument();
        expect(screen.queryByTestId('retail-module')).not.toBeInTheDocument();
      });

      // Verify module access validation was called
      expect(mockModuleService.validateModuleAccess).toHaveBeenCalledWith('healthcare');
    });

    test('maintains shared data across modules', async () => {
      render(
        <BrowserRouter initialEntries={['/retail']}>
          <ModuleApp />
        </BrowserRouter>
      );

      await waitFor(() => {
        expect(screen.getByTestId('shared-data-panel')).toBeInTheDocument();
      });

      // Check shared data in retail module
      expect(screen.getByTestId('shared-business-name')).toHaveTextContent('Test Business Sdn Bhd');
      expect(screen.getByTestId('shared-contact-email')).toHaveTextContent('contact@test.com');

      // Switch to healthcare module
      const healthcareButton = screen.getByTestId('module-healthcare');
      await user.click(healthcareButton);

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

      // Shared data should still be available
      expect(screen.getByTestId('shared-business-name')).toHaveTextContent('Test Business Sdn Bhd');
      expect(screen.getByTestId('shared-contact-email')).toHaveTextContent('contact@test.com');
    });

    test('handles module loading states', async () => {
      // Mock slow loading
      mockModuleService.getModules.mockImplementationOnce(() =>
        new Promise(resolve => setTimeout(resolve, 1000))
      );

      render(
        <BrowserRouter initialEntries={['/retail']}>
          <ModuleApp />
        </BrowserRouter>
      );

      // Should show loading spinner initially
      expect(screen.getByTestId('loading-spinner')).toBeInTheDocument();

      // Wait for loading to complete
      await waitFor(() => {
        expect(screen.queryByTestId('loading-spinner')).not.toBeInTheDocument();
        expect(screen.getByTestId('module-app')).toBeInTheDocument();
      });
    });
  });

  describe('Module-Specific Features', () => {
    test.each([
      { module: 'retail', button: 'add-product-btn', stats: 'retail-stats' },
      { module: 'healthcare', button: 'add-patient-btn', stats: 'healthcare-stats' },
      { module: 'education', button: 'add-student-btn', stats: 'education-stats' },
      { module: 'logistics', button: 'add-shipment-btn', stats: 'logistics-stats' },
      { module: 'beauty', button: 'add-client-btn', stats: 'beauty-stats' },
    ])('loads $module module with correct features', async ({ module, button, stats }) => {
      render(
        <BrowserRouter initialEntries={['/${module}']}>
          <ModuleApp />
        </BrowserRouter>
      );

      await waitFor(() => {
        expect(screen.getByTestId(`${module}-module`)).toBeInTheDocument();
      });

      // Check module-specific buttons
      expect(screen.getByTestId(button)).toBeInTheDocument();

      // Check module-specific stats
      expect(screen.getByTestId(stats)).toBeInTheDocument();

      // Check tenant data isolation
      expect(screen.getByTestId('tenant-data')).toHaveTextContent('tenant-001');

      // Check user data sharing
      expect(screen.getByTestId('user-data')).toHaveTextContent('Test User');
    });
  });

  describe('Permission Management', () => {
    test('validates module access permissions', async () => {
      // Mock permission denied for logistics module
      mockModuleService.validateModuleAccess.mockImplementationOnce((moduleCode) => {
        if (moduleCode === 'logistics') {
          return Promise.reject(new Error('Access denied'));
        }
        return Promise.resolve(true);
      });

      render(
        <BrowserRouter initialEntries={['/retail']}>
          <ModuleApp />
        </BrowserRouter>
      );

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

      // Try to access logistics module
      const logisticsButton = screen.getByTestId('module-logistics');
      await user.click(logisticsButton);

      await waitFor(() => {
        expect(screen.getByTestId('error-container')).toBeInTheDocument();
        expect(screen.getByTestId('error-message')).toHaveTextContent('Access denied');
      });

      // Should not switch to logistics module
      expect(screen.queryByTestId('logistics-module')).not.toBeInTheDocument();
      expect(screen.getByTestId('retail-module')).toBeInTheDocument();
    });

    test('checks feature-level permissions', async () => {
      mockPermissionService.checkPermission.mockImplementation((permission) => {
        const deniedPermissions = ['delete_products', 'manage_patients'];
        return Promise.resolve(!deniedPermissions.includes(permission));
      });

      render(
        <BrowserRouter initialEntries={['/retail']}>
          <ModuleApp />
        </BrowserRouter>
      );

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

      // Test retail permissions
      expect(await mockPermissionService.checkPermission('view_products')).resolves.toBe(true);
      expect(await mockPermissionService.checkPermission('delete_products')).resolves.toBe(false);
    });
  });

  describe('Data Isolation and Sharing', () => {
    test('maintains tenant data isolation', async () => {
      // Mock different tenant data for different modules
      mockDataService.getTenantData.mockImplementation((module) => {
        const tenantData = {
          retail: { id: 'tenant-retail', name: 'Retail Business' },
          healthcare: { id: 'tenant-healthcare', name: 'Healthcare Business' },
        };
        return Promise.resolve({ data: tenantData[module] });
      });

      render(
        <BrowserRouter initialEntries={['/retail']}>
          <ModuleApp />
        </BrowserRouter>
      );

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

      // Check retail tenant data
      expect(screen.getByTestId('tenant-data')).toHaveTextContent('tenant-001');

      // Switch to healthcare module
      const healthcareButton = screen.getByTestId('module-healthcare');
      await user.click(healthcareButton);

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

      // Should still use the same tenant (data isolation at tenant level)
      expect(screen.getByTestId('tenant-data')).toHaveTextContent('tenant-001');
    });

    test('updates shared data across modules', async () => {
      render(
        <BrowserRouter initialEntries={['/retail']}>
          <ModuleApp />
        </BrowserRouter>
      );

      await waitFor(() => {
        expect(screen.getByTestId('shared-data-panel')).toBeInTheDocument();
      });

      // Update shared data
      const updateButton = screen.getByTestId('update-shared-data-btn');
      await user.click(updateButton);

      await waitFor(() => {
        expect(mockDataService.updateSharedData).toHaveBeenCalled();
        expect(screen.getByTestId('shared-business-name')).toHaveTextContent('Updated Business Name');
      });

      // Switch to another module
      const healthcareButton = screen.getByTestId('module-healthcare');
      await user.click(healthcareButton);

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

      // Updated shared data should be available
      expect(screen.getByTestId('shared-business-name')).toHaveTextContent('Updated Business Name');
    });
  });

  describe('Error Handling', () => {
    test('handles module loading errors gracefully', async () => {
      mockModuleService.getModules.mockRejectedValueOnce(new Error('Failed to load modules'));

      render(
        <BrowserRouter initialEntries={['/retail']}>
          <ModuleApp />
        </BrowserRouter>
      );

      await waitFor(() => {
        expect(screen.getByTestId('error-container')).toBeInTheDocument();
        expect(screen.getByTestId('error-message')).toHaveTextContent('Failed to load modules');
      });
    });

    test('handles individual module errors', async () => {
      render(
        <BrowserRouter initialEntries={['/retail']}>
          <ModuleApp />
        </BrowserRouter>
      );

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

      // Simulate module error
      const error = new Error('Module crashed');
      fireEvent.error(window, error);

      await waitFor(() => {
        expect(screen.getByTestId('error-boundary')).toBeInTheDocument();
        expect(screen.getByTestId('error-message')).toHaveTextContent('Module crashed');
      });
    });

    test('recovers from errors with retry', async () => {
      mockModuleService.getModules.mockRejectedValueOnce(new Error('Network error'));

      render(
        <BrowserRouter initialEntries={['/retail']}>
          <ModuleApp />
        </BrowserRouter>
      );

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

      // Mock successful retry
      mockModuleService.getModules.mockResolvedValueOnce({
        data: [
          { id: 1, code: 'retail', name: 'Retail Management', enabled: true },
        ]
      });

      // Click retry button
      const retryButton = screen.getByText('Retry');
      await user.click(retryButton);

      await waitFor(() => {
        expect(screen.getByTestId('module-app')).toBeInTheDocument();
        expect(screen.getByTestId('retail-module')).toBeInTheDocument();
      });
    });
  });

  describe('Performance Optimization', () => {
    test('lazy loads modules on demand', async () => {
      const mockModuleLoad = jest.fn();

      // Mock dynamic import
      jest.mock('react-lazy', () => ({
        lazy: (importFn) => {
          mockModuleLoad();
          return importFn();
        }
      }));

      render(
        <BrowserRouter initialEntries={['/retail']}>
          <ModuleApp />
        </BrowserRouter>
      );

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

      // Module should be loaded on demand
      expect(mockModuleLoad).toHaveBeenCalled();
    });

    test('caches loaded modules', async () => {
      render(
        <BrowserRouter initialEntries={['/retail']}>
          <ModuleApp />
        </BrowserRouter>
      );

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

      // Switch away and back to retail module
      const healthcareButton = screen.getByTestId('module-healthcare');
      await user.click(healthcareButton);

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

      const retailButton = screen.getByTestId('module-retail');
      await user.click(retailButton);

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

      // Should not reload modules (check call count)
      expect(mockModuleService.getModules).toHaveBeenCalledTimes(1);
    });
  });
});