import http,{ IncomingMessage, ServerResponse, request as httpRequest } from 'http';
import https, { request as httpsRequest } from 'https';
import { ClientRequest as ProxyRequest, IncomingMessage as ProxyResponse } from 'http';
import { Response } from 'express';
import zlib from 'zlib';
import fs, { write } from 'fs';
import path from 'path';
import { directSimpleRewriteLinks, getBaseUrl, simpleRewriteLinks } from '../helpers/helpers';
import cookieParser from 'cookie-parser';
import { getPublicHost } from '../helpers/getPublicHost';
import { writeToLog } from '../helpers/writeToLog';
import { val } from 'cheerio/dist/commonjs/api/attributes';

const MAX_REDIRECTS = 5;

export const directReqHandler = (req: IncomingMessage, proxyReq: ProxyRequest) => {
    // Forward necessary headers for requests to the target URL
    // proxyReq.setHeader('Host', 'booking.com');
    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

    // proxyReq.setHeader('Host', 'www.booking.com');
    proxyReq.setHeader('X-Forwarded-Host', proxyReq.getHeader('Host') || '');

    if (proxyReq && proxyReq.getHeaders) {
      writeToLog(`>> Final request headers: ${JSON.stringify(proxyReq.getHeaders())}`);
      Object.entries(proxyReq.getHeaders()).forEach(([key, value]) => {
        writeToLog(`key: ${key}, val: ${value}`);
      });
  }
    
};

