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

// All 10 pages to test
const ALL_PAGES = [
  { name: 'Home', url: '/', titleFragment: 'Transforming Ideas' },
  { name: 'About', url: '/about', titleFragment: 'About Us' },
  { name: 'Services', url: '/services', titleFragment: 'Our Services' },
  { name: 'Portfolio', url: '/portfolio', titleFragment: 'Our Portfolio' },
  { name: 'Blog', url: '/blog', titleFragment: 'Blog' },
  { name: 'Contact', url: '/contact', titleFragment: 'Contact Us' },
  { name: 'Privacy', url: '/privacy', titleFragment: 'Privacy Policy' },
  { name: 'Terms', url: '/terms', titleFragment: 'Terms of Service' },
  { name: 'Sitemap', url: '/sitemap', titleFragment: 'Sitemap' },
  { name: '404', url: '/nonexistent-page-xyz', titleFragment: '' },
];

// Viewport sizes for responsive testing
const VIEWPORTS = [
  { name: 'Mobile', width: 375, height: 667 },
  { name: 'Tablet', width: 768, height: 1024 },
  { name: 'Desktop', width: 1280, height: 800 },
  { name: 'Wide', width: 1920, height: 1080 },
];

// ============================================================
// 1. Cross-Browser Page Load Testing (runs on all configured browsers)
// ============================================================
test.describe('Cross-Browser: All Pages Load', () => {
  for (const p of ALL_PAGES) {
    test(`${p.name} page loads successfully`, async ({ page }) => {
      const consoleErrors: string[] = [];
      page.on('console', (msg) => {
        if (msg.type() === 'error') consoleErrors.push(msg.text());
      });
      page.on('pageerror', (err) => consoleErrors.push(err.message));

      const response = await page.goto(p.url, { waitUntil: 'domcontentloaded' });

      if (p.url === '/nonexistent-page-xyz') {
        expect([404, 200]).toContain(response?.status()); // Some frameworks serve 200 with error page
      } else {
        expect(response?.status()).toBe(200);
      }

      // Verify no critical JS errors (filter out known non-critical browser noise)
      const critical = consoleErrors.filter(
        (e) =>
          !e.includes('favicon') &&
          !e.includes('livereload') &&
          !e.includes('DevTools') &&
          !e.includes('manifest') &&
          !e.includes('net::ERR_ABORTED') // image lazy-load aborts on fast navigation
      );
      if (critical.length > 0) {
        console.warn(`[${p.name}] Console errors:`, critical);
      }
      expect(critical, `Unexpected console errors on ${p.name}`).toHaveLength(0);
    });
  }
});

// ============================================================
// 2. Cross-Browser: Critical Layout Elements Present
// ============================================================
test.describe('Cross-Browser: Layout Elements', () => {
  const CONTENT_PAGES = ALL_PAGES.filter((p) => p.url !== '/nonexistent-page-xyz');

  for (const p of CONTENT_PAGES) {
    test(`${p.name} has header, main content, and footer`, async ({ page }) => {
      await page.goto(p.url, { waitUntil: 'domcontentloaded' });

      // Fixed header is always present
      await expect(page.locator('#main-header')).toBeAttached();

      // Footer is always present
      await expect(page.locator('footer')).toBeAttached();

      // Page has at least one <section> or <main> with content
      const mainContent = page.locator('main, section').first();
      await expect(mainContent).toBeAttached();

      // No invisible page (must have visible body text)
      const bodyText = await page.locator('body').innerText();
      expect(bodyText.trim().length).toBeGreaterThan(50);
    });
  }
});

// ============================================================
// 3. Responsive Design Testing
// ============================================================
test.describe('Responsive Design', () => {
  test.describe('Home page responsive layout', () => {
    for (const vp of VIEWPORTS) {
      test(`renders correctly at ${vp.name} (${vp.width}x${vp.height})`, async ({ page }) => {
        await page.setViewportSize({ width: vp.width, height: vp.height });
        await page.goto('/', { waitUntil: 'domcontentloaded' });

        // Header is always visible
        await expect(page.locator('#main-header')).toBeVisible();

        // Hero section (first section) is visible
        await expect(page.locator('section').first()).toBeVisible();

        // Footer visible
        await expect(page.locator('footer')).toBeVisible();

        // On mobile/tablet: desktop nav is hidden, hamburger is visible
        if (vp.width < 1024) {
          await expect(page.locator('#mobile-menu-toggle')).toBeVisible();
          await expect(page.locator('ul[role="menubar"]')).toBeHidden();
        } else {
          // On desktop: hamburger is hidden, nav is visible
          await expect(page.locator('#mobile-menu-toggle')).toBeHidden();
          await expect(page.locator('ul[role="menubar"]')).toBeVisible();
        }

        // Screenshot for visual reference
        await page.screenshot({
          path: `tests/screenshots/${vp.name.toLowerCase()}-home.png`,
          fullPage: false,
        });
      });
    }
  });

  test.describe('Services page responsive layout', () => {
    for (const vp of [VIEWPORTS[0], VIEWPORTS[2]]) {
      // Mobile + Desktop
      test(`renders at ${vp.name}`, async ({ page }) => {
        await page.setViewportSize({ width: vp.width, height: vp.height });
        await page.goto('/services', { waitUntil: 'domcontentloaded' });

        await expect(page.locator('#main-header')).toBeVisible();
        await expect(page.locator('h1')).toBeVisible();

        const content = await page.locator('body').innerText();
        expect(content).toContain('Services');
      });
    }
  });

  test.describe('Portfolio page responsive layout', () => {
    for (const vp of [VIEWPORTS[0], VIEWPORTS[1], VIEWPORTS[2]]) {
      test(`renders at ${vp.name}`, async ({ page }) => {
        await page.setViewportSize({ width: vp.width, height: vp.height });
        await page.goto('/portfolio', { waitUntil: 'domcontentloaded' });

        // Filter tabs and project grid must be visible
        await expect(page.locator('[role="tablist"]')).toBeVisible();
        await expect(page.locator('#projects-grid')).toBeVisible();

        // At least one project card must be visible
        const cards = page.locator('.project-card');
        await expect(cards.first()).toBeVisible();
      });
    }
  });

  test.describe('Contact page responsive layout', () => {
    for (const vp of [VIEWPORTS[0], VIEWPORTS[2]]) {
      test(`form is usable at ${vp.name}`, async ({ page }) => {
        await page.setViewportSize({ width: vp.width, height: vp.height });
        await page.goto('/contact', { waitUntil: 'domcontentloaded' });

        await expect(page.locator('#contact-form')).toBeVisible();
        await expect(page.locator('#name')).toBeVisible();
        await expect(page.locator('#email')).toBeVisible();
        await expect(page.locator('#message')).toBeVisible();

        // Submit button is visible and clickable
        const submitBtn = page.locator('#contact-form button[type="submit"]');
        await expect(submitBtn).toBeVisible();
        await expect(submitBtn).toBeEnabled();
      });
    }
  });

  test.describe('Blog page responsive layout', () => {
    for (const vp of [VIEWPORTS[0], VIEWPORTS[2]]) {
      test(`renders at ${vp.name}`, async ({ page }) => {
        await page.setViewportSize({ width: vp.width, height: vp.height });
        await page.goto('/blog', { waitUntil: 'domcontentloaded' });

        await expect(page.locator('h1')).toBeVisible();
        await expect(page.locator('footer')).toBeVisible();
      });
    }
  });
});

