/**
 * [TEC-3078] Visual/density regression — breakpoint 1920/1440/1280/1024/768px
 * [TEC-3079] Console errors = 0 và network 4xx/5xx = 0
 * [TEC-3080] Interaction: sort/filter bảng, ticker, chart render
 *
 * Goodgo Platform AI — sàn giao dịch refactor QA
 * Chạy E2E với backend thật. Không stub/mock.
 *
 * npx playwright test --project=web e2e/web/trading-floor-regression.spec.ts
 */
import { test, expect } from '@playwright/test';

// ---------------------------------------------------------------------------
// Helpers
// ---------------------------------------------------------------------------

const BREAKPOINTS = [
  { name: '1920p', width: 1920, height: 1080 },
  { name: '1440p', width: 1440, height: 900 },
  { name: '1280p', width: 1280, height: 800 },
  { name: '1024p', width: 1024, height: 768 },
  { name: '768p',  width: 768,  height: 1024 },
] as const;

function attachNetworkMonitor(page: import('@playwright/test').Page) {
  const consoleErrors: string[] = [];
  const networkErrors: { url: string; status: number }[] = [];

  page.on('console', (msg) => {
    if (msg.type() === 'error') {
      const text = msg.text();
      if (
        text.includes('mapbox') ||
        text.includes('NEXT_PUBLIC_MAPBOX') ||
        text.includes('Failed to load resource') ||
        text.includes('net::ERR')
      ) return;
      consoleErrors.push(text);
    }
  });

  page.on('response', (res) => {
    const status = res.status();
    const url = res.url();
    if (status >= 400 && (url.includes('/api/v1') || url.includes('localhost'))) {
      networkErrors.push({ url, status });
    }
  });

  return { consoleErrors, networkErrors };
}

// ---------------------------------------------------------------------------
// [TEC-3078] Breakpoint regression
// ---------------------------------------------------------------------------

test.describe('Breakpoint regression — sàn giao dịch', () => {
  for (const bp of BREAKPOINTS) {
    test(`/ home dashboard — ${bp.name} (${bp.width}×${bp.height})`, async ({ page }) => {
      await page.setViewportSize({ width: bp.width, height: bp.height });
      await page.goto('/');
      await page.waitForLoadState('networkidle', { timeout: 20_000 }).catch(() => {});

      // Không crash
      await expect(
        page.getByRole('heading', { name: /500|server error/i }),
      ).not.toBeVisible();

      // Main content vẫn visible ở mọi breakpoint
      await expect(page.locator('main').or(page.locator('#__next'))).toBeVisible();

      // Không có horizontal overflow (scrollWidth > clientWidth = layout break)
      const hasHorizontalOverflow = await page.evaluate(() => {
        return document.documentElement.scrollWidth > document.documentElement.clientWidth + 2;
      });
      expect(
        hasHorizontalOverflow,
        `Horizontal overflow tại ${bp.name} — kiểm tra CSS layout`,
      ).toBeFalsy();
    });

    test(`/listings board — ${bp.name} (${bp.width}×${bp.height})`, async ({ page }) => {
      await page.setViewportSize({ width: bp.width, height: bp.height });
      await page.goto('/listings');
      await page.waitForLoadState('networkidle', { timeout: 20_000 }).catch(() => {});

      await expect(
        page.getByRole('heading', { name: /500|server error/i }),
      ).not.toBeVisible();

      // Bảng hoặc card listings phải visible
      const listingEl = page
        .locator('table')
        .or(page.locator('[data-testid="data-table"]'))
        .or(page.locator('[class*="listing"]'))
        .or(page.getByRole('grid'));
      await expect(listingEl.first()).toBeVisible({ timeout: 15_000 });
    });
  }
});

// ---------------------------------------------------------------------------
// [TEC-3079] Console errors = 0 / Network 4xx/5xx = 0
// ---------------------------------------------------------------------------

