import { test, expect } from '@playwright/test';
import {
  TEST_CONFIG,
  loginAsUser,
  setupErrorCapture,
  waitForToast,
  fillField,
  forceSubmitForm,
} from './utils/test-helpers';

/**
 * Error Handling E2E Test Suite
 * 
 * Tests error handling throughout the application:
 * - Network errors (offline, timeout, 500)
 * - Validation errors (form validation)
 * - API errors (400, 401, 403, 404, 500)
 * - Error boundaries (React error boundaries)
 * - User-friendly error messages
 * - Error recovery
 */

test.describe('Error Handling', () => {


  test.beforeEach(async ({ page }) => {
    setupErrorCapture(page);
  });

  test.describe('Network Errors', () => {
    test.beforeEach(async ({ page }) => {
      await loginAsUser(page);
    });

    test('should handle offline mode gracefully', async ({ page }) => {
      // Go offline
      await page.context().setOffline(true);

      await page.goto(`${TEST_CONFIG.FRONTEND_URL}/library`);
      await page.waitForLoadState('domcontentloaded');

      // Should show offline message or cached content
      const offlineIndicator = page.locator('text=offline, text=No internet, text=Connection lost').first();
      const cachedContent = page.locator('[data-testid="tracks-list"], [data-testid="library"]').first();

      const hasOfflineMessage = await offlineIndicator.isVisible({ timeout: 3000 }).catch(() => false);
      const hasCachedContent = await cachedContent.isVisible({ timeout: 3000 }).catch(() => false);

      expect(hasOfflineMessage || hasCachedContent).toBeTruthy();

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

    test('should handle API timeout errors', async ({ page }) => {
      // Intercept API calls and delay them to simulate timeout
      await page.route('**/api/v1/tracks**', async (route) => {
        await new Promise(resolve => setTimeout(resolve, 10000)); // 10 second delay
        route.abort('timedout');
      });

      await page.goto(`${TEST_CONFIG.FRONTEND_URL}/library`);
      await page.waitForLoadState('networkidle');

      // Should show timeout error or loading state
      const timeoutError = await waitForToast(page, 'error', 15000).catch(() => null);
      const loadingState = page.locator('text=Loading, [data-testid="loading"]').first();

      expect(timeoutError !== null || await loadingState.isVisible({ timeout: 2000 }).catch(() => false)).toBeTruthy();
    });

    test('should handle 500 server errors', async ({ page }) => {
      // Intercept API calls and return 500
      await page.route('**/api/v1/tracks**', (route) => {
        route.fulfill({
          status: 500,
          contentType: 'application/json',
          body: JSON.stringify({ error: 'Internal Server Error' }),
        });
      });

      await page.goto(`${TEST_CONFIG.FRONTEND_URL}/library`);
      await page.waitForLoadState('networkidle');

      // Should show error message
      const errorToast = await waitForToast(page, 'error', 5000).catch(() => null);
      expect(errorToast).toBeTruthy();
    });

    test('should handle 503 service unavailable', async ({ page }) => {
      await page.route('**/api/v1/tracks**', (route) => {
        route.fulfill({
          status: 503,
          contentType: 'application/json',
          body: JSON.stringify({ error: 'Service Unavailable' }),
        });
      });

      await page.goto(`${TEST_CONFIG.FRONTEND_URL}/library`);
      await page.waitForLoadState('networkidle');

      const errorToast = await waitForToast(page, 'error', 5000).catch(() => null);
      expect(errorToast).toBeTruthy();
    });
  });

  test.describe('Authentication Errors', () => {
    test('should handle 401 unauthorized errors', async ({ page }) => {
      // Start unauthenticated
      test.use({ storageState: { cookies: [], origins: [] } });

      // Try to access protected route
      await page.goto(`${TEST_CONFIG.FRONTEND_URL}/library`);
      await page.waitForLoadState('networkidle');

      // Should redirect to login
      await expect(page).toHaveURL(new RegExp(`${TEST_CONFIG.FRONTEND_URL}/(login|auth/login)`));
    });

    test('should handle invalid login credentials', async ({ page }) => {
      test.use({ storageState: { cookies: [], origins: [] } });

      await page.goto(`${TEST_CONFIG.FRONTEND_URL}/login`);
      await page.waitForLoadState('networkidle');

      // Fill form with invalid credentials
      await fillField(page, 'input[type="email"]', 'invalid@example.com');
      await fillField(page, 'input[type="password"]', 'wrongpassword');

      const loginForm = page.locator('form').first();
      await forceSubmitForm(page, loginForm);

      // Should show error message
      const errorToast = await waitForToast(page, 'error', 5000).catch(() => null);
      const errorMessage = page.locator('text=Invalid, text=incorrect, text=wrong').first();

      expect(errorToast !== null || await errorMessage.isVisible({ timeout: 3000 }).catch(() => false)).toBeTruthy();
    });

    test('should handle expired token gracefully', async ({ page }) => {
      await loginAsUser(page);

      // Simulate expired token by clearing it
      await page.evaluate(() => {
        localStorage.clear();
        sessionStorage.clear();
      });

      // Try to access protected route
      await page.goto(`${TEST_CONFIG.FRONTEND_URL}/library`);
      await page.waitForLoadState('networkidle');

      // Should redirect to login or show error
      const currentUrl = page.url();
      const redirectedToLogin = currentUrl.includes('/login');
      const errorShown = await waitForToast(page, 'error', 3000).catch(() => null);

      expect(redirectedToLogin || errorShown !== null).toBeTruthy();
    });
  });

  test.describe('Validation Errors', () => {
    test.beforeEach(async ({ page }) => {
      await loginAsUser(page);
    });

    test('should show validation errors for empty required fields', async ({ page }) => {
      await page.goto(`${TEST_CONFIG.FRONTEND_URL}/register`);
      await page.waitForLoadState('networkidle');

      // Try to submit empty form
      const registerForm = page.locator('form').first();
      if (await registerForm.isVisible({ timeout: 2000 }).catch(() => false)) {
        await forceSubmitForm(page, registerForm);

        // Should show validation errors
        const emailError = page.locator('text=required, text=email').first();
        const passwordError = page.locator('text=required, text=password').first();

        const hasEmailError = await emailError.isVisible({ timeout: 2000 }).catch(() => false);
        const hasPasswordError = await passwordError.isVisible({ timeout: 2000 }).catch(() => false);

        expect(hasEmailError || hasPasswordError).toBeTruthy();
      }
    });

    test('should show validation error for invalid email format', async ({ page }) => {
      await page.goto(`${TEST_CONFIG.FRONTEND_URL}/register`);
      await page.waitForLoadState('networkidle');

      const emailInput = page.locator('input[type="email"]').first();
      if (await emailInput.isVisible({ timeout: 2000 }).catch(() => false)) {
        await fillField(page, 'input[type="email"]', 'invalid-email');

        // Blur to trigger validation
        await emailInput.blur();

        // Should show validation error
        const emailError = page.locator('text=invalid, text=email format').first();
        const hasError = await emailError.isVisible({ timeout: 2000 }).catch(() => false);

        // HTML5 validation might also show browser tooltip
        const isValid = await emailInput.evaluate((el: HTMLInputElement) => el.validity.valid);
        expect(hasError || !isValid).toBeTruthy();
      }
    });

    test('should show validation error for password mismatch', async ({ page }) => {
      await page.goto(`${TEST_CONFIG.FRONTEND_URL}/register`);
      await page.waitForLoadState('networkidle');

      const passwordInput = page.locator('input[type="password"]').first();
      const confirmPasswordInput = page.locator('input[name*="confirm"], input[name*="passwordConfirm"]').first();

      if (await passwordInput.isVisible({ timeout: 2000 }).catch(() => false) &&
        await confirmPasswordInput.isVisible({ timeout: 2000 }).catch(() => false)) {
        await fillField(page, 'input[type="password"]', 'password123');
        await fillField(page, 'input[name*="confirm"], input[name*="passwordConfirm"]', 'different123');

        // Blur to trigger validation
        await confirmPasswordInput.blur();

        // Should show validation error
        const passwordError = page.locator('text=match, text=password, text=do not match').first();
        const hasError = await passwordError.isVisible({ timeout: 2000 }).catch(() => false);
        expect(hasError).toBeTruthy();
      }
    });
  });

  test.describe('API Error Responses', () => {
    test.beforeEach(async ({ page }) => {
      await loginAsUser(page);
    });

    test('should handle 400 bad request errors', async ({ page }) => {
      await page.route('**/api/v1/tracks**', (route) => {
        route.fulfill({
          status: 400,
          contentType: 'application/json',
          body: JSON.stringify({
            success: false,
            error: {
              code: 'VALIDATION_ERROR',
              message: 'Invalid request data'
            }
          }),
        });
      });

      await page.goto(`${TEST_CONFIG.FRONTEND_URL}/library`);
      await page.waitForLoadState('networkidle');

      const errorToast = await waitForToast(page, 'error', 5000).catch(() => null);
      expect(errorToast).toBeTruthy();
    });

    test('should handle 403 forbidden errors', async ({ page }) => {
      await page.route('**/api/v1/tracks/*/delete**', (route) => {
        route.fulfill({
          status: 403,
          contentType: 'application/json',
          body: JSON.stringify({
            success: false,
            error: {
              code: 'FORBIDDEN',
              message: 'You do not have permission to perform this action'
            }
          }),
        });
      });

      await page.goto(`${TEST_CONFIG.FRONTEND_URL}/library`);
      await page.waitForLoadState('networkidle');

      // Try to delete a track (if delete button exists)
      const deleteButton = page.locator('button[aria-label*="delete"], button[title*="delete"]').first();
      if (await deleteButton.isVisible({ timeout: 2000 }).catch(() => false)) {
        await deleteButton.click();

        const errorToast = await waitForToast(page, 'error', 5000).catch(() => null);
        expect(errorToast).toBeTruthy();
      }
    });

    test('should handle 404 not found errors', async ({ page }) => {
      await page.route('**/api/v1/tracks/non-existent-id**', (route) => {
        route.fulfill({
          status: 404,
          contentType: 'application/json',
          body: JSON.stringify({
            success: false,
            error: {
              code: 'NOT_FOUND',
              message: 'Track not found'
            }
          }),
        });
      });

      // Try to access non-existent track
      await page.goto(`${TEST_CONFIG.FRONTEND_URL}/tracks/non-existent-id`);
      await page.waitForLoadState('networkidle');

      // Should show 404 message or redirect
      const notFoundMessage = page.locator('text=404, text=Not Found, text=not found').first();
      const errorToast = await waitForToast(page, 'error', 3000).catch(() => null);

      expect(await notFoundMessage.isVisible({ timeout: 2000 }).catch(() => false) || errorToast !== null).toBeTruthy();
    });
  });

  test.describe('Error Recovery', () => {
    test.beforeEach(async ({ page }) => {
      await loginAsUser(page);
    });

    test('should allow retry after network error', async ({ page }) => {
      let requestCount = 0;

      await page.route('**/api/v1/tracks**', (route) => {
        requestCount++;
        if (requestCount === 1) {
          // First request fails
          route.abort('failed');
        } else {
          // Subsequent requests succeed
          route.continue();
        }
      });

      await page.goto(`${TEST_CONFIG.FRONTEND_URL}/library`);
      await page.waitForLoadState('networkidle');

      // Should show error
      const errorToast = await waitForToast(page, 'error', 5000).catch(() => null);

      // Look for retry button
      const retryButton = page.locator('button:has-text("Retry"), button:has-text("Try again")').first();
      if (await retryButton.isVisible({ timeout: 2000 }).catch(() => false)) {
        await retryButton.click();

        // Should retry and succeed
        await page.waitForTimeout(2000);
        expect(requestCount).toBeGreaterThan(1);
      } else {
        // Retry might be automatic or not implemented
        expect(errorToast !== null || requestCount > 1).toBeTruthy();
      }
    });

    test('should clear errors when navigating away', async ({ page }) => {
      // Trigger an error
      await page.route('**/api/v1/tracks**', (route) => {
        route.fulfill({
          status: 500,
          contentType: 'application/json',
          body: JSON.stringify({ error: 'Server Error' }),
        });
      });

      await page.goto(`${TEST_CONFIG.FRONTEND_URL}/library`);
      await page.waitForLoadState('networkidle');

      // Error should be shown
      await waitForToast(page, 'error', 5000).catch(() => null);

      // Navigate away
      await page.goto(`${TEST_CONFIG.FRONTEND_URL}/dashboard`);
      await page.waitForLoadState('networkidle');

      // Error toast should be gone (or dismissed)
      await page.waitForTimeout(1000);
      // This is hard to test directly, but navigation should work
      await expect(page).toHaveURL(new RegExp(`${TEST_CONFIG.FRONTEND_URL}/(dashboard)?`));
    });
  });
});

