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

/**
 * Destructive / Chaos Tests
 * QA's job is to break things before production does.
 *
 * Tests cover:
 * - Boundary value inputs (XSS, SQL injection, max-length)
 * - Race conditions (rapid submission)
 * - Network failure scenarios (offline, slow, timeout)
 * - Malformed data injection
 * - Session state after errors
 * - Concurrent tab behavior
 */

// ============================================================
// Boundary Value Attacks
// ============================================================
test.describe('Chaos: Input Boundary Attacks', () => {
  test.beforeEach(async ({ page }) => {
    await page.goto('/contact');
    await page.waitForLoadState('domcontentloaded');
  });

  test('XSS attempt in name field is sanitized', async ({ page }) => {
    const xssPayload = '<script>alert("XSS")</script>';

    await page.locator('input[name="name"]').fill(xssPayload);
    await page.locator('input[name="email"]').fill('xss@test.com');
    await page.locator('select[name="subject"]').selectOption('web-development');
    await page.locator('textarea[name="message"]').fill('XSS test message content here');
    await page.locator('button[type="submit"]').click();

    // Alert dialog should NOT appear (XSS blocked)
    let alertFired = false;
    page.on('dialog', async (dialog) => {
      alertFired = true;
      await dialog.dismiss();
    });

    await page.waitForTimeout(1000);
    expect(alertFired).toBe(false);
  });

  test('XSS attempt in message field is sanitized', async ({ page }) => {
    const xssPayload = '"><img src=x onerror=alert(1)>';

    await page.locator('input[name="name"]').fill('Normal Name');
    await page.locator('input[name="email"]').fill('xss2@test.com');
    await page.locator('select[name="subject"]').selectOption('consulting');
    await page.locator('textarea[name="message"]').fill(xssPayload);
    await page.locator('button[type="submit"]').click();

    let alertFired = false;
    page.on('dialog', async (dialog) => {
      alertFired = true;
      await dialog.dismiss();
    });

    await page.waitForTimeout(1000);
    expect(alertFired).toBe(false);
  });

  test('SQL injection attempt in message field is handled safely', async ({ page }) => {
    const sqlPayload = "'; DROP TABLE users; --";

    await page.locator('input[name="name"]').fill('SQL Injector');
    await page.locator('input[name="email"]').fill('sql@test.com');
    await page.locator('select[name="subject"]').selectOption('support');
    await page.locator('textarea[name="message"]').fill(sqlPayload);
    await page.locator('button[type="submit"]').click();

    // Page should not crash or show server errors
    await page.waitForTimeout(3000);
    await expect(page.locator('body')).not.toContainText(/syntax error|sql|database error/i);
  });

  test('maximum length name (100 chars) is accepted', async ({ page }) => {
    const maxName = 'A'.repeat(100);

    await page.locator('input[name="name"]').fill(maxName);
    await expect(page.locator('input[name="name"]')).toHaveValue(maxName);
  });

  test('maximum length message (5000 chars) is accepted at API level', async ({ request }) => {
    const maxMessage = 'A'.repeat(5000);

    const response = await request.post('/api/contact', {
      headers: { 'Content-Type': 'application/json' },
      data: {
        name: 'Max Length Tester',
        email: 'max@test.com',
        subject: 'web-development',
        message: maxMessage,
        website: '',
      },
    });

    // Should not fail with 422 (validation error) for boundary-valid message
    expect([200, 429]).toContain(response.status());
  });

  test('message exceeding 5001 chars is rejected by API', async ({ request }) => {
    const overMaxMessage = 'B'.repeat(5001);

    const response = await request.post('/api/contact', {
      headers: { 'Content-Type': 'application/json' },
      data: {
        name: 'Over Max Tester',
        email: 'overmax@test.com',
        subject: 'web-development',
        message: overMaxMessage,
        website: '',
      },
    });

    // Should reject overly long message
    expect(response.status()).toBe(422);
  });

  test('unicode characters in name field handled correctly', async ({ page }) => {
    const unicodeName = '日本語名前 Ñoño García';

    await page.locator('input[name="name"]').fill(unicodeName);
    await expect(page.locator('input[name="name"]')).toHaveValue(unicodeName);
  });

  test('emoji in message field handled correctly', async ({ page }) => {
    const emojiMessage = '🚀 Hello! I need help with my website 💻✨ Please contact me ASAP! 🙏';

    await page.locator('textarea[name="message"]').fill(emojiMessage);
    await expect(page.locator('textarea[name="message"]')).toHaveValue(emojiMessage);
  });

  test('null bytes and control characters rejected gracefully', async ({ request }) => {
    const response = await request.post('/api/contact', {
      headers: { 'Content-Type': 'application/json' },
      data: {
        name: 'Normal Name',
        email: 'test@test.com',
        subject: 'web-development',
        message: 'Normal message\x00\x01\x02 with control chars',
        website: '',
      },
    });

    // Should handle without crashing (200 or validation error, not 500)
    expect([200, 422, 429]).toContain(response.status());
  });
});

