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

type HarnessWindow = Window & {
  __mobileMapHarness?: {
    ready: boolean;
    getPopupRect: () => {
      left: number;
      top: number;
      right: number;
      bottom: number;
      width: number;
      height: number;
      viewportWidth: number;
      viewportHeight: number;
    } | null;
    getFirstHotspotRect: () => {
      width: number;
      height: number;
    } | null;
  };
  __mobileMapIntegrationHarness?: {
    ready: boolean;
    pendingDeferredCallbacks: number;
    flushDeferredCallbacks: () => void;
    destroyMap: () => void;
    getDynamicLayerChildCount: () => number;
    getInitialDynamicRendered: () => boolean;
    getWrapperTransform: () => string;
    getPopupRect: () => {
      left: number;
      top: number;
      right: number;
      bottom: number;
      width: number;
      height: number;
      viewportWidth: number;
      viewportHeight: number;
    } | null;
  };
};

const MOBILE_DEVICE_MATRIX = [
  { label: 'iPhone SE', use: devices['iPhone SE'] },
  { label: 'iPhone 14 Pro Max', use: devices['iPhone 14 Pro Max'] },
  { label: 'Pixel 5', use: devices['Pixel 5'] },
  { label: 'Galaxy S9+', use: devices['Galaxy S9+'] },
] as const;

for (const deviceConfig of MOBILE_DEVICE_MATRIX) {
  test.describe(`Mobile SVG popup QA (${deviceConfig.label})`, () => {
    const { defaultBrowserType: _defaultBrowserType, ...contextOptions } = deviceConfig.use;
    test.use(contextOptions);

    test('keeps popup in viewport and supports dismissal patterns', async ({ page }) => {
      const pageErrors: string[] = [];
      page.on('pageerror', (error) => pageErrors.push(error.message));

      await page.goto('/tests/mobile-map-harness.html');

      await expect
        .poll(async () => {
          return await page.evaluate(() => {
            const w = window as HarnessWindow;
            return Boolean(w.__mobileMapHarness?.ready);
          });
        }, { timeout: 20000 })
        .toBe(true);

      const hotspotRect = await page.evaluate(() => {
        const w = window as HarnessWindow;
        return w.__mobileMapHarness?.getFirstHotspotRect() ?? null;
      });

      expect(hotspotRect).not.toBeNull();
      expect(hotspotRect?.width ?? 0).toBeGreaterThanOrEqual(44);
      expect(hotspotRect?.height ?? 0).toBeGreaterThanOrEqual(44);

      const hotspot = page.locator('.hotspot').first();
      await expect(hotspot).toBeVisible();
      await hotspot.tap();

      const popup = page.locator('.map-popup.map-popup-sheet');
      await expect(popup).toBeVisible();

      await expect
        .poll(async () => {
          return await page.evaluate(() => {
            const w = window as HarnessWindow;
            const rect = w.__mobileMapHarness?.getPopupRect();
            if (!rect) return false;
            return (
              rect.left >= 0 &&
              rect.top >= 0 &&
              rect.right <= rect.viewportWidth + 1 &&
              rect.bottom <= rect.viewportHeight + 1
            );
          });
        }, { timeout: 5000 })
        .toBe(true);

      const popupRect = await page.evaluate(() => {
        const w = window as HarnessWindow;
        return w.__mobileMapHarness?.getPopupRect() ?? null;
      });

      expect(popupRect).not.toBeNull();
      expect(popupRect?.left ?? -1).toBeGreaterThanOrEqual(0);
      expect(popupRect?.top ?? -1).toBeGreaterThanOrEqual(0);
      expect((popupRect?.right ?? 0) - (popupRect?.viewportWidth ?? 0)).toBeLessThanOrEqual(1);
      expect((popupRect?.bottom ?? 0) - (popupRect?.viewportHeight ?? 0)).toBeLessThanOrEqual(1);

      const dragPopupBy = async (distance: number): Promise<void> => {
        await page.evaluate((dragDistance) => {
          const popupEl = document.querySelector('.map-popup.map-popup-sheet') as HTMLElement | null;
          const handle = document.querySelector('.map-popup-sheet-handle') as HTMLElement | null;
          if (!popupEl || !handle || typeof Touch === 'undefined') return;

          const rect = handle.getBoundingClientRect();
          const x = rect.left + rect.width / 2;
          const startY = rect.top + rect.height / 2;
          const endY = startY + dragDistance;
          const target = handle;

          const makeTouch = (y: number): Touch =>
            new Touch({
              identifier: 42,
              target,
              clientX: x,
              clientY: y,
              pageX: x,
              pageY: y,
              screenX: x,
              screenY: y,
              radiusX: 2,
              radiusY: 2,
              rotationAngle: 0,
              force: 0.5,
            });

          const startTouch = makeTouch(startY);
          target.dispatchEvent(
            new TouchEvent('touchstart', {
              bubbles: true,
              cancelable: true,
              touches: [startTouch],
              targetTouches: [startTouch],
              changedTouches: [startTouch],
            })
          );

          const moveTouch = makeTouch(endY);
          target.dispatchEvent(
            new TouchEvent('touchmove', {
              bubbles: true,
              cancelable: true,
              touches: [moveTouch],
              targetTouches: [moveTouch],
              changedTouches: [moveTouch],
            })
          );

          target.dispatchEvent(
            new TouchEvent('touchend', {
              bubbles: true,
              cancelable: true,
              touches: [],
              targetTouches: [],
              changedTouches: [moveTouch],
            })
          );
        }, distance);
      };

      await dragPopupBy(48);
      await expect(page.locator('.map-popup.map-popup-sheet')).toBeVisible();
      await expect
        .poll(async () => {
          return await page.evaluate(() => {
            const popupEl = document.querySelector('.map-popup.map-popup-sheet') as HTMLElement | null;
            return popupEl?.style.transform ?? null;
          });
        }, { timeout: 2000 })
        .toBe('');

      await dragPopupBy(150);
      await expect(page.locator('.map-popup')).toHaveCount(0);

      await hotspot.tap();
      await expect(page.locator('.map-popup.map-popup-sheet')).toBeVisible();
      await page.locator('.popup-close').first().tap();
      await expect(page.locator('.map-popup')).toHaveCount(0);

      await hotspot.tap();
      await expect(page.locator('.map-popup.map-popup-sheet')).toBeVisible();

      await page.touchscreen.tap(6, 6);
      await expect(page.locator('.map-popup')).toHaveCount(0);

      expect(pageErrors).toEqual([]);
    });
  });
}

