import { expect, test, type Page } from '@playwright/test';
import { createWalletContext } from '../setup/wallet-provider';
import { getStackConfig, validateStackHealthy } from '../setup/stack';
import { navigateSPA } from '../setup/navigate';

const ACCOUNT_PRIVATE_KEY = '0xac0974bec39a17e36ba4a6b4d238ff944bacb478cbed5efcae784d7bf4f2ff80';

const STACK_CONFIG = getStackConfig();
const STACK_WEBAPP_URL = STACK_CONFIG.webAppUrl;
const STACK_RPC_URL = STACK_CONFIG.rpcUrl;

// Expected Uniswap deep link components for Base mainnet
const BASE_MAINNET_TOKEN = '0x45caa5929f6ee038039984205bdecf968b954820';

/**
 * Inject a mock window.umami tracker that records events into a global array.
 * Must be called via context.addInitScript so it runs before app code.
 */
function analyticsCollectorScript(): string {
  return `
    window.__analytics_events = [];
    window.umami = {
      track: function(name, data) {
        window.__analytics_events.push({ name: name, data: data || {} });
      }
    };
  `;
}

/** Retrieve collected analytics events from the page. */
async function getAnalyticsEvents(page: Page): Promise<Array<{ name: string; data: Record<string, unknown> }>> {
  return page.evaluate(() => (window as unknown as { __analytics_events: Array<{ name: string; data: Record<string, unknown> }> }).__analytics_events ?? []);
}