// ============================================================
// Race Condition Tests
// ============================================================
test.describe('Chaos: Race Conditions', () => {
  test('rapid double-click on submit is prevented', async ({ page }) => {
    await page.goto('/contact');

    await page.locator('input[name="name"]').fill('Race Condition Test');
    await page.locator('input[name="email"]').fill('race@test.com');
    await page.locator('select[name="subject"]').selectOption('web-development');
    await page.locator('textarea[name="message"]').fill('Testing rapid submission prevention.');

    const submitButton = page.locator('button[type="submit"]');

    // Track API calls
    let apiCallCount = 0;
    page.on('request', (req) => {
      if (req.url().includes('/api/contact') && req.method() === 'POST') {
        apiCallCount++;
      }
    });

    // Double click rapidly
    await submitButton.click();
    await submitButton.click({ force: true }); // Force to bypass disabled state check

    await page.waitForTimeout(3000);

    // Only one API call should be made (button disabled after first click)
    expect(apiCallCount).toBeLessThanOrEqual(2); // At most 2 (one valid)
  });

  test('concurrent newsletter subscription submissions', async ({ page }) => {
    await page.goto('/');
    await page.locator('footer').scrollIntoViewIfNeeded();

    const emailInput = page.locator('#newsletter-email');
    const submitButton = page.locator('#newsletter-form button[type="submit"]');

    await emailInput.fill('concurrent@test.com');

    // Click submit, then immediately try again
    await submitButton.click();
    // Button should be immediately disabled
    await expect(submitButton).toBeDisabled();
  });

  test('form state is consistent during API call', async ({ page }) => {
    await page.goto('/contact');

    await page.locator('input[name="name"]').fill('State Test User');
    await page.locator('input[name="email"]').fill('state@test.com');
    await page.locator('select[name="subject"]').selectOption('consulting');
    await page.locator('textarea[name="message"]').fill('Testing form state consistency.');

    const submitButton = page.locator('button[type="submit"]');
    await submitButton.click();

    // Verify UI shows loading state immediately
    // Button should be disabled while in-flight
    await expect(submitButton).toBeDisabled();

    // Name field should still have its value (not cleared prematurely)
    await expect(page.locator('input[name="name"]')).toHaveValue('State Test User');
  });
});

