/**
 * LiveComponents E2E Tests - Error Recovery & Fallbacks
 *
 * Tests for robust error handling and graceful degradation:
 * - Network error recovery
 * - Server error handling with user feedback
 * - Graceful degradation when JavaScript fails
 * - Retry mechanisms with exponential backoff
 * - Offline mode detection and handling
 * - State recovery after errors
 *
 * @see src/Framework/LiveComponents/Services/ErrorRecoveryService.php
 * @see resources/js/modules/LiveComponent.js (error handling)
 */

import { test, expect } from '@playwright/test';

test.describe('LiveComponents - Error Recovery & Fallbacks', () => {
    test.beforeEach(async ({ page }) => {
        // Navigate to error recovery test page
        await page.goto('/livecomponents/error-test');

        // Wait for LiveComponent to be initialized
        await page.waitForFunction(() => window.LiveComponent !== undefined);
    });

    test('should show user-friendly error message on network failure', async ({ page }) => {
        // Simulate network failure
        await page.route('**/livecomponent/**', route => route.abort('failed'));

        // Trigger action
        await page.click('[data-lc-action="submit"]');

        // Should show error notification
        const errorNotification = page.locator('.livecomponent-error');
        await expect(errorNotification).toBeVisible();
        await expect(errorNotification).toContainText(/network|connection|failed/i);

        // Should offer retry option
        const retryButton = page.locator('[data-action="retry"]');
        await expect(retryButton).toBeVisible();
    });

    test('should automatically retry on transient network errors', async ({ page }) => {
        let attemptCount = 0;

        // Fail first 2 attempts, succeed on 3rd
        await page.route('**/livecomponent/**', route => {
            attemptCount++;
            if (attemptCount <= 2) {
                route.abort('failed');
            } else {
                route.continue();
            }
        });

        // Trigger action
        await page.click('[data-lc-action="submit"]');

        // Should eventually succeed after retries
        await page.waitForResponse(response =>
            response.url().includes('/livecomponent') &&
            response.status() === 200,
            { timeout: 10000 }
        );

        // Success notification
        const successNotification = page.locator('.livecomponent-success');
        await expect(successNotification).toBeVisible();

        // Should have attempted 3 times
        expect(attemptCount).toBe(3);
    });

    test('should use exponential backoff for retries', async ({ page }) => {
        const retryAttempts = [];

        await page.exposeFunction('onRetryAttempt', (attempt) => {
            retryAttempts.push(attempt);
        });

        await page.evaluate(() => {
            window.addEventListener('livecomponent:retry-attempt', (e) => {
                window.onRetryAttempt({
                    timestamp: Date.now(),
                    attemptNumber: e.detail.attemptNumber,
                    delay: e.detail.delay
                });
            });
        });

        // Simulate multiple failures
        await page.route('**/livecomponent/**', route => route.abort('failed'));

        // Trigger action
        await page.click('[data-lc-action="submit"]');

        // Wait for multiple retry attempts
        await page.waitForTimeout(10000);

        // Should have multiple attempts with increasing delays
        expect(retryAttempts.length).toBeGreaterThan(1);

        // Delays should increase exponentially
        if (retryAttempts.length >= 3) {
            const delay1 = retryAttempts[1].delay;
            const delay2 = retryAttempts[2].delay;
            expect(delay2).toBeGreaterThan(delay1);
        }
    });

    test('should stop retrying after max attempts', async ({ page }) => {
        let attemptCount = 0;

        // Always fail
        await page.route('**/livecomponent/**', route => {
            attemptCount++;
            route.abort('failed');
        });

        // Trigger action
        await page.click('[data-lc-action="submit"]');

        // Wait for all retry attempts
        await page.waitForTimeout(15000);

        // Should stop after max attempts (typically 3-5)
        expect(attemptCount).toBeLessThanOrEqual(5);

        // Should show final error message
        const errorNotification = page.locator('.livecomponent-error');
        await expect(errorNotification).toBeVisible();
        await expect(errorNotification).toContainText(/unable|failed|try again later/i);
    });

    test('should handle server 500 errors gracefully', async ({ page }) => {
        // Mock 500 error
        await page.route('**/livecomponent/**', route => {
            route.fulfill({
                status: 500,
                body: JSON.stringify({ error: 'Internal Server Error' })
            });
        });

        // Trigger action
        await page.click('[data-lc-action="submit"]');

        // Should show server error message
        const errorNotification = page.locator('.livecomponent-error');
        await expect(errorNotification).toBeVisible();
        await expect(errorNotification).toContainText(/server error|something went wrong/i);

        // Should log error for debugging
        const consoleLogs = [];
        page.on('console', msg => {
            if (msg.type() === 'error') {
                consoleLogs.push(msg.text());
            }
        });

        await page.waitForTimeout(500);

        // Should have logged error details
        expect(consoleLogs.some(log => log.includes('500'))).toBe(true);
    });

    test('should handle validation errors with field-specific feedback', async ({ page }) => {
        // Mock validation errors
        await page.route('**/livecomponent/**', route => {
            route.fulfill({
                status: 422,
                body: JSON.stringify({
                    success: false,
                    errors: {
                        email: 'Email is invalid',
                        password: 'Password must be at least 8 characters'
                    }
                })
            });
        });

        // Trigger form submission
        await page.click('[data-lc-action="submit"]');

        // Wait for validation errors
        await page.waitForTimeout(1000);

        // Should show field-specific errors
        const emailError = page.locator('.validation-error[data-field="email"]');
        await expect(emailError).toBeVisible();
        await expect(emailError).toContainText('Email is invalid');

        const passwordError = page.locator('.validation-error[data-field="password"]');
        await expect(passwordError).toBeVisible();
        await expect(passwordError).toContainText('at least 8 characters');

        // Form should still be editable
        const emailInput = page.locator('input[name="email"]');
        await expect(emailInput).toBeEnabled();
    });

    test('should detect offline mode and queue actions', async ({ page }) => {
        // Simulate going offline
        await page.context().setOffline(true);

        // Trigger action while offline
        await page.click('[data-lc-action="submit"]');

        // Should show offline notification
        const offlineNotification = page.locator('.livecomponent-offline');
        await expect(offlineNotification).toBeVisible();
        await expect(offlineNotification).toContainText(/offline|no connection/i);

        // Action should be queued
        const queuedActions = await page.evaluate(() => {
            return window.LiveComponent.getQueuedActions();
        });

        expect(queuedActions.length).toBeGreaterThan(0);

        // Go back online
        await page.context().setOffline(false);

        // Wait for auto-retry
        await page.waitForResponse(response =>
            response.url().includes('/livecomponent'),
            { timeout: 10000 }
        );

        // Queued actions should be executed
        const remainingActions = await page.evaluate(() => {
            return window.LiveComponent.getQueuedActions();
        });

        expect(remainingActions.length).toBe(0);
    });

    test('should preserve component state after error recovery', async ({ page }) => {
        // Fill form
        await page.fill('input[name="title"]', 'Test Title');
        await page.fill('input[name="description"]', 'Test Description');

        // Mock temporary error
        let failOnce = true;
        await page.route('**/livecomponent/**', route => {
            if (failOnce) {
                failOnce = false;
                route.abort('failed');
            } else {
                route.continue();
            }
        });

        // Trigger action
        await page.click('[data-lc-action="submit"]');

        // Wait for retry and success
        await page.waitForResponse(response =>
            response.url().includes('/livecomponent') &&
            response.status() === 200,
            { timeout: 10000 }
        );

        // Form values should be preserved
        await expect(page.locator('input[name="title"]')).toHaveValue('Test Title');
        await expect(page.locator('input[name="description"]')).toHaveValue('Test Description');
    });

    test('should fallback to full page reload on critical JavaScript error', async ({ page }) => {
        // Inject JavaScript error in LiveComponent
        await page.evaluate(() => {
            const originalHandle = window.LiveComponent.handleAction;
            window.LiveComponent.handleAction = function() {
                throw new Error('Critical JavaScript error');
            };
        });

        // Track page reload
        let pageReloaded = false;
        page.on('load', () => {
            pageReloaded = true;
        });

        // Trigger action
        await page.click('[data-lc-action="submit"]');

        // Wait for fallback
        await page.waitForTimeout(3000);

        // Should fallback to traditional form submission or page reload
        // (depending on framework implementation)
        const errorFallback = page.locator('.livecomponent-fallback');
        const hasErrorFallback = await errorFallback.isVisible().catch(() => false);

        // Either fallback indicator or page reload should occur
        expect(hasErrorFallback || pageReloaded).toBe(true);
    });

    test('should handle timeout errors with user feedback', async ({ page }) => {
        // Mock slow server (timeout)
        await page.route('**/livecomponent/**', async route => {
            await new Promise(resolve => setTimeout(resolve, 10000));
            route.continue();
        });

        // Trigger action
        await page.click('[data-lc-action="submit"]');

        // Should show timeout error (before 10s)
        const timeoutError = page.locator('.livecomponent-timeout');
        await expect(timeoutError).toBeVisible({ timeout: 6000 });
        await expect(timeoutError).toContainText(/timeout|taking too long/i);

        // Should offer retry or cancel
        const retryButton = page.locator('[data-action="retry"]');
        const cancelButton = page.locator('[data-action="cancel"]');

        await expect(retryButton).toBeVisible();
        await expect(cancelButton).toBeVisible();
    });

    test('should allow manual retry after error', async ({ page }) => {
        // Mock error
        let shouldFail = true;
        await page.route('**/livecomponent/**', route => {
            if (shouldFail) {
                route.abort('failed');
            } else {
                route.continue();
            }
        });

        // Trigger action (will fail)
        await page.click('[data-lc-action="submit"]');

        // Wait for error
        await expect(page.locator('.livecomponent-error')).toBeVisible();

        // Fix the error condition
        shouldFail = false;

        // Click retry button
        await page.click('[data-action="retry"]');

        // Should succeed on retry
        await page.waitForResponse(response =>
            response.url().includes('/livecomponent') &&
            response.status() === 200
        );

        const successNotification = page.locator('.livecomponent-success');
        await expect(successNotification).toBeVisible();
    });

    test('should handle CSRF token expiration', async ({ page }) => {
        // Mock CSRF token error
        await page.route('**/livecomponent/**', route => {
            route.fulfill({
                status: 403,
                body: JSON.stringify({ error: 'CSRF token mismatch' })
            });
        });

        // Trigger action
        await page.click('[data-lc-action="submit"]');

        // Should detect CSRF error
        const csrfError = page.locator('.livecomponent-csrf-error');
        await expect(csrfError).toBeVisible();

        // Should automatically refresh CSRF token and retry
        // (framework-specific implementation)
        await expect(csrfError).toContainText(/security|session|refresh/i);
    });

    test('should show loading state during error recovery', async ({ page }) => {
        let attemptCount = 0;

        await page.route('**/livecomponent/**', route => {
            attemptCount++;
            if (attemptCount <= 2) {
                setTimeout(() => route.abort('failed'), 500);
            } else {
                route.continue();
            }
        });

        // Trigger action
        await page.click('[data-lc-action="submit"]');

        // Loading indicator should persist during retries
        const loadingIndicator = page.locator('.livecomponent-loading');
        await expect(loadingIndicator).toBeVisible();

        // Wait for successful retry
        await page.waitForResponse(response =>
            response.url().includes('/livecomponent') &&
            response.status() === 200,
            { timeout: 15000 }
        );

        // Loading should disappear
        await expect(loadingIndicator).not.toBeVisible();
    });

    test('should emit error events for monitoring', async ({ page }) => {
        const errorEvents = [];

        await page.exposeFunction('onErrorEvent', (event) => {
            errorEvents.push(event);
        });

        await page.evaluate(() => {
            window.addEventListener('livecomponent:error', (e) => {
                window.onErrorEvent(e.detail);
            });
        });

        // Trigger error
        await page.route('**/livecomponent/**', route => route.abort('failed'));
        await page.click('[data-lc-action="submit"]');

        // Wait for error events
        await page.waitForTimeout(2000);

        // Should have error events
        expect(errorEvents.length).toBeGreaterThan(0);
        expect(errorEvents[0]).toHaveProperty('type');
        expect(errorEvents[0]).toHaveProperty('message');
        expect(errorEvents[0]).toHaveProperty('timestamp');
    });

    test('should recover from partial response errors', async ({ page }) => {
        // Mock malformed JSON response
        await page.route('**/livecomponent/**', route => {
            route.fulfill({
                status: 200,
                body: '{invalid json'
            });
        });

        // Trigger action
        await page.click('[data-lc-action="submit"]');

        // Should handle parse error gracefully
        const errorNotification = page.locator('.livecomponent-error');
        await expect(errorNotification).toBeVisible();
        await expect(errorNotification).toContainText(/error|failed/i);

        // Component should remain functional
        const component = page.locator('[data-component-id]');
        await expect(component).toBeVisible();
    });

    test('should handle rate limiting with backoff', async ({ page }) => {
        // Mock rate limit error
        await page.route('**/livecomponent/**', route => {
            route.fulfill({
                status: 429,
                headers: { 'Retry-After': '5' },
                body: JSON.stringify({ error: 'Too many requests' })
            });
        });

        // Trigger action
        await page.click('[data-lc-action="submit"]');

        // Should show rate limit message
        const rateLimitNotification = page.locator('.livecomponent-rate-limit');
        await expect(rateLimitNotification).toBeVisible();
        await expect(rateLimitNotification).toContainText(/too many|rate limit|try again/i);

        // Should show countdown timer
        const countdown = page.locator('.retry-countdown');
        await expect(countdown).toBeVisible();
        await expect(countdown).toContainText(/\d+ second/);
    });

    test('should clear errors when action succeeds', async ({ page }) => {
        // First: trigger error
        let shouldFail = true;
        await page.route('**/livecomponent/**', route => {
            if (shouldFail) {
                route.abort('failed');
            } else {
                route.continue();
            }
        });

        await page.click('[data-lc-action="submit"]');
        await expect(page.locator('.livecomponent-error')).toBeVisible();

        // Second: fix and retry
        shouldFail = false;
        await page.click('[data-action="retry"]');

        await page.waitForResponse(response =>
            response.url().includes('/livecomponent') &&
            response.status() === 200
        );

        // Error should be cleared
        await expect(page.locator('.livecomponent-error')).not.toBeVisible();

        // Success message should appear
        const successNotification = page.locator('.livecomponent-success');
        await expect(successNotification).toBeVisible();
    });

    test('should handle concurrent action errors independently', async ({ page }) => {
        // Create two components
        await page.evaluate(() => {
            const container = document.querySelector('#component-container');

            ['component-1', 'component-2'].forEach(id => {
                const component = document.createElement('div');
                component.dataset.componentId = id;
                component.innerHTML = `
                    <button data-lc-action="submit">Submit</button>
                    <div class="livecomponent-error"></div>
                `;
                container.appendChild(component);
            });
        });

        // Mock different responses
        await page.route('**/livecomponent/**', route => {
            const url = route.request().url();
            if (url.includes('component-1')) {
                route.abort('failed'); // Fail component-1
            } else {
                route.continue(); // Succeed component-2
            }
        });

        // Trigger both actions
        await page.click('#component-1 [data-lc-action="submit"]');
        await page.click('#component-2 [data-lc-action="submit"]');

        await page.waitForTimeout(2000);

        // Component 1 should show error
        await expect(page.locator('#component-1 .livecomponent-error')).toBeVisible();

        // Component 2 should succeed (no error)
        await expect(page.locator('#component-2 .livecomponent-error')).not.toBeVisible();
    });

    test('should provide debug information in development mode', async ({ page }) => {
        // Set development mode
        await page.evaluate(() => {
            document.documentElement.dataset.env = 'development';
        });

        // Mock error
        await page.route('**/livecomponent/**', route => {
            route.fulfill({
                status: 500,
                body: JSON.stringify({
                    error: 'Database connection failed',
                    debug: {
                        file: '/var/www/src/Database/Connection.php',
                        line: 42,
                        trace: ['DatabaseException', 'ConnectionPool', 'EntityManager']
                    }
                })
            });
        });

        // Trigger action
        await page.click('[data-lc-action="submit"]');

        // Should show debug details in dev mode
        const debugInfo = page.locator('.livecomponent-debug');
        await expect(debugInfo).toBeVisible();
        await expect(debugInfo).toContainText('Connection.php:42');
        await expect(debugInfo).toContainText('Database connection failed');
    });

    test('should hide sensitive debug info in production mode', async ({ page }) => {
        // Set production mode
        await page.evaluate(() => {
            document.documentElement.dataset.env = 'production';
        });

        // Mock error with debug info
        await page.route('**/livecomponent/**', route => {
            route.fulfill({
                status: 500,
                body: JSON.stringify({
                    error: 'Internal error',
                    debug: {
                        file: '/var/www/src/Database/Connection.php',
                        line: 42,
                        sensitive_data: 'password=secret123'
                    }
                })
            });
        });

        // Trigger action
        await page.click('[data-lc-action="submit"]');

        // Should show generic error only
        const errorNotification = page.locator('.livecomponent-error');
        await expect(errorNotification).toBeVisible();
        await expect(errorNotification).not.toContainText('Connection.php');
        await expect(errorNotification).not.toContainText('password');

        // Generic message only
        await expect(errorNotification).toContainText(/something went wrong|error occurred/i);
    });
});