// ============================================================
// 4. Navigation Testing
// ============================================================
test.describe('Navigation', () => {
  test('Desktop nav links navigate to correct pages', async ({ page }) => {
    await page.setViewportSize({ width: 1280, height: 800 });
    await page.goto('/', { waitUntil: 'domcontentloaded' });

    const navItems = [
      { label: 'About', path: '/about' },
      { label: 'Services', path: '/services' },
      { label: 'Portfolio', path: '/portfolio' },
      { label: 'Blog', path: '/blog' },
      { label: 'Contact', path: '/contact' },
    ];

    for (const item of navItems) {
      // Navigate back to home first
      await page.goto('/', { waitUntil: 'domcontentloaded' });

      // Click the nav link in the desktop menubar
      const navLink = page.locator(`ul[role="menubar"] a[href="${item.path}"]`);
      await expect(navLink).toBeVisible();
      await navLink.click();

      await page.waitForURL(`**${item.path}**`, { timeout: 5000 });
      expect(page.url()).toContain(item.path);
    }
  });

  test('Logo navigates to home page', async ({ page }) => {
    await page.goto('/about', { waitUntil: 'domcontentloaded' });

    const logo = page.locator('#main-header a[aria-label*="WorkRoot"]').first();
    await expect(logo).toBeVisible();
    await logo.click();
    await page.waitForURL('**/', { timeout: 5000 });
    expect(page.url()).toMatch(/\/$|\/$/);
  });

  test('"Get Started" CTA in header navigates to contact', async ({ page }) => {
    await page.setViewportSize({ width: 1280, height: 800 });
    await page.goto('/', { waitUntil: 'domcontentloaded' });

    const ctaBtn = page.locator('#main-header a[href="/contact"]').first();
    await expect(ctaBtn).toBeVisible();
    await ctaBtn.click();
    await page.waitForURL('**/contact', { timeout: 5000 });
    expect(page.url()).toContain('/contact');
  });

  test('Footer privacy link navigates correctly', async ({ page }) => {
    await page.goto('/', { waitUntil: 'domcontentloaded' });
    await page.evaluate(() => window.scrollTo(0, document.body.scrollHeight));

    const privacyLink = page.locator('footer a[href="/privacy"]').first();
    await expect(privacyLink).toBeVisible();
    await privacyLink.click();
    await page.waitForURL('**/privacy', { timeout: 5000 });
    expect(page.url()).toContain('/privacy');
  });

  test('Footer terms link navigates correctly', async ({ page }) => {
    await page.goto('/', { waitUntil: 'domcontentloaded' });
    await page.evaluate(() => window.scrollTo(0, document.body.scrollHeight));

    const termsLink = page.locator('footer a[href="/terms"]').first();
    await expect(termsLink).toBeVisible();
    await termsLink.click();
    await page.waitForURL('**/terms', { timeout: 5000 });
    expect(page.url()).toContain('/terms');
  });
});

// ============================================================
// 5. Mobile Navigation (hamburger menu)
// ============================================================
test.describe('Mobile Navigation Menu', () => {
  test.beforeEach(async ({ page }) => {
    await page.setViewportSize({ width: 375, height: 667 });
    await page.goto('/', { waitUntil: 'domcontentloaded' });
  });

  test('Hamburger button is visible on mobile', async ({ page }) => {
    await expect(page.locator('#mobile-menu-toggle')).toBeVisible();
  });

  test('Desktop nav is hidden on mobile', async ({ page }) => {
    await expect(page.locator('ul[role="menubar"]')).toBeHidden();
  });

  test('Mobile menu panel opens when hamburger is clicked', async ({ page }) => {
    const toggle = page.locator('#mobile-menu-toggle');
    const menu = page.locator('#mobile-menu');

    // Menu starts closed (translated off-screen)
    await expect(menu).toHaveClass(/translate-x-full/);

    await toggle.click();
    await page.waitForTimeout(400); // CSS transition

    // Menu slides in
    await expect(menu).not.toHaveClass(/translate-x-full/);
    await expect(toggle).toHaveAttribute('aria-expanded', 'true');
  });

  test('Mobile menu closes when close button is clicked', async ({ page }) => {
    await page.locator('#mobile-menu-toggle').click();
    await page.waitForTimeout(400);

    const closeBtn = page.locator('#mobile-menu-close');
    await expect(closeBtn).toBeVisible();
    await closeBtn.click();
    await page.waitForTimeout(400);

    const menu = page.locator('#mobile-menu');
    await expect(menu).toHaveClass(/translate-x-full/);
    await expect(page.locator('#mobile-menu-toggle')).toHaveAttribute('aria-expanded', 'false');
  });

  test('Mobile menu closes when overlay is clicked', async ({ page }) => {
    await page.locator('#mobile-menu-toggle').click();
    await page.waitForTimeout(400);

    const overlay = page.locator('#mobile-menu-overlay');
    await overlay.click({ force: true });
    await page.waitForTimeout(400);

    await expect(page.locator('#mobile-menu')).toHaveClass(/translate-x-full/);
  });

  test('Mobile menu closes on Escape key', async ({ page }) => {
    await page.locator('#mobile-menu-toggle').click();
    await page.waitForTimeout(400);

    await page.keyboard.press('Escape');
    await page.waitForTimeout(400);

    await expect(page.locator('#mobile-menu')).toHaveClass(/translate-x-full/);
    await expect(page.locator('#mobile-menu-toggle')).toHaveAttribute('aria-expanded', 'false');
  });

  test('Mobile menu links navigate correctly', async ({ page }) => {
    await page.locator('#mobile-menu-toggle').click();
    await page.waitForTimeout(400);

    // Click "Services" in mobile nav
    const servicesLink = page.locator('#mobile-menu a[href="/services"]');
    await expect(servicesLink).toBeVisible();
    await servicesLink.click();

    await page.waitForURL('**/services', { timeout: 5000 });
    expect(page.url()).toContain('/services');
  });

  test('Body scroll is locked while menu is open', async ({ page }) => {
    await page.locator('#mobile-menu-toggle').click();
    await page.waitForTimeout(400);

    const overflow = await page.evaluate(() => document.body.style.overflow);
    expect(overflow).toBe('hidden');

    // Scroll re-enabled on close
    await page.locator('#mobile-menu-close').click();
    await page.waitForTimeout(400);

    const overflowAfter = await page.evaluate(() => document.body.style.overflow);
    expect(overflowAfter).toBe('');
  });

  test('Menu closes automatically on resize to desktop', async ({ page }) => {
    await page.locator('#mobile-menu-toggle').click();
    await page.waitForTimeout(400);

    // Resize to desktop
    await page.setViewportSize({ width: 1280, height: 800 });
    await page.waitForTimeout(400);

    await expect(page.locator('#mobile-menu')).toHaveClass(/translate-x-full/);
  });
});

