import puppeteer, { HTTPResponse } from 'puppeteer';
import axios from 'axios';
import * as cheerio from 'cheerio';

const targetUrl = process.argv[2] || process.env.NEXT_PUBLIC_BASE_URL || 'http://localhost:3000';
const gatekeeperPassword = process.env.GATEKEEPER_PASSWORD || 'klz2026';

// Utility for hardcoded delays
const delay = (ms: number) => new Promise((resolve) => setTimeout(resolve, ms));

async function main() {
  console.log(`\n🚀 Starting E2E Form Submission Check for: ${targetUrl}`);

  // 0. Pre-warming settlement: Wait for deployment to respond (sync containers/Varnish)
  console.log(`⏳ Pre-warming: Waiting for deployment to return 200 OK...`);
  const maxWaitMs = 60000;
  const startTime = Date.now();
  let warmed = false;

  while (Date.now() - startTime < maxWaitMs) {
    try {
      const resp = await axios.get(targetUrl, {
        headers: { Cookie: `klz_gatekeeper_session=${gatekeeperPassword}` },
        timeout: 5000,
        validateStatus: (s) => s === 200,
      });
      if (resp.status === 200) {
        warmed = true;
        break;
      }
    } catch (e) {
      // Quietly retry
    }
    await delay(2000);
  }

  if (!warmed) {
    console.warn(`⚠️ Pre-warming timed out after 60s. Proceeding anyway...`);
  } else {
    console.log(`✅ Deployment settled and responding.`);
  }

  // Brief stabilization buffer
  await delay(5000);

  // 1. Fetch Sitemap to discover the contact page and a product page
  const sitemapUrl = `${targetUrl.replace(/\/$/, '')}/sitemap.xml`;
  let urls: string[] = [];

  try {
    console.log(`📥 Fetching sitemap from ${sitemapUrl}...`);
    const response = await axios.get(sitemapUrl, {
      headers: { Cookie: `klz_gatekeeper_session=${gatekeeperPassword}` },
    });

    const $ = cheerio.load(response.data, { xmlMode: true });
    urls = $('url loc')
      .map((i, el) => $(el).text())
      .get();

    // Normalize to target URL instance
    const urlPattern = /https?:\/\/[^\/]+/;
    urls = [...new Set(urls)]
      .filter((u) => u.startsWith('http'))
      .map((u) => u.replace(urlPattern, targetUrl.replace(/\/$/, '')))
      .sort();
  } catch (err: any) {
    console.error(`❌ Failed to fetch sitemap: ${err.message}`);
    process.exit(1);
  }

  const contactUrl = urls.find((u) => u.includes('/de/kontakt'));
  // Ensure we select an actual product page (depth >= 7: http://host/de/produkte/category/product)
  const productUrl = urls.find(
    (u) =>
      u.includes('/de/produkte/') && new URL(u).pathname.split('/').filter(Boolean).length >= 4,
  );

  if (!contactUrl) {
    console.error(`❌ Could not find contact page in sitemap. Ensure /de/kontakt exists.`);
    process.exit(1);
  }

  if (!productUrl) {
    console.error(
      `❌ Could not find a product page in sitemap. Form testing requires at least one product page.`,
    );
    process.exit(1);
  }

  console.log(`✅ Discovered Contact Page: ${contactUrl}`);
  console.log(`✅ Discovered Product Page: ${productUrl}`);

  // 2. Launch Headless Browser
  console.log(`\n🕷️ Launching Puppeteer Headless Engine...`);
  const browser = await puppeteer.launch({
    headless: true,
    args: [
      '--no-sandbox',
      '--disable-setuid-sandbox',
      '--disable-dev-shm-usage',
      '--disable-gpu',
      '--ignore-certificate-errors',
      '--disable-web-security',
      '--disable-features=IsolateOrigins,site-per-process',
    ],
  });

  const page = await browser.newPage();

  // Enable Request Interception to force cache-busting on ALL static assets
  await page.setRequestInterception(true);
  page.on('request', (request) => {
    const url = request.url();
    // Intercept Next.js chunks and data to bypass Varnish 404-caching
    // Includes standard /_next/ and subpath /gatekeeper/_next/
    if (
      (url.includes('/_next/static/') || url.includes('/_next/data/') || url.includes('/gatekeeper/_next/')) && 
      !url.includes('?cb=') && 
      !url.includes('&cb=')
    ) {
      const buster = `cb=${Date.now()}`;
      const newUrl = url.includes('?') ? `${url}&${buster}` : `${url}?${buster}`;
      request.continue({ url: newUrl });
    } else {
      request.continue();
    }
  });

  let chunkErrorsDetected = false;
  page.on('console', (msg) => {
    const text = msg.text();
    console.log('💻 BROWSER CONSOLE:', text);
    if (text.includes('ChunkLoadError') || text.includes('failed to load chunk')) {
      chunkErrorsDetected = true;
    }
  });
  page.on('pageerror', (error) => {
    console.error('💻 BROWSER ERROR:', error.message);
    if (error.message.includes('ChunkLoadError')) {
      chunkErrorsDetected = true;
    }
  });
  page.on('requestfailed', (request) => {
    const url = request.url();
    const failure = request.failure()?.errorText;
    console.error('💻 BROWSER REQUEST FAILED:', url, failure);
    if (url.endsWith('.js') && (failure === 'net::ERR_ABORTED' || failure?.includes('404'))) {
      chunkErrorsDetected = true;
    }
  });

  // Helper for resilient navigation with cache-busting fallback
  const navigateWithRetry = async (url: string, label: string) => {
    chunkErrorsDetected = false;
    console.log(`\n🧪 Testing ${label} on: ${url}`);
    
    // First attempt: Wait for network to be relatively idle
    await page.goto(url, { waitUntil: 'networkidle2', timeout: 30000 });
    
    // REDIRECTION CHECK: Logging redirected landing page
    const finalUrl = page.url();
    if (finalUrl !== url && !finalUrl.includes(url)) {
      console.log(`   🔀 Redirected to: ${finalUrl}`);
    }

    if (chunkErrorsDetected) {
      const buster = `cb=${Date.now()}`;
      const cbUrl = finalUrl.includes('?') ? `${finalUrl}&${buster}` : `${finalUrl}?${buster}`;
      console.warn(`   ⚠️ Assets failed to load (Varnish staleness suspected). Retrying with cache-buster: ${cbUrl}`);
      chunkErrorsDetected = false;
      await page.goto(cbUrl, { waitUntil: 'networkidle2', timeout: 30000 });
    }

    // Capture HTML on failure during hydration wait
    try {
      await page.waitForNetworkIdle({ idleTime: 1000, timeout: 5000 }).catch(() => {});
    } catch (e) {
      console.warn('   ⚠️ Network idle timeout, proceeding with current state.');
    }
  };

  // 3. Authenticate through Gatekeeper login form
  try {
    // Navigate to a protected page so Gatekeeper redirects us to the login screen
    // RELAXED NAVIGATION: Use domcontentloaded to handle rapid redirects
    await page.goto(contactUrl, { waitUntil: 'domcontentloaded', timeout: 60000 });

    // BOUNCE BUFFER: Wait for potential Gatekeeper redirect chain to settle
    console.log(`   Waiting for potential Gatekeeper redirect bounce...`);
    await delay(3000);

    // Check if we landed on the Gatekeeper login page
    const isGatekeeperPage = await page.$('input[name="password"]');
    if (isGatekeeperPage) {
      console.log(`   Gatekeeper gate detected. Logging in...`);
      await page.type('input[name="password"]', gatekeeperPassword);
      await Promise.all([
        page.waitForNavigation({ waitUntil: 'domcontentloaded' }),
        page.click('button[type="submit"]'),
      ]);

      // LOGIN SYNC BUFFER: Let the authenticated session settle
      console.log(`   Authenticating session...`);
      await delay(3000);
      console.log(`✅ Gatekeeper authentication successful!`);
    } else {
      console.log(`✅ Already authenticated (no Gatekeeper gate detected).`);
    }
  } catch (err: any) {
    console.error(`❌ Gatekeeper authentication failed: ${err.message}`);
    await browser.close();
    process.exit(1);
  }

  let hasErrors = false;

  // 4. Test Contact Form
  try {
    await navigateWithRetry(contactUrl, 'Contact Form');

    // Ensure React has hydrated completely - CI runners can be slow
    console.log(`   ⏳ Waiting for hydration (5s)...`);
    await delay(5000);

    // Ensure form is visible and interactive
    try {
      // Find the form input by name
      await page.waitForSelector('input[name="name"]', { visible: true, timeout: 15000 });
    } catch (e) {
      console.error('❌ Failed to find Contact Form input. Page Title:', await page.title());
      const html = await page.content();
      console.log(`💻 FULL HTML DUMP (top 2000 chars):\n${html.slice(0, 2000)}`);
      throw e;
    }

    // Wait specifically for hydration logic to initialize the onSubmit handler
    await page.evaluate(() => new Promise((resolve) => setTimeout(resolve, 2000)));

    // Fill form fields
    await page.type('input[name="name"]', 'Automated E2E Test');
    await page.type('input[name="email"]', 'testing@mintel.me');
    await page.type(
      'textarea[name="message"]',
      'This is an automated test verifying the contact form submission.',
    );

    // Give state a moment to settle
    await page.evaluate(() => new Promise((resolve) => setTimeout(resolve, 500)));

    console.log(`   ⏳ Waiting for alert selector (30s)...`);
    try {
      // Explicitly click submit and wait for navigation/state-change
      await Promise.all([
        page.waitForSelector('[role="alert"]', { timeout: 30000 }),
        page.click('button[type="submit"]'),
      ]);
    } catch (e) {
      console.error(`❌ Timeout waiting for [role="alert"]. Dumping page content:`);
      const bodyHTML = await page.evaluate(() => document.body.innerHTML.slice(0, 1000));
      console.log(`💻 PAGE BODY (partial): ${bodyHTML}`);
      throw e;
    }

    const alertText = await page.$eval('[role="alert"]', (el) => el.textContent);
    console.log(`   🔔 Alert text: ${alertText}`);

    // Detection robust for both English and German versions
    const isError = 
      alertText?.toLowerCase().includes('failed') || 
      alertText?.toLowerCase().includes('wrong') ||
      alertText?.toLowerCase().includes('fehlgeschlagen') || 
      alertText?.toLowerCase().includes('schief gelaufen') ||
      alertText?.toLowerCase().includes('fehler');

    if (isError) {
      throw new Error(`Form submitted but showed error: ${alertText}`);
    }

    console.log(`✅ Contact Form submitted successfully! (Success state verified)`);
  } catch (err: any) {
    console.error(`❌ Contact Form Test Failed: ${err.message}`);
    hasErrors = true;
  }

  // 4. Test Product Quote Form
  try {
    await navigateWithRetry(productUrl, 'Product Quote Form');

    // Ensure React has hydrated completely
    console.log(`   ⏳ Waiting for hydration (5s)...`);
    await delay(5000);

    // The product form uses dynamic IDs, so we select by input type in the specific form context
    try {
      await page.waitForSelector('form input[type="email"]', { visible: true, timeout: 15000 });
    } catch (e) {
      console.error('Failed to find Product Quote Form input. Page Title:', await page.title());
      throw e;
    }

    // Wait specifically for hydration logic to initialize the onSubmit handler
    await page.evaluate(() => new Promise((resolve) => setTimeout(resolve, 2000)));

    // In RequestQuoteForm, the email input is type="email" and message is a textarea.
    await page.type('form input[type="email"]', 'testing@mintel.me');
    await page.type(
      'form textarea',
      'Automated request for product quote via E2E testing framework.',
    );

    // Give state a moment to settle
    await page.evaluate(() => new Promise((resolve) => setTimeout(resolve, 500)));

    console.log(`   ⏳ Waiting for alert selector (30s)...`);
    try {
      // Submit and wait for success state
      await Promise.all([
        page.waitForSelector('[role="alert"]', { timeout: 30000 }),
        page.click('form button[type="submit"]'),
      ]);
    } catch (e) {
      console.error(`❌ Timeout waiting for [role="alert"] on Product page. Dumping content:`);
      const bodyHTML = await page.evaluate(() => document.body.innerHTML.slice(0, 1000));
      console.log(`💻 PAGE BODY (partial): ${bodyHTML}`);
      throw e;
    }

    const alertText = await page.$eval('[role="alert"]', (el) => el.textContent);
    console.log(`   🔔 Alert text: ${alertText}`);

    const isError = 
      alertText?.toLowerCase().includes('failed') || 
      alertText?.toLowerCase().includes('wrong') ||
      alertText?.toLowerCase().includes('fehlgeschlagen') || 
      alertText?.toLowerCase().includes('schief gelaufen') ||
      alertText?.toLowerCase().includes('fehler');

    if (isError) {
      throw new Error(`Form submitted but showed error: ${alertText}`);
    }

    console.log(`✅ Product Quote Form submitted successfully! (Success state verified)`);
  } catch (err: any) {
    console.error(`❌ Product Quote Form Test Failed: ${err.message}`);
    hasErrors = true;
  }

  // 5. Cleanup: Delete test submissions from Payload CMS
  console.log(`\n🧹 Starting cleanup of test submissions...`);
  try {
    const apiUrl = `${targetUrl.replace(/\/$/, '')}/api/form-submissions`;
    const searchUrl = `${apiUrl}?where[email][equals]=testing@mintel.me`;

    // Fetch test submissions
    const searchResponse = await axios.get(searchUrl, {
      headers: { Cookie: `klz_gatekeeper_session=${gatekeeperPassword}` },
    });

    const testSubmissions = searchResponse.data.docs || [];
    console.log(`   Found ${testSubmissions.length} test submissions to clean up.`);

    for (const doc of testSubmissions) {
      try {
        await axios.delete(`${apiUrl}/${doc.id}`, {
          headers: { Cookie: `klz_gatekeeper_session=${gatekeeperPassword}` },
        });
        console.log(`   ✅ Deleted submission: ${doc.id}`);
      } catch (delErr: any) {
        // Log but don't fail, 403s on Directus / Payload APIs for guest Gatekeeper sessions are normal
        console.warn(
          `   ⚠️ Cleanup attempt on ${doc.id} returned an error, typically due to API Auth separation: ${delErr.message}`,
        );
      }
    }
  } catch (err: any) {
    if (err.response?.status === 403) {
      console.warn(
        `   ⚠️ Cleanup fetch failed with 403 Forbidden. This is expected if the runner lacks admin API credentials. Test submissions remain in the database.`,
      );
    } else {
      console.error(`   ❌ Cleanup fetch failed: ${err.message}`);
    }
    // Don't mark the whole test as failed just because cleanup failed
  }

  await browser.close();

  // 6. Evaluation
  if (hasErrors) {
    console.error(`\n🚨 IMPORTANT: Form E2E checks failed. The CI build is failing.`);
    process.exit(1);
  } else {
    console.log(`\n🎉 SUCCESS: All form submissions arrived and handled correctly!`);
    process.exit(0);
  }
}

main();