test.describe('Zero console errors & zero 4xx/5xx — các route chính', () => {
  const ROUTES = ['/', '/listings', '/login'] as const;

  for (const route of ROUTES) {
    test(`${route} — không có console error và không có API 5xx`, async ({ page }) => {
      const { consoleErrors, networkErrors } = attachNetworkMonitor(page);

      await page.goto(route);
      await page.waitForLoadState('networkidle', { timeout: 20_000 }).catch(() => {});

      expect(
        consoleErrors,
        `Console errors tại ${route}: ${consoleErrors.join(' | ')}`,
      ).toHaveLength(0);

      const fiveXX = networkErrors.filter((e) => e.status >= 500);
      expect(
        fiveXX,
        `Network 5xx tại ${route}: ${fiveXX.map((e) => `${e.status} ${e.url}`).join(' | ')}`,
      ).toHaveLength(0);
    });
  }

  test('/listings — không có API 4xx (unauthorized ngoại lệ cho /admin)', async ({ page }) => {
    const { networkErrors } = attachNetworkMonitor(page);

    await page.goto('/listings');
    await page.waitForLoadState('networkidle', { timeout: 20_000 }).catch(() => {});

    const unexpectedFourXX = networkErrors.filter(
      (e) =>
        e.status >= 400 &&
        e.status < 500 &&
        // Bỏ qua 401 cho các endpoint yêu cầu auth (bình thường khi chưa đăng nhập)
        e.status !== 401 &&
        // Bỏ qua preflight OPTIONS
        !e.url.includes('OPTIONS'),
    );

    expect(
      unexpectedFourXX,
      `Unexpected 4xx tại /listings: ${unexpectedFourXX.map((e) => `${e.status} ${e.url}`).join(' | ')}`,
    ).toHaveLength(0);
  });
});

// ---------------------------------------------------------------------------
// [TEC-3080] Interaction: sort/filter bảng, DensityToggle, preview panel
// ---------------------------------------------------------------------------

test.describe('Interaction — Listings board (sort, filter, density, preview)', () => {
  test('sort cột giá hoạt động (click header)', async ({ page }) => {
    await page.goto('/listings');
    await page.waitForLoadState('networkidle', { timeout: 20_000 }).catch(() => {});

    // Tìm header cột giá trong DataTable
    const priceHeader = page
      .getByRole('columnheader', { name: /giá|price/i })
      .or(page.locator('th').filter({ hasText: /giá|price/i }));

    if (await priceHeader.first().isVisible({ timeout: 5_000 }).catch(() => false)) {
      await priceHeader.first().click();
      // Sau click không crash
      await expect(
        page.getByRole('heading', { name: /500|server error/i }),
      ).not.toBeVisible();
      // Table vẫn hiện
      await expect(
        page.locator('table').or(page.getByRole('grid')).first(),
      ).toBeVisible();
    }
  });

  test('DensityToggle compact/comfortable chuyển đổi không crash', async ({ page }) => {
    await page.goto('/listings');
    await page.waitForLoadState('networkidle', { timeout: 20_000 }).catch(() => {});

    const toggle = page
      .locator('[data-testid="density-toggle"]')
      .or(page.getByRole('button', { name: /compact|comfortable|density/i }))
      .or(page.locator('button[class*="density"]'));

    if (await toggle.first().isVisible({ timeout: 5_000 }).catch(() => false)) {
      // Click 2 lần (compact → comfortable → compact)
      await toggle.first().click();
      await page.waitForTimeout(300);
      await toggle.first().click();
      await page.waitForTimeout(300);

      await expect(
        page.getByRole('heading', { name: /500|server error/i }),
      ).not.toBeVisible();
    }
  });

  test('click row điều hướng đến listing detail (hoặc mở PreviewPanel)', async ({ page }) => {
    await page.goto('/listings');
    await page.waitForLoadState('networkidle', { timeout: 20_000 }).catch(() => {});

    // Tìm row đầu tiên trong table
    const firstRow = page
      .locator('tbody tr')
      .or(page.locator('[role="row"]').nth(1)) // nth(0) là header
      .first();

    if (await firstRow.isVisible({ timeout: 10_000 }).catch(() => false)) {
      // Design hiện tại: click row → router.push('/listings/{id}') → navigate sang detail page
      // Có thể trong tương lai sẽ mở PreviewPanel thay thế
      await Promise.race([
        firstRow.click({ timeout: 5_000 }),
        page.waitForURL(/\/listings\/[\w-]+/, { timeout: 10_000 }).catch(() => {}),
      ]).catch(() => {});

      // Sau khi click: hoặc đang ở detail page, hoặc panel mở, hoặc vẫn ở /listings
      // Không quan trọng outcome — chỉ cần không crash 500
      await page.waitForLoadState('domcontentloaded', { timeout: 10_000 }).catch(() => {});

      // Kiểm tra side panel nếu có (design tương lai)
      const panel = page
        .locator('[data-testid="listing-preview-panel"]')
        .or(page.locator('[class*="preview-panel"]'))
        .or(page.locator('[class*="PreviewPanel"]'))
        .or(page.locator('[role="dialog"]'));

      const panelVisible = await panel.first().isVisible({ timeout: 2_000 }).catch(() => false);
      if (panelVisible) {
        await expect(panel.first()).toBeVisible();
      }

      // Không crash dù navigate hay panel
      await expect(
        page.getByRole('heading', { name: /500|server error/i }),
      ).not.toBeVisible();
    }
  });
});