// ============================================================
// 6. Contact Form Testing
// ============================================================
test.describe('Contact Form', () => {
  test.beforeEach(async ({ page }) => {
    await page.goto('/contact', { waitUntil: 'domcontentloaded' });
  });

  test('Form elements are all present', async ({ page }) => {
    await expect(page.locator('#contact-form')).toBeVisible();
    await expect(page.locator('#name')).toBeVisible();
    await expect(page.locator('#email')).toBeVisible();
    await expect(page.locator('#phone')).toBeVisible();
    await expect(page.locator('#subject')).toBeVisible();
    await expect(page.locator('#message')).toBeVisible();

    // Honeypot field is present but visually hidden
    await expect(page.locator('#website')).toBeAttached();
    await expect(page.locator('#website')).not.toBeVisible();
  });

  test('Subject dropdown has all expected options', async ({ page }) => {
    const options = await page.locator('#subject option').allTextContents();
    expect(options).toContain('Web Development');
    expect(options).toContain('Mobile Development');
    expect(options).toContain('Cloud Services');
    expect(options).toContain('AI & Machine Learning');
    expect(options).toContain('IT Consulting');
    expect(options).toContain('Technical Support');
    expect(options).toContain('Other');
  });

  test('Error messages are hidden initially', async ({ page }) => {
    await expect(page.locator('[data-error="name"]')).toBeHidden();
    await expect(page.locator('[data-error="email"]')).toBeHidden();
    await expect(page.locator('[data-error="subject"]')).toBeHidden();
    await expect(page.locator('[data-error="message"]')).toBeHidden();
  });

  test('Submit button is visible and enabled', async ({ page }) => {
    const btn = page.locator('#contact-form button[type="submit"]');
    await expect(btn).toBeVisible();
    await expect(btn).toBeEnabled();
    await expect(btn).toContainText('Send Message');
  });

  test('Name field accepts valid input', async ({ page }) => {
    const nameInput = page.locator('#name');
    await nameInput.fill('John Doe');
    expect(await nameInput.inputValue()).toBe('John Doe');
  });

  test('Email field accepts valid email format', async ({ page }) => {
    const emailInput = page.locator('#email');
    await emailInput.fill('john@example.com');
    expect(await emailInput.inputValue()).toBe('john@example.com');
  });

  test('Phone field is optional and accepts phone number', async ({ page }) => {
    const phoneInput = page.locator('#phone');
    await phoneInput.fill('+1 (555) 123-4567');
    expect(await phoneInput.inputValue()).toBe('+1 (555) 123-4567');
  });

  test('Message textarea accepts multi-line text', async ({ page }) => {
    const messageInput = page.locator('#message');
    const testMessage = 'This is a test message.\nWith multiple lines.\nFor our project.';
    await messageInput.fill(testMessage);
    expect(await messageInput.inputValue()).toBe(testMessage);
  });

  test('Full form can be filled out completely', async ({ page }) => {
    await page.fill('#name', 'Test User');
    await page.fill('#email', 'test@example.com');
    await page.fill('#phone', '+1234567890');
    await page.selectOption('#subject', 'web-development');
    await page.fill('#message', 'This is a complete test message for automated testing purposes.');

    // All fields should have their values
    expect(await page.locator('#name').inputValue()).toBe('Test User');
    expect(await page.locator('#email').inputValue()).toBe('test@example.com');
    expect(await page.locator('#subject').inputValue()).toBe('web-development');
    expect((await page.locator('#message').inputValue()).length).toBeGreaterThan(10);
  });

  test('Honeypot field is not visible to users', async ({ page }) => {
    // Real users can't see or interact with the honeypot field
    const honeypot = page.locator('#website');
    await expect(honeypot).toBeAttached();

    // It should not be focusable via tab (tabindex="-1")
    const tabIndex = await honeypot.getAttribute('tabindex');
    expect(tabIndex).toBe('-1');
  });

  test('Form is accessible on mobile viewport', async ({ page }) => {
    await page.setViewportSize({ width: 375, height: 667 });
    await page.goto('/contact', { waitUntil: 'domcontentloaded' });

    // All required fields are visible and tappable on mobile
    await expect(page.locator('#name')).toBeVisible();
    await expect(page.locator('#email')).toBeVisible();
    await expect(page.locator('#subject')).toBeVisible();
    await expect(page.locator('#message')).toBeVisible();
    await expect(page.locator('#contact-form button[type="submit"]')).toBeVisible();
  });
});

