import express, { response } from 'express';
import { createProxyServer } from 'http-proxy';
import zlib from 'zlib';
import fs from 'fs';
import path from 'path';
import cheerio from 'cheerio';
import bodyParser from 'body-parser';

let originalUrl: string | undefined  = undefined;

const app = express();
const proxy = createProxyServer({
  target: 'http://google.com', // Replace with your target URL
  changeOrigin: true,
  // ignorePath: true,
  selfHandleResponse: true, // Required for modifying the response
    // onProxyRes: responseInterceptor(async (responseBuffer, proxyRes, req, res) => {
    //   let response = responseBuffer.toString('utf8');

    //   // Convert relative URLs to absolute URLs
    //   response = response.replace(/(href|src)="\/([^"]+)"/g, `$1="http://google.com/$2"`);

    //   return response;
    // }),
});

// app.use('/hardcodedproxy', proxy('www.google.com'));

// proxy.on('proxyRes', (proxyRes, req, res) => {
//   let body: Buffer[] = [];

//   proxyRes.on('data', (chunk) => {
//     body.push(Buffer.isBuffer(chunk) ? chunk : Buffer.from(chunk));
//   });

//   proxyRes.on('end', () => {
//     let rawBody = Buffer.concat(body);

//     // Get content type
//     const contentType = proxyRes.headers['content-type'] || '';

//     // Handle decompression safely
//     try {
//       if (proxyRes.headers['content-encoding']) {
//         if (proxyRes.headers['content-encoding'].includes('gzip')) {
//           rawBody = zlib.gunzipSync(rawBody);
//         } else if (proxyRes.headers['content-encoding'].includes('br')) {
//           rawBody = zlib.brotliDecompressSync(rawBody);
//         } else if (proxyRes.headers['content-encoding'].includes('deflate')) {
//           rawBody = zlib.inflateSync(rawBody);
//         }

//         delete proxyRes.headers['content-encoding']; // Let the browser handle plain text
//       }
//     } catch (err) {
//       console.error('Decompression failed:', err);
//     }

//     // Modify HTML only
//     if (contentType.includes('text/html')) {
//       let response = rawBody.toString('utf8');
//       response = response.replace(/<head>/, `<head><base href="https://www.bosch-diy.com/">`);
//       rawBody = Buffer.from(response, 'utf8');
//     }

//     // 🔹 Set CORS headers dynamically after modifications
//     res.setHeader('Access-Control-Allow-Origin', '*');
//     res.setHeader('Access-Control-Allow-Methods', 'GET, POST, OPTIONS');
//     res.setHeader('Access-Control-Allow-Headers', 'Content-Type');

//     // 🔹 Allow CORS for static assets
//     if (
//       contentType.includes('font') ||
//       contentType.includes('css') ||
//       contentType.includes('javascript') ||
//       contentType.includes('image')
//     ) {
//       res.setHeader('Access-Control-Allow-Origin', '*');
//     }

//     res.writeHead(proxyRes.statusCode || 200, proxyRes.headers);
//     res.end(rawBody);
//   });
// });

// const baseUrl = 'http://localhost:3005/proxy?url='; // replace wtih env vars for different envs
const baseUrl = 'http://localhost:3005/proxy/'; // replace wtih env vars for different envs
const getBaseUrl = (urlString: string): string => {
  try {
      const url = new URL(urlString);
      const baseDomain = url.hostname; // Get the base domain
      const protocol = url.protocol; // Get the protocol (includes ':' at the end)

      // Format the output as needed
      const output = {
          protocol: protocol, // e.g., 'https:'
          baseDomain: baseDomain // e.g., 'www.example.com'
      };

      return protocol + '://' + baseDomain;
  } catch (error) {
      return '';
  }
};

