export function insitella() {
  const apiBaseUrl = "http://192.168.10.152/insitella/analytics"; // "http://localhost:5000/insitella/analytics";;
  const socket = io("http://192.168.10.152");  //http://localhost:5000/
  const apiUrlPattern = "api.dev";  // URLs containing this pattern will be considered API calls
  const isApiRequest = (url) => {
    try {
      return url.includes(apiUrlPattern);
    } catch {
      return url.includes(apiUrlPattern);
    }
  };

  const setCookie = (name, value, hoursToExpire) => {
    const expiryDate = new Date(Date.now() + hoursToExpire * 60 * 60 * 1000).toUTCString();
    document.cookie = `${name}=${value};expires=${expiryDate};path=/`;
  };

  const getCookie = (cookieName) =>
    document.cookie
      .split(";")
      .map((c) => c.trim())
      .find((c) => c.startsWith(`${cookieName}=`))
      ?.split("=")[1] || null;

  const saveSessionData = (key, value) => sessionStorage.setItem(key, JSON.stringify(value));

  const getSessionData = (key, defaultValue) => {
    const storedValue = sessionStorage.getItem(key);
    return storedValue ? JSON.parse(storedValue) : defaultValue;
  };

  const detectDeviceType = () => {
    const ua = navigator.userAgent;
    if (/mobile/i.test(ua)) return "Mobile";
    if (/tablet|ipad|playbook|silk/i.test(ua)) return "Tablet";
    return "Desktop";
  };

  const detectBrowser = () => {
    const ua = navigator.userAgent;
    if (ua.includes("Firefox")) return "Firefox";
    if (ua.includes("Edg")) return "Edge";
    if (ua.includes("Chrome")) return "Chrome";
    if (ua.includes("Safari")) return "Safari";
    if (ua.includes("Trident")) return "Internet Explorer";
    return "Unknown";
  };

  const initializeUserSession = () => {
    const existingSession = getSessionData("insitella_userevents", null);
    if (existingSession) {
      existingSession.userEvents ??= [];
      return existingSession;
    }
    return {
      sessionId: getCookie("userId") || `${Date.now()}-${Math.random().toString(36).slice(2, 11)}`,
      startTime: new Date().toISOString(),
      clientName: document.querySelector("title")?.innerText || "",
      device: detectDeviceType(),
      browser: detectBrowser(),
      userEvents: [],
      endTime: null,
    };
  };

  const persistUserSession = (userSession) =>
    saveSessionData("insitella_userevents", userSession);

  const renderCookieConsentBanner = (onConsentAccept) => {
    if (getCookie("cookieAccepted")) return;
    const consentBannerHTML = `
      <div id="cookiePopup" style="position: fixed; inset: 0; background: rgba(0,0,0,0.5); 
        display: flex; justify-content: center; align-items: center; z-index: 9999;">
        <div style="background: #fff; position: fixed; bottom: 20px; left: 50px; max-width: 500px; 
          border-radius: 15px; text-align: center; border: 1px solid #493179; padding: 25px; 
          box-shadow: 0 0 18px rgba(0,0,0,0.13);">
          <div style="margin-top: 10px;">
            <h1 style="font-size: 25px; font-weight: 600;">GDPR Compliance Notice</h1>
            <h5>What data do we collect?</h5>
            <ul style="list-style-type: disc; text-align: left;">
              <li>Personal info (name, email, location) when you sign up or interact.</li>
              <li>Usage patterns, preferences, and interactions to enhance your experience.</li>
            </ul>
            <div style="display: flex; justify-content: center;">
              <button id="cookieCancelBtn" style="padding: 10px 20px; margin: 0 5px; border: none; 
                font-size: 16px; font-weight: 500; border-radius: 5px; cursor: pointer; background: #eee; color: #333;">
                Cancel
              </button>
              <button id="cookieAcceptBtn" style="padding: 10px 20px; margin: 0 5px; border: none; 
                font-size: 16px; font-weight: 500; border-radius: 5px; cursor: pointer; background: #493179; color: #fff;">
                Accept
              </button>
            </div>
          </div>
        </div>
      </div>
    `;
    document.body.insertAdjacentHTML("beforeend", consentBannerHTML);
    document.getElementById("cookieAcceptBtn")?.addEventListener("click", onConsentAccept);
    document.getElementById("cookieCancelBtn")?.addEventListener("click", () =>
      document.getElementById("cookiePopup")?.remove()
    );
  };

  const handleConsentAcceptance = async (userSession, userInfo) => {
    document.getElementById("cookiePopup")?.remove();
    if (!getCookie("userId")) {
      setCookie("userId", userSession.sessionId, 30);
    }
    if (navigator.geolocation) {
      try {
        const getUserPosition = () =>
          new Promise((resolve, reject) =>
            navigator.geolocation.getCurrentPosition(resolve, reject)
          );
        const { coords } = await getUserPosition();
        const res = await fetch(
          `https://nominatim.openstreetmap.org/reverse?lat=${coords.latitude}&lon=${coords.longitude}&format=json`
        );
        const locationData = await res.json();
        userInfo.userLocation = {
          latitude: coords.latitude.toString(),
          longitude: coords.longitude.toString(),
          cityName: locationData.address?.city || locationData.address?.town || locationData.address?.village || "",
          country: locationData.address?.country || "",
        };
      } catch (err) {
        console.warn("Location fetch failed", err);
      }
    }
    setCookie("serverUpdateIntervalMs", 5000, 30);
    await fetch(`${apiBaseUrl}/userInfo`, {
      method: "POST",
      headers: { "Content-Type": "application/json" },
      body: JSON.stringify(userInfo),
    });
    setCookie("cookieAccepted", "true", 24);
  };

  const trackPageVisit = (displayConsentBanner) => {
    console.log("Tracking page visit:", window.location.href);
    displayConsentBanner();
  };

  const recordClickEvent = (event, userSession, persistSessionFn) => {
    const currentPageUrl = lastPageUrl;
    let pageSession =
      userSession.userEvents.find((ev) => ev.pagesVisited.url === currentPageUrl);
    if (!pageSession) {
      pageSession = {
        pagesVisited: { url: currentPageUrl, timestamp: new Date().toISOString() },
        clicks: [],
        brokenLinks: [],
      };
      userSession.userEvents.push(pageSession);
    }
    const clickedElement = event.target.closest( 'button, a, input, [role="button"], .p-button, .p-menuitem-link, .p-radiobutton, .p-checkbox-box, .p-select, .p-select-label');
    if (!clickedElement) return;
    const clickData = {
      tag: clickedElement.tagName,
      id: clickedElement.id,
      class: clickedElement.className,
      label:
        clickedElement.getAttribute("label") ||
        clickedElement.getAttribute("aria-label") ||
        clickedElement.innerText.trim().slice(0, 100),
      href: clickedElement.href || clickedElement.getAttribute("href") || null,
      timestamp: new Date().toISOString(),
    };
    pageSession.clicks.push(clickData);
    persistSessionFn(userSession);
    if (clickData.href?.startsWith("http")) {
      fetch(clickData.href, { method: "HEAD" })
        .then((res) => {
          if (!res.ok) {
            pageSession.brokenLinks.push({
              href: clickData.href,
              status: res.status,
              statusText: res.statusText,
              method: 'HEAD',
              type: 'click',
              isApi: false
            });
            persistSessionFn(userSession);
          }
        })
        .catch(() => {
          pageSession.brokenLinks.push({
            href: clickData.href,
            status: 0,
            statusText: "Fetch Failed",
            method: 'HEAD',
            type: 'click',
            isApi: false
          });
          persistSessionFn(userSession);
        });
    }
  };

  const normalizeUrl = (url) => {
    try {
      return new URL(url, location.href).href;
    } catch {
      return url;
    }
  };

  const recordFailedApiCall = (userSession, apiData) => {
    const currentPageUrl = lastPageUrl;
    let pageSession = userSession.userEvents.find((ev) => ev.pagesVisited.url === currentPageUrl);
    if (!pageSession) {
      pageSession = {
        pagesVisited: { url: currentPageUrl, timestamp: new Date().toISOString() },
        clicks: [],
        brokenLinks: []
      };
      userSession.userEvents.push(pageSession);
    }
    const href = normalizeUrl(apiData.url);
    pageSession.brokenLinks.push({
      href,
      status: apiData.status,
      statusText: apiData.statusText,
      method: apiData.method,
      type: apiData.type,
      isApi: apiData.isApi,
      timestamp: new Date().toISOString()
    });
    persistUserSession(userSession);
  };

  const transmitUserEvents = async (userSession) => {
    if (isTransmitting) return;
    const sessionData = getSessionData("insitella_userevents", null);
    if (!sessionData) return;
    const hasPendingEvents = sessionData.userEvents.some(
      (ev) => (ev.clicks?.length || 0) > 0 || (ev.brokenLinks?.length || 0) > 0
    );

    if (!hasPendingEvents) return;
    isTransmitting = true;
    const snapshot = JSON.parse(JSON.stringify(sessionData.userEvents));

    try {
      const res = await fetch(`${apiBaseUrl}/userClickEvents`, {
        method: "POST",
        headers: { "Content-Type": "application/json" },
        body: JSON.stringify({ ...sessionData, userEvents: snapshot }),
      });
      if (!res.ok) throw new Error(`Failed: ${res.status}`);
      await res.json();

      userSession.userEvents = userSession.userEvents.map(ev => {
        const sentPage = snapshot.find(s => s.pagesVisited.url === ev.pagesVisited.url);
        if (!sentPage) return ev;
        const newClicks = ev.clicks.filter(click => !sentPage.clicks.some(sentClick => sentClick.timestamp === click.timestamp));
        const newBrokenLinks = ev.brokenLinks.filter(link => !sentPage.brokenLinks.some(sentLink => sentLink.timestamp === link.timestamp));
        if (newClicks.length > 0 || newBrokenLinks.length > 0) {
          return {
            ...ev,
            clicks: newClicks,
            brokenLinks: newBrokenLinks
          };
        }
        return null;
      }).filter(ev => ev !== null);
      persistUserSession(userSession);
    } catch (err) {
      console.error("Transmission error:", err);
    } finally {
      isTransmitting = false;
    }
  };

  const scheduleEventTransmission = (userSession) => {
    const intervalMs = parseInt(getCookie("serverUpdateIntervalMs"), 10);
    if (!intervalMs) return;
    clearInterval(eventDataTimer);
    eventDataTimer = setInterval(
      () => transmitUserEvents(userSession),
      intervalMs
    );
    clearInterval(bootstrapTimer);
    bootstrapTimer = null;
  };

  const interceptFetch = (userSession) => {
    const originalFetch = window.fetch;
    window.fetch = async (...args) => {
      const request = args[0];
      const url = typeof request === 'string' ? request : request.url;
      const method = typeof request === 'string' ? (args[1]?.method || 'GET') : request.method || 'GET';

      try {
        const response = await originalFetch.apply(window, args);
          if (!response.ok && isApiRequest(url)) {
            recordFailedApiCall(userSession, {
              url,
              method,
              status: response.status,
              statusText: response.statusText,
              type: 'fetch',
              isApi: true,
              error: 'HTTP Error'
            });
          }
        return response;
      } catch (error) {
          if (isApiRequest(url)) {
            recordFailedApiCall(userSession, {
              url,
              method,
              status: 0,
              statusText: error.message,
              type: 'fetch',
              isApi: true,
              error: 'Network Error'
            });
          }
        throw error;
      }
    };
  };

  const interceptXHR = (userSession) => {
    const XHR = XMLHttpRequest.prototype;
    const originalOpen = XHR.open;
    const originalSend = XHR.send;

    XHR.open = function(method, url) {
      this.insitellaMethod = method;
      this.insitellaUrl = url;
      return originalOpen.apply(this, arguments);
    };

    XHR.send = function() {
      if (!isApiRequest(this.insitellaUrl)) return originalSend.apply(this, arguments);

      const recordXHRError = (status, statusText, error) => {
        recordFailedApiCall(userSession, {
          url: this.insitellaUrl,
          method: this.insitellaMethod,
          status,
          statusText,
          type: 'xhr',
          isApi: true,
          error
        });
      };

      this.addEventListener('load', () => {
        if (this.status >= 400) recordXHRError(this.status, this.statusText, 'HTTP Error');
      });

      this.addEventListener('error', () => {
        recordXHRError(0, 'Network Error', 'Network Error');
      });

      this.addEventListener('timeout', () => {
        recordXHRError(0, 'Timeout', 'Timeout');
      });

      return originalSend.apply(this, arguments);
    };
  };

  const displayProductNotification = (data) => {
    const existingNotification = document.getElementById('notification');
    if(existingNotification) {
      existingNotification.remove()
    }

    const notificationDiv = document.createElement('div');
    notificationDiv.id = 'notification';
    notificationDiv.innerHTML = data;
    document.body.appendChild(notificationDiv);
  };

  const closeNotification = () => {
    const notificationDiv = document.getElementById('notification');

    if (notificationDiv) {
        notificationDiv.remove();
    }
  };

  window.closeNotification = closeNotification;
  let lastPageUrl = window.location.href;
  let isTransmitting = false;
  let eventDataTimer = null;
  let bootstrapTimer = null;

  socket.on('connect', () => {
    console.log('Connected to the server');
  });

  socket.on('welcome', (data) => {
    console.log('Received welcome message:', data);
  });

  socket.on('receive_message', (data) => displayProductNotification(data));

  (() => {
    const userSession = initializeUserSession();
    const storedUserId = getCookie("userId");
    const userInfo = {
      sessionId: storedUserId || userSession.sessionId,
      startTime: new Date().toISOString(),
      clientName: document.querySelector("title")?.innerText || "",
      device: detectDeviceType(),
      browser: detectBrowser(),
      platform: navigator.platform,
      userAgent: navigator.userAgent,
      userLocation: {},
    };
    interceptFetch(userSession);
    interceptXHR(userSession);
    const { pushState } = history;
    history.pushState = (...args) => {
      pushState.apply(history, args);
      lastPageUrl = window.location.href;
      trackPageVisit(() =>
        renderCookieConsentBanner(() =>
          handleConsentAcceptance(userSession, userInfo)
        )
      );
    };
    window.addEventListener("popstate", () => {
      lastPageUrl = window.location.href;
      trackPageVisit(() =>
        renderCookieConsentBanner(() =>
          handleConsentAcceptance(userSession, userInfo)
        ))
      } 
    );
    window.addEventListener("DOMContentLoaded", () => {
      lastPageUrl = window.location.href;
      trackPageVisit(() =>
        renderCookieConsentBanner(() =>
          handleConsentAcceptance(userSession, userInfo)
        ))
      }  
    );
    document.addEventListener("click", (e) => recordClickEvent(e, userSession, persistUserSession), true);
    transmitUserEvents(userSession);
    bootstrapTimer = setInterval(
      () => scheduleEventTransmission(userSession),
      1000
    );
  })();
}

module.exports = { insitella };