// ============================================================
// 7. Portfolio Filter Functionality
// ============================================================
test.describe('Portfolio Filters', () => {
  test.beforeEach(async ({ page }) => {
    await page.goto('/portfolio', { waitUntil: 'domcontentloaded' });
    // Wait for project cards to be rendered
    await expect(page.locator('.project-card').first()).toBeVisible();
  });

  test('All filter buttons are present', async ({ page }) => {
    await expect(page.locator('button[data-filter="all"]')).toBeVisible();
    await expect(page.locator('button[data-filter="web"]')).toBeVisible();
    await expect(page.locator('button[data-filter="mobile"]')).toBeVisible();
    await expect(page.locator('button[data-filter="ai"]')).toBeVisible();
  });

  test('"All Projects" filter is active by default', async ({ page }) => {
    const allBtn = page.locator('button[data-filter="all"]');
    await expect(allBtn).toHaveClass(/active/);
    await expect(allBtn).toHaveAttribute('aria-selected', 'true');

    // All 8 project cards should be visible
    const cards = page.locator('.project-card');
    await expect(cards).toHaveCount(8);
  });

  test('"Web Development" filter shows only web projects', async ({ page }) => {
    const webBtn = page.locator('button[data-filter="web"]');
    await webBtn.click();
    await page.waitForTimeout(500); // CSS animation

    await expect(webBtn).toHaveAttribute('aria-selected', 'true');

    const visibleCards = page.locator('.project-card:not(.hidden)');
    const count = await visibleCards.count();
    expect(count).toBeGreaterThan(0);

    // All visible cards should be web category
    const categories = await visibleCards.evaluateAll((cards) =>
      cards.map((c) => (c as HTMLElement).dataset.category)
    );
    expect(categories.every((cat) => cat === 'web')).toBe(true);
  });

  test('"Mobile Apps" filter shows only mobile projects', async ({ page }) => {
    await page.locator('button[data-filter="mobile"]').click();
    await page.waitForTimeout(500);

    const visibleCards = page.locator('.project-card:not(.hidden)');
    const count = await visibleCards.count();
    expect(count).toBeGreaterThan(0);

    const categories = await visibleCards.evaluateAll((cards) =>
      cards.map((c) => (c as HTMLElement).dataset.category)
    );
    expect(categories.every((cat) => cat === 'mobile')).toBe(true);
  });

  test('"AI & ML" filter shows only AI projects', async ({ page }) => {
    await page.locator('button[data-filter="ai"]').click();
    await page.waitForTimeout(500);

    const visibleCards = page.locator('.project-card:not(.hidden)');
    const count = await visibleCards.count();
    expect(count).toBeGreaterThan(0);

    const categories = await visibleCards.evaluateAll((cards) =>
      cards.map((c) => (c as HTMLElement).dataset.category)
    );
    expect(categories.every((cat) => cat === 'ai')).toBe(true);
  });

  test('Switching back to "All" restores all projects', async ({ page }) => {
    // Filter to web first
    await page.locator('button[data-filter="web"]').click();
    await page.waitForTimeout(500);

    // Switch back to all
    await page.locator('button[data-filter="all"]').click();
    await page.waitForTimeout(500);

    const visibleCards = page.locator('.project-card:not(.hidden)');
    await expect(visibleCards).toHaveCount(8);
  });

  test('Only one filter button is active at a time', async ({ page }) => {
    await page.locator('button[data-filter="web"]').click();
    await page.waitForTimeout(300);

    const activeButtons = page.locator('button[data-filter][aria-selected="true"]');
    await expect(activeButtons).toHaveCount(1);
    await expect(activeButtons.first()).toHaveAttribute('data-filter', 'web');
  });

  test('Case study modal opens when "View Case Study" is clicked', async ({ page }) => {
    const firstDetailsBtn = page.locator('.view-details-btn').first();
    await firstDetailsBtn.click();
    await page.waitForTimeout(400);

    const modal = page.locator('#case-study-modal');
    await expect(modal).not.toHaveClass(/hidden/);
    await expect(modal).toBeVisible();

    // Modal has a title
    await expect(page.locator('#modal-title')).toBeVisible();
  });

  test('Case study modal closes when close button is clicked', async ({ page }) => {
    await page.locator('.view-details-btn').first().click();
    await page.waitForTimeout(400);

    await page.locator('#close-modal-btn').click();
    await page.waitForTimeout(400);

    await expect(page.locator('#case-study-modal')).toHaveClass(/hidden/);
  });

  test('Case study modal closes on Escape key', async ({ page }) => {
    await page.locator('.view-details-btn').first().click();
    await page.waitForTimeout(400);

    await page.keyboard.press('Escape');
    await page.waitForTimeout(400);

    await expect(page.locator('#case-study-modal')).toHaveClass(/hidden/);
  });

  test('Portfolio filters work on mobile viewport', async ({ page }) => {
    await page.setViewportSize({ width: 375, height: 667 });
    await page.goto('/portfolio', { waitUntil: 'domcontentloaded' });
    await expect(page.locator('.project-card').first()).toBeVisible();

    // Filter tabs should be visible (flex-wrap layout)
    await expect(page.locator('[role="tablist"]')).toBeVisible();

    await page.locator('button[data-filter="web"]').click();
    await page.waitForTimeout(500);

    const visibleCards = page.locator('.project-card:not(.hidden)');
    const count = await visibleCards.count();
    expect(count).toBeGreaterThan(0);
  });
});