const rewriteLinks = (response: string): string => {
  const originalHost = new URL(originalUrl || '').host;
  // const baseProxyUrl = `http://localhost:3005/proxy?url=`;
  const baseProxyUrl = `http://localhost:3005/proxy/`;
  const hostnameParts = originalHost.split('.');
  const baseDomain = hostnameParts.slice(-2).join('.');

      // 🔹 Skip URLs pointing to static assets (JS, CSS, images, fonts)
      const staticFileExtensions = /\.(js|css|png|jpe?g|gif|svg|woff2?|ttf|eot|ico|mp4|webm|ogg|json|xml)(\?.*)?$/i;

      // 🔹 Rewrite `<a href="...">` links (absolute & relative) unless they are static assets
      response = response.replace(
        /<a\s+[^>]*?href=["'](https?:\/\/([^"']+))["']/gi,
        (match, fullUrl, domain) => {
          if (domain.includes(baseDomain) && !staticFileExtensions.test(fullUrl)) {
            console.log('match', match);
            return match.replace(fullUrl, `${baseProxyUrl}${encodeURIComponent(fullUrl)}`);
          }
          return match;
        }
      );

      response = response.replace(
        /<a\s+[^>]*?href=["'](\/[^"']*)["']/gi,
        (match, relativePath) => {
          if (!staticFileExtensions.test(relativePath)) {
            return match.replace(relativePath, `${baseProxyUrl}${encodeURIComponent(originalUrl + relativePath)}`);
          }
          return match;
        }
      );

      // 🔹 Rewrite `<form action="...">` links unless they are static assets
      response = response.replace(
        /<form\s+[^>]*?action=["'](https?:\/\/([^"']+))["']/gi,
        (match, fullUrl, domain) => {
          if (domain.includes(baseDomain) && !staticFileExtensions.test(fullUrl)) {
            return match.replace(fullUrl, `${baseProxyUrl}${encodeURIComponent(fullUrl)}`);
          }
          return match;
        }
      );

      response = response.replace(
        /<form\s+[^>]*?action=["'](\/[^"']*)["']/gi,
        (match, relativePath) => {
          if (!staticFileExtensions.test(relativePath)) {
            return match.replace(relativePath, `${baseProxyUrl}${encodeURIComponent(originalUrl + relativePath)}`);
          }
          return match;
        }
      );

      // 🔹 Rewrite `<iframe src="...">` unless it is a static asset
      response = response.replace(
        /<iframe\s+[^>]*?src=["'](https?:\/\/([^"']+))["']/gi,
        (match, fullUrl, domain) => {
          if (domain.includes(baseDomain) && !staticFileExtensions.test(fullUrl)) {
            return match.replace(fullUrl, `${baseProxyUrl}${encodeURIComponent(fullUrl)}`);
          }
          return match;
        }
      );

      // 🔹 Rewrite AJAX / Fetch requests to go through the proxy
      response = response.replace(
        /fetch\(["'](https?:\/\/([^"']+))["']/gi,
        (match, fullUrl, domain) => {
          if (domain.includes(baseDomain) && !staticFileExtensions.test(fullUrl)) {
            return match.replace(fullUrl, `${baseProxyUrl}${encodeURIComponent(fullUrl)}`);
          }
          return match;
        }
      );

      // 🔹 Rewrite links inside JSON-like structures: `"url": "https://..."`
      response = response.replace(
        /"url"\s*:\s*"([^"]+)"/gi,
        (match, fullUrl) => {
          try {
            const parsedUrl = new URL(fullUrl);
            if (parsedUrl.host.includes(baseDomain) && !staticFileExtensions.test(fullUrl)) {
              return match.replace(fullUrl, `${baseProxyUrl}${encodeURIComponent(fullUrl)}`);
            }
          } catch (e) {
            return match;
          }
          return match;
        }
      );

      return response;
}