// ---------------------------------------------------------------------------
// [TEC-3080] Interaction — Home dashboard ticker
// ---------------------------------------------------------------------------

test.describe('Interaction — Home dashboard ticker & market data', () => {
  test('ticker hoặc market data section render đúng', async ({ page }) => {
    const { consoleErrors } = attachNetworkMonitor(page);

    await page.goto('/');
    // Đợi ticker animation hoặc data load
    await page.waitForLoadState('networkidle', { timeout: 20_000 }).catch(() => {});
    await page.waitForTimeout(1_000); // cho animation chạy 1 giây

    // Ticker hoặc market section phải visible
    const ticker = page
      .locator('[data-testid="ticker"]')
      .or(page.locator('[class*="ticker"]'))
      .or(page.locator('[class*="Ticker"]'))
      .or(page.getByText(/giá tb|avg|m²|tỷ/i));

    if (await ticker.first().isVisible({ timeout: 8_000 }).catch(() => false)) {
      await expect(ticker.first()).toBeVisible();
    }

    // Không có error boundary dù ticker chưa render
    await expect(
      page.getByRole('heading', { name: /500|server error/i }),
    ).not.toBeVisible();

    expect(
      consoleErrors,
      `Console errors tại /: ${consoleErrors.join(' | ')}`,
    ).toHaveLength(0);
  });

  test('navigation chính hoạt động từ home', async ({ page }) => {
    await page.goto('/');
    await page.waitForLoadState('networkidle', { timeout: 20_000 }).catch(() => {});

    // Click vào link Listings từ nav
    const listingsLink = page
      .getByRole('link', { name: /tin đăng|listings|bất động sản/i })
      .first();

    if (await listingsLink.isVisible({ timeout: 5_000 }).catch(() => false)) {
      await listingsLink.click();
      await page.waitForLoadState('domcontentloaded', { timeout: 10_000 });
      await expect(page).toHaveURL(/\/listings/);
    }
  });
});

// ---------------------------------------------------------------------------
// [TEC-3080] Interaction — Listing detail inquiry modal
// ---------------------------------------------------------------------------

test.describe('Interaction — Listing detail (inquiry modal, breadcrumb)', () => {
  test('breadcrumb và back navigation hoạt động', async ({ page }) => {
    // Đi từ /listings → listing detail → back
    await page.goto('/listings');
    await page.waitForLoadState('networkidle', { timeout: 20_000 }).catch(() => {});

    // Click vào listing đầu tiên
    const listingLink = page
      .locator('a[href*="/listings/"]')
      .filter({ hasNot: page.locator('a[href="/listings"]') })
      .first();

    if (await listingLink.isVisible({ timeout: 10_000 }).catch(() => false)) {
      await listingLink.click();
      await page.waitForLoadState('networkidle', { timeout: 20_000 }).catch(() => {});

      // Breadcrumb hoặc back button hiển thị
      const breadcrumb = page
        .locator('[aria-label="breadcrumb"]')
        .or(page.locator('nav[class*="breadcrumb"]'))
        .or(page.getByRole('link', { name: /trang chủ|home|quay lại/i }));

      if (await breadcrumb.first().isVisible({ timeout: 3_000 }).catch(() => false)) {
        await expect(breadcrumb.first()).toBeVisible();
      }

      // Không có 500
      await expect(
        page.getByRole('heading', { name: /500|server error/i }),
      ).not.toBeVisible();
    }
  });
});