test.describe('Mobile SVG popup integration path', () => {
  const { defaultBrowserType: _defaultBrowserType, ...iphoneSE } = devices['iPhone SE'];
  test.use(iphoneSE);

  test('opens popup through MapComponent hotspot marker tap', async ({ page }) => {
    const pageErrors: string[] = [];
    page.on('pageerror', (error) => pageErrors.push(error.message));

    await page.goto('/tests/mobile-map-integration-harness.html');

    await expect
      .poll(async () => {
        return await page.evaluate(() => {
          const w = window as HarnessWindow;
          return Boolean(w.__mobileMapIntegrationHarness?.ready);
        });
      }, { timeout: 30000 })
      .toBe(true);

    const timeSlider = page.locator('.time-slider');
    const mapControls = page.locator('.map-controls');
    await expect(timeSlider).toBeVisible();
    await expect(mapControls).toBeVisible();
    const controlsDoNotOverlap = await page.evaluate(() => {
      const slider = document.querySelector('.time-slider') as HTMLElement | null;
      const controls = document.querySelector('.map-controls') as HTMLElement | null;
      if (!slider || !controls) return false;
      const sliderRect = slider.getBoundingClientRect();
      const controlsRect = controls.getBoundingClientRect();
      return sliderRect.right <= controlsRect.left + 1;
    });
    expect(controlsDoNotOverlap).toBe(true);

    const hotspot = page.locator('.hotspot').first();
    await expect(hotspot).toBeVisible();
    await hotspot.tap();

    const popup = page.locator('.map-popup.map-popup-sheet');
    await expect(popup).toBeVisible();

    await expect
      .poll(async () => {
        return await page.evaluate(() => {
          const w = window as HarnessWindow;
          const rect = w.__mobileMapIntegrationHarness?.getPopupRect();
          if (!rect) return false;
          return (
            rect.left >= 0 &&
            rect.top >= 0 &&
            rect.right <= rect.viewportWidth + 1 &&
            rect.bottom <= rect.viewportHeight + 1
          );
        });
      }, { timeout: 5000 })
      .toBe(true);

    const popupRect = await page.evaluate(() => {
      const w = window as HarnessWindow;
      return w.__mobileMapIntegrationHarness?.getPopupRect() ?? null;
    });

    expect(popupRect).not.toBeNull();
    expect(popupRect?.left ?? -1).toBeGreaterThanOrEqual(0);
    expect(popupRect?.top ?? -1).toBeGreaterThanOrEqual(0);
    expect((popupRect?.right ?? 0) - (popupRect?.viewportWidth ?? 0)).toBeLessThanOrEqual(1);
    expect((popupRect?.bottom ?? 0) - (popupRect?.viewportHeight ?? 0)).toBeLessThanOrEqual(1);

    await popup.locator('.popup-close').first().tap();
    await expect(page.locator('.map-popup')).toHaveCount(0);

    expect(pageErrors).toEqual([]);
  });

  test('defers overlays while preserving transform and guarding destroy', async ({ page }) => {
    const pageErrors: string[] = [];
    page.on('pageerror', (error) => pageErrors.push(error.message));

    await page.goto('/tests/mobile-map-integration-harness.html?defer-control=1');

    await expect
      .poll(async () => {
        return await page.evaluate(() => document.querySelectorAll('.country').length);
      }, { timeout: 30000 })
      .toBeGreaterThan(0);

    await expect
      .poll(async () => {
        return await page.evaluate(() => {
          const w = window as HarnessWindow;
          return w.__mobileMapIntegrationHarness?.pendingDeferredCallbacks ?? 0;
        });
      }, { timeout: 10000 })
      .toBeGreaterThan(0);

    const transformBeforeFlush = await page.evaluate(() => {
      const w = window as HarnessWindow;
      return w.__mobileMapIntegrationHarness?.getWrapperTransform() ?? '';
    });
    expect(transformBeforeFlush).toContain('scale(2.7)');

    const dynamicBeforeFlush = await page.evaluate(() => {
      const w = window as HarnessWindow;
      return w.__mobileMapIntegrationHarness?.getDynamicLayerChildCount() ?? -1;
    });
    expect(dynamicBeforeFlush).toBe(0);

    await page.evaluate(() => {
      const w = window as HarnessWindow;
      w.__mobileMapIntegrationHarness?.destroyMap();
      w.__mobileMapIntegrationHarness?.flushDeferredCallbacks();
    });

    const initialDynamicRendered = await page.evaluate(() => {
      const w = window as HarnessWindow;
      return w.__mobileMapIntegrationHarness?.getInitialDynamicRendered() ?? true;
    });
    expect(initialDynamicRendered).toBe(false);
    expect(pageErrors).toEqual([]);
  });
});