// ============================================================
// 8. Blog Markdown Rendering
// ============================================================
test.describe('Blog Rendering', () => {
  test('Blog listing page displays articles', async ({ page }) => {
    await page.goto('/blog', { waitUntil: 'domcontentloaded' });

    await expect(page.locator('h1')).toBeVisible();

    // Blog listing should have links to posts or a "no posts" message
    const postLinks = page.locator('a[href^="/blog/"]');
    const articleCards = page.locator('article');
    const totalContent = (await postLinks.count()) + (await articleCards.count());

    // Either posts exist or page loads cleanly
    const bodyText = await page.locator('body').innerText();
    expect(bodyText.trim().length).toBeGreaterThan(50);
  });

  test('Navigating to a blog post renders markdown content', async ({ page }) => {
    await page.goto('/blog', { waitUntil: 'domcontentloaded' });

    const firstPostLink = page.locator('a[href^="/blog/"]').first();
    const hasPost = (await firstPostLink.count()) > 0;

    if (!hasPost) {
      test.skip(); // No blog posts in content collection - skip gracefully
      return;
    }

    await firstPostLink.click();
    await page.waitForLoadState('domcontentloaded');

    // Page must have an h1 title
    await expect(page.locator('h1')).toBeVisible();

    // Page must have substantial content (markdown rendered to paragraphs)
    const paragraphs = page.locator('p');
    const pCount = await paragraphs.count();
    expect(pCount).toBeGreaterThan(0);

    // Should have prose content area
    const content = await page.locator('body').innerText();
    expect(content.trim().length).toBeGreaterThan(100);
  });

  test('Blog post page has proper heading hierarchy', async ({ page }) => {
    await page.goto('/blog', { waitUntil: 'domcontentloaded' });
    const firstPostLink = page.locator('a[href^="/blog/"]').first();
    if ((await firstPostLink.count()) === 0) {
      test.skip();
      return;
    }

    await firstPostLink.click();
    await page.waitForLoadState('domcontentloaded');

    // Must have exactly one h1
    const h1Count = await page.locator('h1').count();
    expect(h1Count).toBeGreaterThanOrEqual(1);
  });

  test('Blog post code blocks render with pre/code tags', async ({ page }) => {
    await page.goto('/blog', { waitUntil: 'domcontentloaded' });
    const firstPostLink = page.locator('a[href^="/blog/"]').first();
    if ((await firstPostLink.count()) === 0) {
      test.skip();
      return;
    }

    await firstPostLink.click();
    await page.waitForLoadState('domcontentloaded');

    const htmlContent = await page.content();
    // Code blocks use <pre> tags in markdown rendering - acceptable if absent (post may have no code)
    const hasPreTag = htmlContent.includes('<pre') || htmlContent.includes('<code');
    // Just verify the page rendered properly (code blocks optional per post content)
    expect(htmlContent.length).toBeGreaterThan(500);

    await page.screenshot({
      path: 'tests/screenshots/blog-post-rendered.png',
      fullPage: true,
    });
  });

  test('Blog listing renders on mobile viewport', async ({ page }) => {
    await page.setViewportSize({ width: 375, height: 667 });
    await page.goto('/blog', { waitUntil: 'domcontentloaded' });

    await expect(page.locator('h1')).toBeVisible();
    await expect(page.locator('#main-header')).toBeVisible();
    await expect(page.locator('footer')).toBeVisible();
  });
});

// ============================================================
// 9. Console Error Monitoring (comprehensive - all pages, all known errors)
// ============================================================
test.describe('Console Error Monitoring', () => {
  test('No critical JavaScript errors across all pages', async ({ page }) => {
    const errorLog: { url: string; error: string }[] = [];

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

    page.on('pageerror', (err) => {
      errorLog.push({ url: page.url(), error: err.message });
    });

    const testPages = ALL_PAGES.filter((p) => p.url !== '/nonexistent-page-xyz');

    for (const p of testPages) {
      await page.goto(p.url, { waitUntil: 'domcontentloaded' });
      await page.waitForTimeout(500); // Allow async scripts to run
    }

    const critical = errorLog.filter(
      (e) =>
        !e.error.includes('favicon') &&
        !e.error.includes('livereload') &&
        !e.error.includes('DevTools') &&
        !e.error.includes('manifest') &&
        !e.error.includes('net::ERR_ABORTED')
    );

    if (critical.length > 0) {
      console.error('CRITICAL CONSOLE ERRORS FOUND:');
      critical.forEach((e) => console.error(`  [${e.url}] ${e.error}`));
    }

    expect(critical, `Found ${critical.length} critical console error(s)`).toHaveLength(0);
  });

  test('No failed network requests (4xx/5xx) for critical assets', async ({ page }) => {
    const failedRequests: { url: string; status: number }[] = [];

    page.on('response', (response) => {
      const status = response.status();
      const url = response.url();

      // Only track failures for page-relative assets (JS, CSS, fonts, images from same origin)
      if (
        status >= 400 &&
        (url.includes('.js') ||
          url.includes('.css') ||
          url.includes('.woff') ||
          url.includes('.woff2'))
      ) {
        failedRequests.push({ url, status });
      }
    });

    await page.goto('/', { waitUntil: 'networkidle' });

    if (failedRequests.length > 0) {
      console.warn('Failed asset requests:', failedRequests);
    }

    expect(failedRequests).toHaveLength(0);
  });
});

// ============================================================
// 10. Header Scroll Behavior
// ============================================================
test.describe('Header Scroll Behavior', () => {
  test('Header gets shadow class after scrolling down', async ({ page }) => {
    await page.goto('/', { waitUntil: 'domcontentloaded' });

    const header = page.locator('#main-header');
    await expect(header).not.toHaveClass(/header-scrolled/);

    // Scroll down
    await page.evaluate(() => window.scrollTo(0, 100));
    await page.waitForTimeout(200);

    await expect(header).toHaveClass(/header-scrolled/);
  });

  test('Header shadow is removed when scrolled back to top', async ({ page }) => {
    await page.goto('/', { waitUntil: 'domcontentloaded' });

    await page.evaluate(() => window.scrollTo(0, 200));
    await page.waitForTimeout(200);

    await page.evaluate(() => window.scrollTo(0, 0));
    await page.waitForTimeout(200);

    await expect(page.locator('#main-header')).not.toHaveClass(/header-scrolled/);
  });
});