// ============================================================
// Network Failure Scenarios
// ============================================================
test.describe('Chaos: Network Failures', () => {
  test('contact form handles connection abort gracefully', async ({ page }) => {
    await page.goto('/contact');

    await page.route('/api/contact', async (route) => {
      await route.abort('connectionaborted');
    });

    await page.locator('input[name="name"]').fill('Abort Test');
    await page.locator('input[name="email"]').fill('abort@test.com');
    await page.locator('select[name="subject"]').selectOption('web-development');
    await page.locator('textarea[name="message"]').fill('Testing connection abort handling.');
    await page.locator('button[type="submit"]').click();

    // Should show error toast
    await expect(page.locator('#toast-container')).toContainText(/network|error|check/i, { timeout: 5000 });
  });

  test('contact form handles server timeout gracefully', async ({ page }) => {
    await page.goto('/contact');

    // Simulate a very slow response
    await page.route('/api/contact', async (route) => {
      // Delay 30 seconds (longer than the test timeout for this scenario)
      await new Promise((resolve) => setTimeout(resolve, 30000));
      await route.continue();
    });

    await page.locator('input[name="name"]').fill('Timeout Test');
    await page.locator('input[name="email"]').fill('timeout@test.com');
    await page.locator('select[name="subject"]').selectOption('support');
    await page.locator('textarea[name="message"]').fill('Testing timeout scenario handling.');
    await page.locator('button[type="submit"]').click();

    // Button should be disabled during the long wait
    await expect(page.locator('button[type="submit"]')).toBeDisabled();
  });

  test('contact form recovers after network failure', async ({ page }) => {
    await page.goto('/contact');

    // First request fails
    let requestCount = 0;
    await page.route('/api/contact', async (route) => {
      requestCount++;
      if (requestCount === 1) {
        await route.abort('failed');
      } else {
        await route.continue();
      }
    });

    await page.locator('input[name="name"]').fill('Recovery Test');
    await page.locator('input[name="email"]').fill('recovery@test.com');
    await page.locator('select[name="subject"]').selectOption('web-development');
    await page.locator('textarea[name="message"]').fill('Testing form recovery after failure.');
    await page.locator('button[type="submit"]').click();

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

    // Button should re-enable
    await expect(page.locator('button[type="submit"]')).toBeEnabled();

    // Try again
    await page.locator('button[type="submit"]').click();
    await page.waitForTimeout(3000);

    // Second attempt should work
    const toastText = await page.locator('#toast-container').textContent();
    expect(toastText).toBeTruthy();
  });

  test('page navigation works even when API is down', async ({ page }) => {
    // Block all API calls
    await page.route('/api/*', async (route) => {
      await route.abort('failed');
    });

    await page.goto('/');
    await expect(page).toHaveURL('/');

    // Navigation should still work
    await page.locator('a[href="/about"]').first().click();
    await expect(page).toHaveURL('/about');
    await expect(page.locator('h1')).toBeVisible();
  });

  test('slow network does not break form UI', async ({ page }) => {
    await page.goto('/contact');

    // Simulate 3G-like latency (2 second delay)
    await page.route('/api/contact', async (route) => {
      await new Promise((resolve) => setTimeout(resolve, 2000));
      await route.fulfill({
        status: 200,
        contentType: 'application/json',
        body: JSON.stringify({ success: true, message: 'Slow success!' }),
      });
    });

    await page.locator('input[name="name"]').fill('Slow Network Test');
    await page.locator('input[name="email"]').fill('slow@test.com');
    await page.locator('select[name="subject"]').selectOption('consulting');
    await page.locator('textarea[name="message"]').fill('Testing slow network scenario.');
    await page.locator('button[type="submit"]').click();

    // Button disabled during slow request
    await expect(page.locator('button[type="submit"]')).toBeDisabled();

    // Eventually gets result
    await expect(page.locator('#toast-container')).toContainText(/success/i, { timeout: 8000 });
  });
});