test.describe('Conversion Funnel: Landing → Swap → Stake', () => {
  // Cap per-test timeout to 3 minutes — funnel tests are navigation-only, no transactions.
  test.describe.configure({ timeout: 3 * 60 * 1000 });

  test.beforeAll(async () => {
    await validateStackHealthy(STACK_CONFIG);
  });

  test('desktop: full funnel navigation and deep link verification', async ({ browser }) => {
    // Desktop viewport (matches playwright.config default)
    const context = await browser.newContext({
      viewport: { width: 1280, height: 720 },
      screen: { width: 1280, height: 720 },
    });
    // Inject analytics collector before any page loads
    await context.addInitScript({ content: analyticsCollectorScript() });

    const page = await context.newPage();
    page.on('console', msg => console.log(`[BROWSER] ${msg.type()}: ${msg.text()}`));

    try {
      // ── Step 1: Landing page loads ──
      console.log('[FUNNEL] Loading landing page...');
      await page.goto(`${STACK_WEBAPP_URL}/`, { waitUntil: 'domcontentloaded' });

      // Verify landing page rendered — look for the hero CTA
      const heroCta = page.locator('.header-cta button, .header-cta .k-button').first();
      await expect(heroCta).toBeVisible({ timeout: 30_000 });
      console.log('[FUNNEL] Landing page loaded, hero CTA visible');

      await page.screenshot({ path: 'test-results/funnel-01-landing-desktop.png' });

      // ── Step 2: Click CTA → navigates to /app/get-krk ──
      console.log('[FUNNEL] Clicking hero "Get $KRK" CTA...');
      // Use Promise.all to avoid race: the click triggers window.location.href
      // which starts a full-page navigation through Caddy to the webapp.
      await Promise.all([
        page.waitForURL('**/app/get-krk**', { timeout: 30_000 }),
        heroCta.click(),
      ]);
      console.log('[FUNNEL] Navigated to web-app get-krk page');

      // Verify the Get KRK page rendered
      const getKrkHeading = page.getByRole('heading', { name: 'Get $KRK Tokens' });
      await expect(getKrkHeading).toBeVisible({ timeout: 15_000 });

      await page.screenshot({ path: 'test-results/funnel-02-get-krk-desktop.png' });

      // ── Step 3: Verify swap page content ──
      console.log('[FUNNEL] Verifying swap page...');

      // In local dev (VITE_ENABLE_LOCAL_SWAP=true), the LocalSwapWidget is shown.
      // Without a wallet, it renders a "Connect your wallet" message instead of
      // the swap input. Check for the widget container class to detect local mode.
      const localSwapWidget = page.locator('.local-swap-widget');
      const hasLocalSwap = await localSwapWidget.isVisible({ timeout: 5_000 }).catch(() => false);

      if (hasLocalSwap) {
        console.log('[FUNNEL] Local swap widget detected (dev environment)');
        // Without wallet, the widget shows a connect prompt
        const swapWarning = page.locator('.swap-warning');
        const hasWarning = await swapWarning.isVisible({ timeout: 3_000 }).catch(() => false);
        if (hasWarning) {
          console.log('[FUNNEL] Wallet not connected — swap widget shows connect prompt');
        } else {
          // With wallet, the swap input is shown
          await expect(page.locator('[data-testid="swap-amount-input"]')).toBeVisible({ timeout: 5_000 });
          console.log('[FUNNEL] Swap input visible');
        }
      } else {
        // Production path: verify the Uniswap deep link
        const swapLink = page.locator('a.swap-button');
        await expect(swapLink).toBeVisible({ timeout: 10_000 });
        const href = await swapLink.getAttribute('href');
        expect(href).toBeTruthy();

        console.log(`[FUNNEL] Uniswap link: ${href}`);

        // Verify link points to Uniswap with correct structure
        expect(href).toContain('app.uniswap.org/swap');
        expect(href).toContain('outputCurrency=');

        // Verify the link opens in a new tab (doesn't navigate away)
        const target = await swapLink.getAttribute('target');
        expect(target).toBe('_blank');
        const rel = await swapLink.getAttribute('rel');
        expect(rel).toContain('noopener');

        // For Base mainnet deployments, verify exact token address
        if (href!.includes('chain=base&')) {
          expect(href).toContain(`outputCurrency=${BASE_MAINNET_TOKEN}`);
          console.log('[FUNNEL] ✅ Base mainnet token address verified');
        }

        // Verify network info is displayed
        const networkLabel = page.locator('.info-value').first();
        await expect(networkLabel).toBeVisible();
      }

      // ── Step 4: Navigate to stake page ──
      console.log('[FUNNEL] Navigating to stake page...');
      await navigateSPA(page, '/app/stake');
      // Stake page may redirect to /app/login if no wallet connected.
      // Wait for either page's root element to confirm the route has mounted.
      await page.waitForSelector('.stake-view, .login-wrapper', { timeout: 15_000 });

      // Either outcome confirms the route is functional.
      const currentUrl = page.url();
      const isStakePage = currentUrl.includes('/app/stake');
      const isLoginPage = currentUrl.includes('/app/login');
      expect(isStakePage || isLoginPage).toBeTruthy();
      console.log(`[FUNNEL] Stake route resolved to: ${currentUrl}`);

      await page.screenshot({ path: 'test-results/funnel-03-stake-desktop.png' });

      console.log('[FUNNEL] ✅ Desktop funnel navigation complete');
    } finally {
      await context.close();
    }
  });

  test('mobile: full funnel navigation and deep link verification', async ({ browser }) => {
    // iPhone-like viewport
    const context = await browser.newContext({
      viewport: { width: 375, height: 812 },
      screen: { width: 375, height: 812 },
      isMobile: true,
    });
    await context.addInitScript({ content: analyticsCollectorScript() });

    const page = await context.newPage();
    page.on('console', msg => console.log(`[BROWSER] ${msg.type()}: ${msg.text()}`));

    try {
      // ── Step 1: Landing page loads on mobile ──
      console.log('[FUNNEL-MOBILE] Loading landing page...');
      await page.goto(`${STACK_WEBAPP_URL}/`, { waitUntil: 'domcontentloaded' });

      // Verify mobile header image is shown
      const mobileHeader = page.locator('.header-section img').first();
      await expect(mobileHeader).toBeVisible({ timeout: 30_000 });
      console.log('[FUNNEL-MOBILE] Landing page loaded');

      await page.screenshot({ path: 'test-results/funnel-01-landing-mobile.png' });

      // ── Step 2: Verify CTA exists, then navigate to /app/get-krk ──
      const heroCta = page.locator('.header-cta button').first();
      await expect(heroCta).toBeVisible({ timeout: 15_000 });
      console.log('[FUNNEL-MOBILE] Hero CTA visible on mobile viewport');

      // Navigate directly to the get-krk page. On mobile with isMobile:true,
      // Playwright tap events don't reliably trigger Vue @click handlers that
      // set window.location.href. The desktop test already verifies the CTA
      // click→navigation flow; here we verify mobile layout and page rendering.
      await page.goto(`${STACK_WEBAPP_URL}/app/get-krk`, { waitUntil: 'domcontentloaded' });
      console.log('[FUNNEL-MOBILE] Navigated to get-krk page');

      const getKrkHeading = page.getByRole('heading', { name: 'Get $KRK Tokens' });
      await expect(getKrkHeading).toBeVisible({ timeout: 15_000 });

      await page.screenshot({ path: 'test-results/funnel-02-get-krk-mobile.png' });

      // ── Step 3: Verify swap page on mobile ──
      const localSwapWidget = page.locator('.local-swap-widget');
      const hasLocalSwap = await localSwapWidget.isVisible({ timeout: 5_000 }).catch(() => false);

      if (hasLocalSwap) {
        console.log('[FUNNEL-MOBILE] Local swap widget detected');
      } else {
        const swapLink = page.locator('a.swap-button');
        await expect(swapLink).toBeVisible({ timeout: 10_000 });
        const href = await swapLink.getAttribute('href');
        expect(href).toContain('app.uniswap.org/swap');
        console.log('[FUNNEL-MOBILE] Uniswap deep link verified on mobile');
      }

      // ── Step 4: Navigate to stake ──
      await navigateSPA(page, '/app/stake');
      // Wait for either page's root element — stake or login redirect.
      await page.waitForSelector('.stake-view, .login-wrapper', { timeout: 15_000 });

      const currentUrl = page.url();
      expect(currentUrl.includes('/app/stake') || currentUrl.includes('/app/login')).toBeTruthy();

      await page.screenshot({ path: 'test-results/funnel-03-stake-mobile.png' });

      console.log('[FUNNEL-MOBILE] ✅ Mobile funnel navigation complete');
    } finally {
      await context.close();
    }
  });

  test('analytics infrastructure: collector available in landing and web-app contexts', async ({ browser }) => {
    // Verifies that the addInitScript mock collector is active in both apps and that
    // the umami tracker object is present. Note: this test confirms the collector
    // infrastructure (addInitScript wiring, umami availability) — not that the app
    // source code calls trackCtaClick on a real CTA click, since full-page navigation
    // unloads the page context before events can be read.
    const context = await createWalletContext(browser, {
      privateKey: ACCOUNT_PRIVATE_KEY,
      rpcUrl: STACK_RPC_URL,
    });
    await context.addInitScript({ content: analyticsCollectorScript() });

    const page = await context.newPage();
    page.on('console', msg => console.log(`[BROWSER] ${msg.type()}: ${msg.text()}`));

    try {
      // ── Landing page: verify addInitScript collector is active ──
      console.log('[ANALYTICS] Loading landing page...');
      await page.goto(`${STACK_WEBAPP_URL}/`, { waitUntil: 'domcontentloaded' });

      const heroCta = page.locator('.header-cta button, .header-cta .k-button').first();
      await expect(heroCta).toBeVisible({ timeout: 30_000 });

      // Confirm the collector mock is live by recording a synthetic event and reading it back.
      await page.evaluate(() => {
        (window as unknown as { umami: { track: (n: string, d: Record<string, unknown>) => void } })
          .umami.track('cta_click', { label: 'hero_get_krk' });
      });

      const landingEvents = await getAnalyticsEvents(page);
      console.log(`[ANALYTICS] Landing page events captured: ${JSON.stringify(landingEvents)}`);
      const ctaEvent = landingEvents.find(e => e.name === 'cta_click');
      expect(ctaEvent).toBeTruthy();
      expect(ctaEvent!.data.label).toBe('hero_get_krk');
      console.log('[ANALYTICS] ✅ Collector active on landing page');

      // ── Web-app: verify umami tracker is available ──
      await page.goto(`${STACK_WEBAPP_URL}/app/get-krk`, { waitUntil: 'domcontentloaded' });
      await expect(page.getByRole('heading', { name: 'Get $KRK Tokens' })).toBeVisible({ timeout: 15_000 });

      const hasUmami = await page.evaluate(() => typeof (window as unknown as { umami?: unknown }).umami !== 'undefined');
      expect(hasUmami).toBeTruthy();
      console.log('[ANALYTICS] ✅ umami tracker available in web-app context');

      console.log('[ANALYTICS] ✅ Analytics infrastructure verified');
    } finally {
      await context.close();
    }
  });

  test('Uniswap deep link structure for all configured chains', async ({ browser }) => {
    // Verify deep link construction logic matches expected patterns.
    // This test loads the get-krk page and checks the link format.
    const context = await createWalletContext(browser, {
      privateKey: ACCOUNT_PRIVATE_KEY,
      rpcUrl: STACK_RPC_URL,
    });

    const page = await context.newPage();

    try {
      console.log('[DEEPLINK] Loading get-krk page...');
      await page.goto(`${STACK_WEBAPP_URL}/app/get-krk`, { waitUntil: 'domcontentloaded' });
      await expect(page.getByRole('heading', { name: 'Get $KRK Tokens' })).toBeVisible({ timeout: 15_000 });

      // Check if we're in local swap mode or production link mode.
      // The LocalSwapWidget container is always rendered when enableLocalSwap=true,
      // but the swap input only appears when a wallet is connected.
      const localSwapWidget = page.locator('.local-swap-widget');
      const hasLocalSwap = await localSwapWidget.isVisible({ timeout: 5_000 }).catch(() => false);

      if (hasLocalSwap) {
        console.log('[DEEPLINK] Local swap mode — connecting wallet first');
        // The wallet must be explicitly connected for the swap input to render.
        const navbarTitle = page.locator('.navbar-title').first();
        await navbarTitle.waitFor({ state: 'visible', timeout: 30_000 });
        await page.evaluate(() => window.dispatchEvent(new Event('resize')));

        // Wait for wagmi to settle — either connected (auto-reconnect) or disconnected
        const anyConnectBtn = page.locator('.connect-button--disconnected, .connect-button--connected').first();
        await anyConnectBtn.waitFor({ state: 'visible', timeout: 15_000 });

        if (await page.locator('.connect-button--connected').first().isVisible()) {
          console.log('[DEEPLINK] Wallet already connected (auto-reconnect)');
        } else {
          console.log('[DEEPLINK] Found desktop Connect button, clicking...');
          await page.locator('.connect-button--disconnected').first().click();
          const connector = page.locator('.connectors-element').first();
          await connector.waitFor({ state: 'visible', timeout: 10_000 });
          console.log('[DEEPLINK] Clicking wallet connector...');
          await connector.click();
          await page.getByText(/0x[a-fA-F0-9]{4}/i).first().waitFor({ state: 'visible', timeout: 15_000 });
          console.log('[DEEPLINK] Wallet connected');
        }
        console.log('[DEEPLINK] Verifying swap widget is functional');
        // With wallet connected, the swap input and buy button should be visible
        await expect(page.locator('[data-testid="swap-amount-input"]')).toBeVisible({ timeout: 5_000 });
        await expect(page.getByTestId('swap-buy-button')).toBeVisible();
        console.log('[DEEPLINK] ✅ Local swap widget verified');
      } else {
        // Production mode: verify the Uniswap link
        const swapLink = page.locator('a.swap-button');
        await expect(swapLink).toBeVisible({ timeout: 10_000 });
        const href = await swapLink.getAttribute('href');
        expect(href).toBeTruthy();

        // Parse and validate the URL structure
        const url = new URL(href!);
        expect(url.hostname).toBe('app.uniswap.org');
        expect(url.pathname).toBe('/swap');
        expect(url.searchParams.has('chain')).toBeTruthy();
        expect(url.searchParams.has('outputCurrency')).toBeTruthy();

        const chain = url.searchParams.get('chain');
        const outputCurrency = url.searchParams.get('outputCurrency');

        // Chain must be a valid Uniswap chain identifier
        expect(['base', 'base_sepolia', 'mainnet']).toContain(chain);
        // Token address must be a valid Ethereum address
        expect(outputCurrency).toMatch(/^0x[a-fA-F0-9]{40}$/);

        console.log(`[DEEPLINK] ✅ Uniswap deep link valid: chain=${chain}, token=${outputCurrency}`);
      }

      // Verify network info display
      const networkInfo = page.locator('.swap-info .info-value').first();
      if (await networkInfo.isVisible({ timeout: 3_000 }).catch(() => false)) {
        const networkText = await networkInfo.textContent();
        console.log(`[DEEPLINK] Network displayed: ${networkText}`);
        expect(networkText).toBeTruthy();
      }
    } finally {
      await context.close();
    }
  });
});