// ============================================================
// 11. Redesigned Contact Page — New Elements
// ============================================================
test.describe('Contact Page: Redesigned Elements', () => {
  test.beforeEach(async ({ page }) => {
    await page.goto('/contact', { waitUntil: 'domcontentloaded' });
  });

  test('Trust stats strip renders with 4 stat cards', async ({ page }) => {
    // Hero section contains 4 trust stat cards
    const statCards = page.locator('section').first().locator('.text-2xl.font-bold.text-white');
    await expect(statCards).toHaveCount(4);
  });

  test('Budget range radio chips render and are selectable', async ({ page }) => {
    const budgetOptions = page.locator('.budget-option');
    await expect(budgetOptions).toHaveCount(5);

    // Click the first budget chip
    await budgetOptions.first().click();
    await page.waitForTimeout(200);

    // The clicked chip should receive the "selected" class via JS
    await expect(budgetOptions.first()).toHaveClass(/selected/);

    // Only one chip should be selected at a time
    const selectedChips = page.locator('.budget-option.selected');
    await expect(selectedChips).toHaveCount(1);
  });

  test('Budget chips are mutually exclusive (radio behavior)', async ({ page }) => {
    const budgetOptions = page.locator('.budget-option');

    await budgetOptions.nth(0).click();
    await page.waitForTimeout(150);
    await budgetOptions.nth(2).click();
    await page.waitForTimeout(150);

    // Only the second-clicked chip should be selected
    await expect(budgetOptions.nth(0)).not.toHaveClass(/selected/);
    await expect(budgetOptions.nth(2)).toHaveClass(/selected/);
  });

  test('FAQ accordion sections render (4 questions)', async ({ page }) => {
    const faqItems = page.locator('.faq-item');
    await expect(faqItems).toHaveCount(4);

    // All summaries are visible
    for (let i = 0; i < 4; i++) {
      await expect(faqItems.nth(i).locator('summary')).toBeVisible();
    }
  });

  test('FAQ accordion opens and closes on click', async ({ page }) => {
    const firstFaq = page.locator('.faq-item').first();
    const summary = firstFaq.locator('summary');

    // Initially closed
    const isOpenInitially = await firstFaq.evaluate((el) => (el as HTMLDetailsElement).open);
    expect(isOpenInitially).toBe(false);

    // Open it
    await summary.click();
    await page.waitForTimeout(200);
    const isOpenAfterClick = await firstFaq.evaluate((el) => (el as HTMLDetailsElement).open);
    expect(isOpenAfterClick).toBe(true);

    // Content is now visible
    await expect(firstFaq.locator('p')).toBeVisible();

    // Close it again
    await summary.click();
    await page.waitForTimeout(200);
    const isClosedAgain = await firstFaq.evaluate((el) => (el as HTMLDetailsElement).open);
    expect(isClosedAgain).toBe(false);
  });

  test('FAQ accordion is keyboard accessible', async ({ page }) => {
    const summary = page.locator('.faq-item').first().locator('summary');
    await summary.focus();

    // Enter key should toggle open
    await page.keyboard.press('Enter');
    await page.waitForTimeout(200);
    const isOpen = await page.locator('.faq-item').first().evaluate((el) => (el as HTMLDetailsElement).open);
    expect(isOpen).toBe(true);
  });

  test('Social links section renders all 4 platforms', async ({ page }) => {
    // Social links section contains 4 social link items
    const socialLinks = page.locator('.bg-secondary-900 a[aria-label]');
    await expect(socialLinks).toHaveCount(4);

    // Each has an aria-label
    for (let i = 0; i < 4; i++) {
      const label = await socialLinks.nth(i).getAttribute('aria-label');
      expect(label).toBeTruthy();
    }
  });

  test('Map placeholder renders with directions link', async ({ page }) => {
    // Map section has a "Get Directions" link
    const directionsLink = page.locator('a[aria-label*="Get directions"]');
    await expect(directionsLink).toBeAttached();
    await expect(directionsLink).toHaveAttribute('href', /maps\.google\.com/);
    await expect(directionsLink).toHaveAttribute('target', '_blank');
    await expect(directionsLink).toHaveAttribute('rel', 'noopener noreferrer');
  });

  test('Character counter updates as user types in message', async ({ page }) => {
    const charCount = page.locator('#char-count');
    await expect(charCount).toBeVisible();

    const initialText = await charCount.textContent();
    expect(initialText).toBe('0 / 5000');

    await page.fill('#message', 'Hello world');
    await page.waitForTimeout(150);

    const updatedText = await charCount.textContent();
    expect(updatedText).toBe('11 / 5000');
  });

  test('Character counter turns red when approaching limit', async ({ page }) => {
    // Fill message with 4600 characters (triggers red state at > 4500)
    const longMsg = 'a'.repeat(4501);
    await page.fill('#message', longMsg);
    await page.waitForTimeout(150);

    const charCount = page.locator('#char-count');
    await expect(charCount).toHaveClass(/text-red-500/);
  });

  test('Success state is hidden by default', async ({ page }) => {
    await expect(page.locator('#success-state')).toHaveClass(/hidden/);
    await expect(page.locator('#contact-form')).not.toHaveClass(/hidden/);
  });

  test('Toast container is present and has live region', async ({ page }) => {
    const toastContainer = page.locator('#toast-container');
    await expect(toastContainer).toBeAttached();
    await expect(toastContainer).toHaveAttribute('aria-live', 'assertive');
  });

  test('Scroll-reveal elements are present', async ({ page }) => {
    const revealElements = page.locator('.reveal-on-scroll');
    const count = await revealElements.count();
    expect(count).toBeGreaterThan(0);
  });

  test('Contact info cards render 4 cards (location, phone, email, hours)', async ({ page }) => {
    const contactCards = page.locator('.contact-card');
    await expect(contactCards).toHaveCount(4);

    // Cards show expected headings
    const cardTitles = await page.locator('.contact-card h3').allTextContents();
    expect(cardTitles).toContain('Visit Us');
    expect(cardTitles).toContain('Call Us');
    expect(cardTitles).toContain('Email Us');
    expect(cardTitles).toContain('Business Hours');
  });

  test('CTA banner "Start a Project" scrolls to form', async ({ page }) => {
    const ctaBtn = page.locator('a[href="#contact-form"]');
    await expect(ctaBtn).toBeVisible();
  });

  test('Contact page renders correctly at mobile viewport', async ({ page }) => {
    await page.setViewportSize({ width: 375, height: 667 });
    await page.goto('/contact', { waitUntil: 'domcontentloaded' });

    // Form is visible and usable
    await expect(page.locator('#contact-form')).toBeVisible();

    // Budget chips wrap on mobile (they use flex-wrap)
    const budgetOptions = page.locator('.budget-option');
    await expect(budgetOptions.first()).toBeVisible();

    // FAQ section is visible
    await expect(page.locator('.faq-item').first()).toBeVisible();

    // Social links are visible
    await expect(page.locator('.bg-secondary-900')).toBeVisible();

    await page.screenshot({
      path: 'tests/screenshots/mobile-contact-redesign.png',
      fullPage: false,
    });
  });

  test('Contact page renders correctly at tablet viewport', async ({ page }) => {
    await page.setViewportSize({ width: 768, height: 1024 });
    await page.goto('/contact', { waitUntil: 'domcontentloaded' });

    await expect(page.locator('#contact-form')).toBeVisible();
    await expect(page.locator('.contact-card').first()).toBeVisible();

    await page.screenshot({
      path: 'tests/screenshots/tablet-contact-redesign.png',
      fullPage: false,
    });
  });

  test('Contact page renders correctly at desktop viewport', async ({ page }) => {
    await page.setViewportSize({ width: 1280, height: 800 });
    await page.goto('/contact', { waitUntil: 'domcontentloaded' });

    // 5-column grid: form (3 cols) + sidebar (2 cols)
    await expect(page.locator('#contact-form')).toBeVisible();
    await expect(page.locator('.contact-card').first()).toBeVisible();
    await expect(page.locator('.bg-secondary-900')).toBeVisible();

    await page.screenshot({
      path: 'tests/screenshots/desktop-contact-redesign.png',
      fullPage: false,
    });
  });
});