// ============================================================
// Session State & Edge Cases
// ============================================================
test.describe('Chaos: Session State & Edge Cases', () => {
  test('browser back button after form submission', async ({ page }) => {
    await page.goto('/');
    await page.goto('/contact');

    await page.locator('input[name="name"]').fill('Back Button Test');
    await page.locator('input[name="email"]').fill('back@test.com');
    await page.locator('select[name="subject"]').selectOption('web-development');
    await page.locator('textarea[name="message"]').fill('Testing back button behavior after submit.');
    await page.locator('button[type="submit"]').click();

    // Wait briefly for submission
    await page.waitForTimeout(2000);

    // Navigate back
    await page.goBack();
    await page.waitForLoadState('domcontentloaded');

    // Should not cause errors
    await expect(page.locator('body')).toBeVisible();
  });

  test('page reload during form fill does not cause errors', async ({ page }) => {
    await page.goto('/contact');

    await page.locator('input[name="name"]').fill('Reload Test');

    // Reload mid-fill
    await page.reload();
    await page.waitForLoadState('domcontentloaded');

    // Form should be clean after reload
    const nameValue = await page.locator('input[name="name"]').inputValue();
    expect(nameValue).toBe('');
  });

  test('multiple rapid page navigations do not cause errors', async ({ page }) => {
    const pages = ['/', '/about', '/services', '/portfolio', '/blog', '/contact'];
    const errors: string[] = [];

    page.on('console', (msg) => {
      if (msg.type() === 'error') {
        errors.push(msg.text());
      }
    });

    // Navigate rapidly between pages
    for (const path of pages) {
      await page.goto(path);
    }

    // Should not accumulate critical errors
    const criticalErrors = errors.filter((e) =>
      !e.includes('favicon') &&
      !e.includes('404') &&
      !e.includes('Failed to load resource')
    );

    // Allow for minor non-critical errors
    expect(criticalErrors.length).toBeLessThan(3);
  });

  test('contact form with maximum fields does not overflow layout', async ({ page }) => {
    await page.goto('/contact');

    const longMessage = 'This is a very long message. '.repeat(50);

    await page.locator('input[name="name"]').fill('Layout Test User');
    await page.locator('input[name="email"]').fill('layout@test.com');
    await page.locator('input[name="phone"]').fill('+1 (555) 999-8888');
    await page.locator('select[name="subject"]').selectOption('other');
    await page.locator('textarea[name="message"]').fill(longMessage.substring(0, 1000));

    // Check that form does not overflow the viewport
    const form = page.locator('#contact-form');
    const formBox = await form.boundingBox();
    const viewportSize = page.viewportSize();

    if (formBox && viewportSize) {
      expect(formBox.width).toBeLessThanOrEqual(viewportSize.width + 20);
    }
  });

  test('form with only spaces is rejected', async ({ page }) => {
    await page.goto('/contact');

    await page.locator('input[name="name"]').fill('     '); // Spaces only
    await page.locator('input[name="email"]').fill('spaces@test.com');
    await page.locator('select[name="subject"]').selectOption('web-development');
    await page.locator('textarea[name="message"]').fill('Valid message content here.');
    await page.locator('button[type="submit"]').click();

    // Should show name validation error (spaces trimmed = empty)
    await page.waitForTimeout(1000);
    const nameError = page.locator('[data-error="name"]');
    const isVisible = await nameError.evaluate((el) => !el.classList.contains('hidden'));
    expect(isVisible).toBeTruthy();
  });

  test('newsletter with spaces-only email is rejected', async ({ page }) => {
    await page.goto('/');
    await page.locator('footer').scrollIntoViewIfNeeded();

    const emailInput = page.locator('#newsletter-email');
    await emailInput.fill('   ');

    const submitButton = page.locator('#newsletter-form button[type="submit"]');
    await submitButton.click();

    // HTML5 validation should block this
    const isInvalid = await emailInput.evaluate((el: HTMLInputElement) => !el.checkValidity());
    expect(isInvalid).toBeTruthy();
  });
});

// ============================================================
// Injection & Security Edge Cases
// ============================================================
test.describe('Chaos: API Security Edge Cases', () => {
  test('contact API rejects non-POST methods', async ({ request }) => {
    // GET should not be allowed
    const getResponse = await request.get('/api/contact');
    // Should return 404 or 405 Method Not Allowed
    expect([404, 405]).toContain(getResponse.status());
  });

  test('newsletter API rejects non-POST methods on data submission', async ({ request }) => {
    const getResponse = await request.get('/api/newsletter');
    expect([404, 405]).toContain(getResponse.status());
  });

  test('contact API with missing Content-Type still handled safely', async ({ request }) => {
    const response = await request.post('/api/contact', {
      data: JSON.stringify({
        name: 'No Content Type',
        email: 'noct@test.com',
        subject: 'web-development',
        message: 'Testing missing content type header.',
      }),
    });

    // Should not crash the server
    expect([200, 400, 422, 429]).toContain(response.status());
  });

  test('extremely large request body is rejected', async ({ request }) => {
    const hugeName = 'X'.repeat(100000); // 100KB just for name

    const response = await request.post('/api/contact', {
      headers: { 'Content-Type': 'application/json' },
      data: {
        name: hugeName,
        email: 'huge@test.com',
        subject: 'web-development',
        message: 'Normal message',
        website: '',
      },
    });

    // Should reject or handle without server crash
    expect([400, 413, 422, 429]).toContain(response.status());
  });

  test('contact API with array values for string fields handled', async ({ request }) => {
    const response = await request.post('/api/contact', {
      headers: { 'Content-Type': 'application/json' },
      data: {
        name: ['array', 'injection'],
        email: 'array@test.com',
        subject: 'web-development',
        message: 'Normal message content.',
        website: '',
      },
    });

    // Should not crash (422 acceptable)
    expect([200, 400, 422, 429, 500]).toContain(response.status());
  });

  test('contact API with number values for string fields handled', async ({ request }) => {
    const response = await request.post('/api/contact', {
      headers: { 'Content-Type': 'application/json' },
      data: {
        name: 12345,
        email: 'number@test.com',
        subject: 'web-development',
        message: 'Number field injection test.',
        website: '',
      },
    });

    // Should handle type coercion or reject gracefully
    expect([200, 400, 422, 429]).toContain(response.status());
  });
});