const followRedirectOriginal = (proxyRes: ProxyResponse, req: IncomingMessage, res: ServerResponse<IncomingMessage>) => {
  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 proxyBase = `http://localhost:3005/browse/`;
        const proxyBase = getPublicHost();
        // const proxiedRedirect = `${proxyBase}${encodeURIComponent(targetUrl.href.replace(/\/$/, ''))}`;
        const proxiedRedirect = `${proxyBase}${targetUrl.href.replace(/\/$/, '')}`;

        writeToLog(`>> redirect detected: Target: ${location}, Proxy base: ${proxyBase}, Proxied: ${proxiedRedirect}`);

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

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

export const directResHandler = (proxyRes: ProxyResponse, req: IncomingMessage, res: ServerResponse<IncomingMessage>, originalUrl: string) => {

  // handler redirects first:

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

    if (location) {
      try {
        const redirectTarget = new URL(location, originalUrl); // Support relative redirects
        const currentProxyUrl = new URL(originalUrl, getPublicHost());

        const samePath =
          redirectTarget.pathname.replace(/\/$/, '') === currentProxyUrl.pathname.replace(/\/$/, '');
        const sameSearch = redirectTarget.search === currentProxyUrl.search;

        if (
          redirectTarget.hostname === new URL(getBaseUrl(originalUrl)).hostname &&
          samePath &&
          sameSearch
        ) {
          writeToLog(`↩️ Canonical self-redirect detected to ${location}. Rewriting to proxy anyway to avoid leaking target domain.`);
        }

        // ✅ Always rewrite the redirect to go through the proxy
        const proxiedRedirect = `${getPublicHost()}${redirectTarget.pathname}${redirectTarget.search || ''}`;

        writeToLog(`🔀 Rewriting Location: ${location} → ${proxiedRedirect}`);

        res.writeHead(proxyRes.statusCode, {
          ...proxyRes.headers,
          Location: proxiedRedirect,
        });
        res.end();
        return;

      } catch (err) {
        console.error('⚠️ Failed to parse Location header:', location, err);
      }
    }
  }
  
  writeToLog(`>> proxyRes.headers: ${JSON.stringify(proxyRes.headers)}`);
    if (proxyRes.headers['set-cookie']) {
      console.log('Forwarding Set-Cookie:', proxyRes.headers['set-cookie']);
      writeToLog(`>!> FORWARDING Set-Cookie: ${proxyRes.headers['set-cookie']}`);
      res.setHeader('Set-Cookie', proxyRes.headers['set-cookie']);
    }
    

    writeToLog(`>> ${proxyRes.statusCode}, proxyRes.headers: ${proxyRes.headers['location']}`);
    // moved into a method
    // 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 proxyBase = `http://localhost:3005/browse/`;
    //         const proxyBase = getPublicHost();
    //         // const proxiedRedirect = `${proxyBase}${encodeURIComponent(targetUrl.href.replace(/\/$/, ''))}`;
    //         const proxiedRedirect = `${proxyBase}${targetUrl.href.replace(/\/$/, '')}`;

    //         writeToLog(`>> redirect detected: Target: ${location}, Proxy base: ${proxyBase}, Proxied: ${proxiedRedirect}`);
    
    //         console.log(`direct access - 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: Buffer) => {
        body.push(Buffer.isBuffer(chunk) ? chunk : Buffer.from(chunk));
      });
    
      proxyRes.on('end', () => {

        // Check for redirect response and follow if necessary
        // if (proxyRes.statusCode && proxyRes.statusCode >= 300 && proxyRes.statusCode < 400) {
        //   const redirectUrl = proxyRes.headers.location; // Get the redirect URL
        //   if (redirectUrl) {
        //     followRedirect(redirectUrl, (res as unknown as Response) ); // Follow the redirect
        //   } 
        // }

        let rawBody = Buffer.concat(body);
        // Log the raw body for debugging
        // console.log('Raw body length:', rawBody.length);
        // console.log('Raw body (first 100 bytes):', rawBody.toString('utf8', 0, Math.min(100, rawBody.length)));
    
        // Handle decompression safely
        try {
          console.log('Raw body length:', rawBody.length);
          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');
          // console.log('WRITING TO FILE', rawBody.length, 'for url', originalUrl);
          const filePath = path.join(__dirname, 'original_response.html');
          const filePathModified = path.join(__dirname, 'modified_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.");
        }
    

        if (!originalUrl.includes('js_tracking') && !originalUrl.includes('favicon.ico')) {
          fs.writeFile(filePath, response, (err) => {
            if (err) {
                console.error('Error writing to file', err);
              }
            console.error('Successtully writing response to file', err);
          });
        }

        console.log('!!!! Rewriting  links');
        response = directSimpleRewriteLinks(response, originalUrl);
        console.log('!!!! writing to file', filePathModified);
  
        if (!originalUrl.includes('js_tracking') && !originalUrl.includes('favicon.ico')) {
          fs.writeFile(filePathModified, 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 = "3bee-207-180-216-165.ngrok-free.app";
                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']) {
          //   writeToLog(`set-cookie found: ${proxyRes.headers['set-cookie']}`);
          //   proxyRes.headers['set-cookie'] = proxyRes.headers['set-cookie'].map((cookie: string) =>
          //     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
          }
        }
    
        const filePathModified = path.join(__dirname, 'modified_response.html');
        if (!originalUrl.includes('js_tracking')) {
          fs.writeFile(filePathModified, rawBody, (err) => {
            if (err) {
                console.error('Error writing to file', err);
              }
            console.error('Successtully writing response to file', err);
          });
        }

        // 🔹 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.setHeader('location', 'https://3bee-207-180-216-165.ngrok-free.app');
        // res.setHeader('location', 'http://localhost:3005');
        // proxyRes.headers['location'] = 'https://3bee-207-180-216-165.ngrok-free.app';
        // proxyRes.headers['location'] = 'http://localhost:3005';
      //   if (proxyRes.headers['location']) {
      //     const originalLocation = proxyRes.headers['location'];
      //     const originalUrlObj = new URL(originalLocation, getBaseUrl(originalUrl));
      //     writeToLog(`🔹 checking status code: ${proxyRes.statusCode} for location: ${proxyRes.headers['location']}`);
      //     if (proxyRes.statusCode && proxyRes.statusCode >= 300 && proxyRes.statusCode < 400 && `${originalUrlObj.pathname}${originalUrlObj.search || ''}` === '/') {
      //       // do nothing
      //       writeToLog(`doing nothing because: ${proxyRes.statusCode} → ${ `${originalUrlObj.pathname}${originalUrlObj.search || ''}`}`);
      //       writeToLog(`writing raw body:`);
      //       writeToLog(rawBody.toString());
      //       proxyRes.headers['location'] = getPublicHost('basePath');
      //       // proxyRes.statusCode = 200;
      //     } else if (proxyRes.statusCode && proxyRes.statusCode >= 300 && proxyRes.statusCode < 400 && proxyRes.headers['location']) {
      //       try {
      //         const proxiedLocation = `${getPublicHost('basePath')}${originalUrlObj.pathname}${originalUrlObj.search || ''}`;
          
      //         writeToLog(`Intercepted redirect: ${originalLocation} → ${proxiedLocation}`);
          
      //         proxyRes.headers['location'] = proxiedLocation;
      //       } catch (err) {
      //         console.error('Error rewriting location header:', proxyRes.headers['location'], err);
      //       }
      //     }
      // }
      //   // res.setHeader('Set-Cookie', 'headers');


      // this is probably redundat as we are handling redirections at the begining of directResHandler
      if (proxyRes.statusCode && proxyRes.statusCode >= 300 && proxyRes.statusCode < 400) {
        const location = proxyRes.headers['location'];
        if (location) {
          try {
            const oLU = new URL(location, getBaseUrl(originalUrl)); // handles relative URLs
            const originalLocationUrl = new URL(oLU.pathname + oLU.search); // handles relative URLs
            const currentProxyUrl = new URL(originalUrl, getPublicHost());
      
            const isSameTarget = (
              // originalLocationUrl.hostname === new URL(targetUrl).hostname &&
              originalLocationUrl.hostname === new URL('https://www.booking.com').hostname &&
              originalLocationUrl.pathname === new URL(originalUrl).pathname
            );
      
            if (isSameTarget) {
              // Booking.com redirected to same canonical page, don't rewrite
              console.warn(`Skipping redirect rewrite: Booking.com → Booking.com`);
              res.writeHead(proxyRes.statusCode, { Location: location });
              res.end();
              return;
            }
      
            // Otherwise rewrite redirect to go through proxy
            const proxiedRedirect = `${getPublicHost()}${originalLocationUrl.pathname}${originalLocationUrl.search || ''}`;
            console.log(`Rewriting redirect → ${proxiedRedirect}`);
            res.writeHead(proxyRes.statusCode, { Location: proxiedRedirect });
            res.end();
            return;
      
          } catch (err) {
            console.error('Redirect URL parsing failed:', location, err);
          }
        }
      }

        writeToLog(`response headers: ${JSON.stringify(proxyRes.headers)}`);
    
        res.writeHead(proxyRes.statusCode || 200, proxyRes.headers);
        res.end(rawBody);
      });

}