// ============================================================
// 12. Scroll-Reveal Animations (cross-page)
// ============================================================
test.describe('Scroll-Reveal Animations', () => {
  const ANIMATION_PAGES = ['/', '/about', '/services', '/contact', '/portfolio'];

  for (const url of ANIMATION_PAGES) {
    test(`Scroll-reveal elements activate on ${url}`, async ({ page }) => {
      await page.goto(url, { waitUntil: 'domcontentloaded' });

      const revealElements = page.locator('.reveal-on-scroll');
      const count = await revealElements.count();

      if (count === 0) return; // Page may not have reveal elements

      // Simulate scrolling to trigger IntersectionObserver
      await page.evaluate(() => {
        window.scrollTo(0, document.body.scrollHeight / 2);
      });
      await page.waitForTimeout(800); // Allow CSS transitions

      // At least some elements should be revealed
      const revealedCount = await page.locator('.reveal-on-scroll.revealed').count();
      expect(revealedCount).toBeGreaterThan(0);
    });
  }

  test('Reduced motion: reveal elements are immediately visible', async ({ page }) => {
    await page.emulateMedia({ reducedMotion: 'reduce' });
    await page.goto('/contact', { waitUntil: 'domcontentloaded' });

    // With reduced motion, CSS transitions are disabled so revealed class may not be added,
    // but the element should still be visually accessible (opacity: 1, transform: none via CSS)
    const revealEl = page.locator('.reveal-on-scroll').first();
    await expect(revealEl).toBeAttached();

    // Verify via computed style — reduced motion CSS sets opacity: 1 directly
    const opacity = await revealEl.evaluate((el) =>
      window.getComputedStyle(el).getPropertyValue('opacity')
    );
    expect(opacity).toBe('1');
  });
});

// ============================================================
// 13. Cross-Browser Visual Snapshots (all redesigned pages)
// ============================================================
test.describe('Visual Snapshots: Redesigned Pages', () => {
  const SNAPSHOT_PAGES = [
    { name: 'home', url: '/' },
    { name: 'about', url: '/about' },
    { name: 'services', url: '/services' },
    { name: 'portfolio', url: '/portfolio' },
    { name: 'contact', url: '/contact' },
    { name: 'blog', url: '/blog' },
  ];

  for (const p of SNAPSHOT_PAGES) {
    test(`Desktop screenshot: ${p.name}`, async ({ page, browserName }) => {
      await page.setViewportSize({ width: 1280, height: 800 });
      await page.goto(p.url, { waitUntil: 'domcontentloaded' });

      // Trigger scroll-reveal for above-fold content
      await page.evaluate(() => window.scrollTo(0, 1));
      await page.waitForTimeout(500);
      await page.evaluate(() => window.scrollTo(0, 0));

      await page.screenshot({
        path: `tests/screenshots/${browserName}-${p.name}-desktop.png`,
        fullPage: false,
        clip: { x: 0, y: 0, width: 1280, height: 800 },
      });
    });

    test(`Mobile screenshot: ${p.name}`, async ({ page, browserName }) => {
      await page.setViewportSize({ width: 375, height: 667 });
      await page.goto(p.url, { waitUntil: 'domcontentloaded' });
      await page.waitForTimeout(300);

      await page.screenshot({
        path: `tests/screenshots/${browserName}-${p.name}-mobile.png`,
        fullPage: false,
      });
    });
  }
});