const simpleRewriteLinks = (response: string): string => {
  const originalHost = new URL(originalUrl || '').host;
  // const baseProxyUrl = `http://localhost:3005/proxy?url=`;
  const baseProxyUrl = `http://localhost:3005/proxy/`;
  const hostnameParts = originalHost.split('.');
  const baseDomain = hostnameParts.slice(-2).join('.');
  console.log('baseDomain', baseDomain);

  // Regular expression to match URLs (absolute, relative, and JSON-like strings)
  // const urlRegex = /(https?:\/\/[^\s"']+|(?<!http:|https:)\/\/[^\s"']+|(?<!")\/[^\s"']*|:"\/[^\s"']*)/g;
  const urlRegex = /(https?:\/\/[^\s"']+|(?<!http:|https:)\/\/[^\s"']+|\\s*=\s*['"]|:"\/[^\s"']*)/g;

  // Replace URLs with the base domain
  return response.replace(urlRegex, (match) => {
      // console.log('match', match);
      // If the URL is already absolute, leave it as is
        if (match.startsWith('http://') || match.startsWith('https://')) {
            return match;
        }
        // If the URL is protocol-relative (starts with //), prepend https:
        if (match.startsWith('=')) {
          return `="${baseDomain}${match.slice(2)}"`;
        }
        // If the URL is in a JSON-like string (starts with :"/"), remove the `:"` prefix
        if (match.startsWith(':"/')) {
            return `:"${baseDomain}${match.slice(2)}"`;
        }
        // If the URL is relative, prepend the base domain
        return `${baseDomain}${match}`;
  });
}

proxy.on('proxyReq', (proxyReq, req) => {
  // Forward necessary headers
  proxyReq.setHeader('User-Agent', req.headers['user-agent'] || '');
  proxyReq.setHeader('Accept-Language', req.headers['accept-language'] || '');
  proxyReq.setHeader('Cookie', req.headers['cookie'] || ''); // Ensure cookies are forwarded
});

proxy.on('proxyRes', (proxyRes, req, res) => {

  if (proxyRes.statusCode && proxyRes.statusCode >= 300 && proxyRes.statusCode < 400) {
    const location = proxyRes.headers['location'];

    if (location) {
      try {
        const targetUrl = new URL(location);
        // const proxyBase = `http://localhost:3005/proxy?url=`;
        const proxyBase = `http://localhost:3005/proxy/`;
        const proxiedRedirect = `${proxyBase}${encodeURIComponent(targetUrl.href)}`;

        console.log(`Redirect intercepted: ${location} -> ${proxiedRedirect}`);

        res.writeHead(proxyRes.statusCode, { Location: proxiedRedirect });
        res.end();
        return;
      } catch (err) {
        console.error('Failed to parse redirect URL:', location, err);
      }
    }
  }

  let body: Buffer[] = [];
  delete proxyRes.headers['content-length'];

  proxyRes.on('data', (chunk) => {
    body.push(Buffer.isBuffer(chunk) ? chunk : Buffer.from(chunk));
  });

  proxyRes.on('end', () => {
    let rawBody = Buffer.concat(body);

    // Handle decompression safely
    try {
      if (proxyRes.headers['content-encoding']) {
        if (proxyRes.headers['content-encoding'].includes('gzip')) {
          rawBody = zlib.gunzipSync(rawBody);
        } else if (proxyRes.headers['content-encoding'].includes('br')) {
          rawBody = zlib.brotliDecompressSync(rawBody);
        } else if (proxyRes.headers['content-encoding'].includes('deflate')) {
          rawBody = zlib.inflateSync(rawBody);
        }

        delete proxyRes.headers['content-encoding']; // Let the browser handle plain text
      }
    } catch (err) {
      console.error('Decompression failed:', err);
    }

    // Modify HTML only
    const contentType = proxyRes.headers['content-type'] || '';
    if (contentType.includes('text/html')) {
      let response = rawBody.toString('utf8');
      const filePath = path.join(__dirname, 'original_response.html');
      fs.writeFile(filePath, response, (err) => {
        if (err) {
            console.error('Error writing to file', err);
          }
        console.error('Successtully writing response to file', err);
    });

    // Debugging
    console.log("Received HTML Length:", response.length);
    console.log('checking if __NEXT_DATA__ exists in response:');
    if (!rawBody.toString().includes('__NEXT_DATA__')) {
      console.warn("🚨 __NEXT_DATA__ is missing from final response! 🚨");
    } else {
      console.log("✅ __NEXT_DATA__ exists in final response.");
    }

      // const oU = originalUrl || 'https://www.bosch-diy.com';
      // if (originalUrl) {
      //   const baseOriginalUrl = getBaseUrl(originalUrl);
      //   const rewrittenUrl = originalUrl ? `${baseUrl}${baseOriginalUrl}` : '';
      //   // Add a <base> tag to ensure relative URLs resolve correctly
      //   response = response.replace(/<head>/, `<head><base href="${rewrittenUrl}">`);


      //   // Rewrite only relative URLs to absolute URLs
      //   response = response.replace(/(href|src)="\/([^"]+)"/g, `$1="${rewrittenUrl}/$2"`);
      // }

      // const originalHost = new URL(originalUrl || '').host;
      // const baseProxyUrl = `http://localhost:3005/proxy?url=`;
      // // 🔹 Skip URLs pointing to static assets (JS, CSS, images, fonts)
      // const staticFileExtensions = /\.(js|css|png|jpe?g|gif|svg|woff2?|ttf|eot|ico|mp4|webm|ogg|json|xml)(\?.*)?$/i;


      // // 🔹 Rewrite <a href="..."> links to go through the proxy
      // console.log('Rewriting absolute <a href="..."> links');
      // response = response.replace(
      //   /<a\s+[^>]*?href=["'](https?:\/\/([^"']+))["']/gi,
      //   (match, fullUrl, domain) => {
      //     if (domain.includes(originalHost) && !staticFileExtensions.test(fullUrl)) {
      //       return match.replace(fullUrl, `${baseProxyUrl}${encodeURIComponent(fullUrl)}`);
      //     }
      //     return match;
      //   }
      // );

      // // 🔹 Rewrite relative <a href="..."> links (e.g., /page.html) to absolute via the proxy
      // console.log('Rewriting relative <a href="..."> links');
      // response = response.replace(
      //   /<a\s+[^>]*?href=["'](\/[^"']*)["']/gi,
      //   (match, relativePath) => {
      //     if (!staticFileExtensions.test(relativePath)) {
      //       return match.replace(relativePath, `${baseProxyUrl}${encodeURIComponent(originalUrl + relativePath)}`);
      //     }
      //     return match;
      //   }
      // );

      // // 🔹 Rewrite <form action="..."> to go through the proxy
      // console.log('Rewriting forms <form action="..."> links');
      // response = response.replace(
      //   /<form\s+[^>]*?action=["'](https?:\/\/([^"']+))["']/gi,
      //   (match, fullUrl, domain) => {
      //     if (domain.includes(originalHost) && !staticFileExtensions.test(fullUrl)) {
      //       return match.replace(fullUrl, `${baseProxyUrl}${encodeURIComponent(fullUrl)}`);
      //     }
      //     return match;
      //   }
      // );

      // // 🔹 Rewrite relative <form action="..."> paths
      // response = response.replace(
      //   /<form\s+[^>]*?action=["'](\/[^"']*)["']/gi,
      //   (match, relativePath) => {
      //     return match.replace(relativePath, `${baseProxyUrl}${encodeURIComponent(originalUrl + relativePath)}`);
      //   }
      // );

      response = simpleRewriteLinks(response);

      fs.writeFile(filePath, response, (err) => {
        if (err) {
            console.error('Error writing to file', err);
          }
        console.error('Successtully writing response to file', err);
    });

      // force all internal navigation to go trough the proxy
      const overrideScript = `
        <script>
          (function() {
            const originalPushState = window.history.pushState;
            const originalReplaceState = window.history.replaceState;

            function sanitizeUrl(url) {
              if (!url.startsWith('/') && !url.startsWith(window.location.origin)) {
                console.warn('🔍 Blocking navigation to external URL:', url);
                return window.location.pathname; // Stay on the same page
              }
              return url;
            }

            window.history.pushState = function(state, title, url) {
              return originalPushState.call(this, state, title, sanitizeUrl(url));
            };

            window.history.replaceState = function(state, title, url) {
              return originalReplaceState.call(this, state, title, sanitizeUrl(url));
            };
          })();
        </script>`;

        const overrideScriptToSolvePushState_orig = `
          <script>
            (function() {
              function sanitizeUrl(url) {
                // If the URL is trying to go to another origin, block it
                if (!url.startsWith(window.location.origin)) {
                  console.warn('🚫 Blocking cross-origin replaceState:', url);
                  return window.location.pathname; // Stay on the same page
                }
                return url;
              }

              function overrideHistoryMethod(method) {
                const original = history[method];

                Object.defineProperty(history, method, {
                  configurable: false,
                  enumerable: false,
                  writable: false,
                  value: function(state, title, url) {
                    const safeUrl = sanitizeUrl(url);
                    try {
                      return original.call(this, state, title, safeUrl);
                    } catch (e) {
                      console.error(\`❌ History. error prevented:\`, e);
                    }
                  }
                });
              }

              overrideHistoryMethod('replaceState');
              overrideHistoryMethod('pushState');

              console.log('✅ History override (Object.defineProperty) injected');
            })();
          </script>`;

          const overrideScriptToSolvePushState = `
          <script>
    (function() {
      console.log('✅ Injecting domain and history overrides...');

      // Override history.replaceState to prevent cross-origin errors
      const originalReplaceState = window.history.replaceState;
      window.history.replaceState = function(state, title, url) {
        try {
          if (url && url.startsWith("https://www.bosch-diy.com")) {
            console.warn("🚨 Prevented replaceState to", url);
            return;
          }
          originalReplaceState.apply(this, arguments);
          console.log("✅ replaceState called with:", state, title, url);
        } catch (e) {
          console.error("❌ Error in replaceState override:", e);
        }
      };

      console.log("✅ history.replaceState overridden successfully");
    })();
  </script>`;

  const fixDomainScript = `
  <script>
    console.log('✅ Injecting document.domain fix...');

    try {
      document.domain = "localhost";
      console.log("✅ document.domain set to localhost");
    } catch (e) {
      console.warn("❌ Unable to set document.domain:", e);
    }
  </script>`;

        console.log("🔹 Adding override script to response");
      response = response.replace('</head>', `${overrideScriptToSolvePushState}${fixDomainScript}${overrideScript}</head>`);


      rawBody = Buffer.from(response, 'utf8');

      const encoding = proxyRes.headers['content-encoding'] || '';
      console.log("🔄 Encoding Type:", encoding);

      if (encoding.includes('gzip')) {
        rawBody = zlib.gunzipSync(rawBody);
      } else if (encoding.includes('br')) {
        rawBody = zlib.brotliDecompressSync(rawBody);
      } else if (encoding.includes('deflate')) {
        rawBody = zlib.inflateSync(rawBody);
      }

      delete proxyRes.headers['content-encoding']; // Remove encoding header
      if (proxyRes.headers['set-cookie']) {
        proxyRes.headers['set-cookie'] = proxyRes.headers['set-cookie'].map(cookie =>
          cookie.replace(/Domain=\.?bosch-diy\.com/gi, 'Domain=localhost').replace(/Secure;/gi, '') // Remove Secure for local dev
        );
      }
      if (proxyRes.headers['content-security-policy']) {
        delete proxyRes.headers['content-security-policy']; // Remove CSP
      }
      if (proxyRes.headers['content-security-policy-report-only']) {
        delete proxyRes.headers['content-security-policy-report-only']; // Remove CSP report-only mode
      }
    }

    // 🔹 Set CORS headers dynamically for all responses
    res.setHeader('Access-Control-Allow-Origin', '*');
    res.setHeader('Access-Control-Allow-Methods', 'GET, POST, OPTIONS');
    res.setHeader('Access-Control-Allow-Headers', 'Content-Type');
    res.setHeader('Cache-Control', 'no-store, no-cache, must-revalidate, proxy-revalidate');
    res.setHeader('Pragma', 'no-cache');
    res.setHeader('Expires', '0');

    res.writeHead(proxyRes.statusCode || 200, proxyRes.headers);
    res.end(rawBody);
  });
});





app.get('/proxy/*', (req, res) => {
  const oU = decodeURIComponent((req as any).params[0]); // This captures everything after '/proxy/'
  console.log('>>req', (req as any).params[0]);
  console.log('toU:', oU)
  const ooU = req.query.url;
  console.log('> originalUrl:', ooU)
  if (!oU || typeof oU !== 'string') {
    return res.status(400).send('Please provide a valid URL as a string');
  }
  originalUrl = oU;

  req.url = decodeURIComponent(req.url.replace(/^\/proxy/, '/search'));
  console.log('req.url:', req.url)

  // console.log('proxy:', proxy)
  proxy.web(req, res, { target: originalUrl });
});

// // Serve frontend static files
// const distDir = path.join(__dirname, '../frontend/build'); // Adjust this if your frontend build output is different
// app.use(express.static(distDir));

// app.get('*', (req, res) => {
//   res.sendFile(path.join(__dirname, '../../frontend/build', 'index.html')); // Serve the frontend
// });

const port = process.env.PORT || 3005;

app.listen(port, () => {
  console.log(`Server listening on port ${port}`);
});