// ============================================================
// 14. Cross-Browser Form Validation Behavior
// ============================================================
test.describe('Cross-Browser: Form Validation', () => {
  test.beforeEach(async ({ page }) => {
    await page.goto('/contact', { waitUntil: 'domcontentloaded' });
  });

  test('Submitting empty form shows validation errors', async ({ page }) => {
    const submitBtn = page.locator('#contact-form button[type="submit"]');
    await submitBtn.click();
    await page.waitForTimeout(300);

    // At minimum, name, email, subject, and message errors should show
    const nameError = page.locator('[data-error="name"]');
    const emailError = page.locator('[data-error="email"]');
    const subjectError = page.locator('[data-error="subject"]');
    const messageError = page.locator('[data-error="message"]');

    // At least one error should be visible after empty submit
    const errorsVisible = await Promise.all([
      nameError.isVisible(),
      emailError.isVisible(),
      subjectError.isVisible(),
      messageError.isVisible(),
    ]);
    expect(errorsVisible.some(Boolean)).toBe(true);
  });

  test('Valid name field clears error and shows checkmark', async ({ page }) => {
    // First trigger validation
    await page.locator('#name').fill('A'); // too short
    await page.locator('#name').blur();
    await page.waitForTimeout(150);

    await expect(page.locator('[data-error="name"]')).toBeVisible();
    await expect(page.locator('#name')).toHaveClass(/is-invalid/);

    // Fix the error
    await page.locator('#name').fill('John Doe');
    await page.locator('#name').blur();
    await page.waitForTimeout(150);

    await expect(page.locator('[data-error="name"]')).toBeHidden();
    await expect(page.locator('#name')).toHaveClass(/is-valid/);
  });

  test('Invalid email shows error, valid email clears it', async ({ page }) => {
    await page.locator('#email').fill('not-an-email');
    await page.locator('#email').blur();
    await page.waitForTimeout(150);

    await expect(page.locator('[data-error="email"]')).toBeVisible();
    await expect(page.locator('#email')).toHaveClass(/is-invalid/);

    await page.locator('#email').fill('valid@example.com');
    await page.locator('#email').blur();
    await page.waitForTimeout(150);

    await expect(page.locator('[data-error="email"]')).toBeHidden();
    await expect(page.locator('#email')).toHaveClass(/is-valid/);
  });

  test('Empty phone (optional) does not show error', async ({ page }) => {
    await page.locator('#phone').focus();
    await page.locator('#phone').blur();
    await page.waitForTimeout(150);

    // Phone is optional — empty value should not trigger error
    await expect(page.locator('[data-error="phone"]')).toBeHidden();
  });

  test('Short message triggers error, adequate message clears it', async ({ page }) => {
    await page.locator('#message').fill('Hi'); // < 10 chars
    await page.locator('#message').blur();
    await page.waitForTimeout(150);

    await expect(page.locator('[data-error="message"]')).toBeVisible();

    await page.locator('#message').fill('This is a valid message with enough content.');
    await page.locator('#message').blur();
    await page.waitForTimeout(150);

    await expect(page.locator('[data-error="message"]')).toBeHidden();
  });

  test('Form resets correctly after "Send another message"', async ({ page }) => {
    // Pre-fill form
    await page.fill('#name', 'Test User');
    await page.fill('#email', 'test@example.com');
    await page.fill('#message', 'Test message content here.');

    // Manually simulate success state (direct DOM manipulation)
    await page.evaluate(() => {
      document.getElementById('contact-form')?.classList.add('hidden');
      document.getElementById('success-state')?.classList.remove('hidden');
    });

    await expect(page.locator('#success-state')).not.toHaveClass(/hidden/);

    // Click "Send another message"
    await page.locator('#send-another').click();
    await page.waitForTimeout(300);

    // Form should be visible again, success state hidden
    await expect(page.locator('#contact-form')).not.toHaveClass(/hidden/);
    await expect(page.locator('#success-state')).toHaveClass(/hidden/);

    // Fields should be cleared
    expect(await page.locator('#name').inputValue()).toBe('');
    expect(await page.locator('#message').inputValue()).toBe('');
  });
});

// ============================================================
// 15. Cross-Browser: Services Page Redesign
// ============================================================
test.describe('Services Page: Cross-Browser Layout', () => {
  test('Services page loads with all key sections', async ({ page }) => {
    await page.goto('/services', { waitUntil: 'domcontentloaded' });

    // Page loads
    expect((await page.goto('/services'))?.status()).toBe(200);

    await expect(page.locator('h1')).toBeVisible();
    await expect(page.locator('footer')).toBeVisible();
  });

  test('Services page has no horizontal scroll overflow at mobile', async ({ page }) => {
    await page.setViewportSize({ width: 375, height: 667 });
    await page.goto('/services', { waitUntil: 'domcontentloaded' });

    const bodyScrollWidth = await page.evaluate(() => document.body.scrollWidth);
    const viewportWidth = await page.evaluate(() => window.innerWidth);

    expect(bodyScrollWidth).toBeLessThanOrEqual(viewportWidth + 5); // 5px tolerance
  });

  test('Services page has no horizontal scroll overflow at desktop', async ({ page }) => {
    await page.setViewportSize({ width: 1280, height: 800 });
    await page.goto('/services', { waitUntil: 'domcontentloaded' });

    const bodyScrollWidth = await page.evaluate(() => document.body.scrollWidth);
    const viewportWidth = await page.evaluate(() => window.innerWidth);

    expect(bodyScrollWidth).toBeLessThanOrEqual(viewportWidth + 5);
  });
});

// ============================================================
// 16. Cross-Browser: Portfolio Page Advanced Filtering
// ============================================================
test.describe('Portfolio Page: Advanced Filtering Cross-Browser', () => {
  test('Portfolio filter count badge updates when filtering', async ({ page }) => {
    await page.goto('/portfolio', { waitUntil: 'domcontentloaded' });
    await expect(page.locator('.project-card').first()).toBeVisible();

    const initialCount = await page.locator('.project-card:not(.hidden)').count();
    expect(initialCount).toBeGreaterThan(0);

    // Filter to web
    await page.locator('button[data-filter="web"]').click();
    await page.waitForTimeout(500);

    const filteredCount = await page.locator('.project-card:not(.hidden)').count();
    expect(filteredCount).toBeGreaterThan(0);
    expect(filteredCount).toBeLessThanOrEqual(initialCount);
  });

  test('Portfolio gallery modal shows project details', async ({ page }) => {
    await page.goto('/portfolio', { waitUntil: 'domcontentloaded' });
    await expect(page.locator('.view-details-btn').first()).toBeVisible();

    await page.locator('.view-details-btn').first().click();
    await page.waitForTimeout(400);

    const modal = page.locator('#case-study-modal');
    await expect(modal).toBeVisible();

    // Modal has content
    await expect(page.locator('#modal-title')).toBeVisible();
  });

  test('Portfolio page has no horizontal scroll overflow at mobile', async ({ page }) => {
    await page.setViewportSize({ width: 375, height: 667 });
    await page.goto('/portfolio', { waitUntil: 'domcontentloaded' });

    const bodyScrollWidth = await page.evaluate(() => document.body.scrollWidth);
    const viewportWidth = await page.evaluate(() => window.innerWidth);
    expect(bodyScrollWidth).toBeLessThanOrEqual(viewportWidth + 5);
  });
});

// ============================================================
// 17. Cross-Browser: No Horizontal Overflow (all pages)
// ============================================================
test.describe('Cross-Browser: No Horizontal Scroll Overflow', () => {
  const CHECK_PAGES = ['/', '/about', '/services', '/portfolio', '/contact', '/blog'];

  for (const url of CHECK_PAGES) {
    test(`No horizontal overflow at mobile on ${url}`, async ({ page }) => {
      await page.setViewportSize({ width: 375, height: 667 });
      await page.goto(url, { waitUntil: 'domcontentloaded' });

      const bodyScrollWidth = await page.evaluate(() => document.body.scrollWidth);
      const viewportWidth = await page.evaluate(() => window.innerWidth);
      expect(bodyScrollWidth).toBeLessThanOrEqual(viewportWidth + 5);
    });
  }
});
