import { createRoot } from "react-dom/client";
import App from "./App";
import "./index.css";
import { QueryClientProvider } from "@tanstack/react-query";
import { queryClient } from "./lib/queryClient";
import { navigationManager } from "./utils/navigation";
import { register } from './utils/serviceWorkerRegistration';
import { capacitorNotifications } from './utils/capacitorNotifications';
import { shouldSkipRefreshNow } from './lib/noRefreshRegistry';

// Admin "Test as User" bootstrap. When this tab is launched as an iframe
// from /user-test with `?impersonate=<token>`, stash the token on a
// window-local variable (NOT sessionStorage — same-origin iframes share
// sessionStorage with their parent tab, which would leak the token back to
// the admin's window and make it impersonate itself). Strip the param from
// the URL, then globally wrap window.fetch so every same-origin /api/*
// request from inside the iframe carries `Authorization: Bearer <token>`
// with `credentials: 'omit'`. queryClient already does this for its own
// calls, but many components / hooks use raw `fetch(...)` directly —
// without this global patch they'd send the admin's session cookie and the
// server would reply with admin data, defeating the impersonation. Only
// honored inside an iframe — a pasted URL in a normal tab does nothing.
try {
  const _url = new URL(window.location.href);
  const _tok = _url.searchParams.get('impersonate');
  if (_tok && window.self !== window.top) {
    // Window-local — does NOT leak through the shared same-origin
    // sessionStorage back to the admin's parent tab.
    (window as any).__impersonationToken__ = _tok;
    _url.searchParams.delete('impersonate');
    window.history.replaceState({}, '', _url.toString());

    const _origFetch = window.fetch.bind(window);
    const _shouldImpersonate = (input: RequestInfo | URL): boolean => {
      try {
        const raw = typeof input === 'string'
          ? input
          : input instanceof URL ? input.href : (input as Request).url;
        const u = new URL(raw, window.location.origin);
        return u.origin === window.location.origin && u.pathname.startsWith('/api/');
      } catch { return false; }
    };
    window.fetch = ((input: RequestInfo | URL, init?: RequestInit) => {
      if (!_shouldImpersonate(input)) return _origFetch(input, init);
      const headers = new Headers(init?.headers || (input instanceof Request ? input.headers : undefined));
      headers.set('Authorization', `Bearer ${_tok}`);
      const merged: RequestInit = {
        ...(init || {}),
        credentials: 'omit',
        headers,
      };
      return _origFetch(input instanceof Request ? input.url : input, merged);
    }) as typeof window.fetch;

    console.info('[user-test] impersonation iframe armed (fetch patched)');
  }
} catch (e) {
  console.warn('[user-test] failed to read impersonate param:', e);
}

// Log app version for debugging deployment issues
try {
  const appVersion = document.querySelector('meta[name="app-version"]')?.getAttribute('content');
  const buildTimestamp = document.querySelector('meta[name="build-timestamp"]')?.getAttribute('content');
  console.log('[App] Version:', appVersion || 'unknown');
  console.log('[App] Build timestamp:', buildTimestamp || 'unknown');
  console.log('[App] Loaded at:', new Date().toISOString());
} catch (e) {
  console.error('[App] Error reading version metadata:', e);
}

// CRITICAL FIX: Handle chunk load failures after deployment
// When a new deployment replaces chunk files, lazy-loaded routes will fail to load
// This catches the error and auto-refreshes to get the new chunks
window.addEventListener('vite:preloadError', (event) => {
  if (shouldSkipRefreshNow()) {
    console.log('[NoRefresh] Skipping preload-error reload — page is in no-refresh list');
    return;
  }
  console.log('[App] Chunk load failed (new deployment detected), refreshing...', event);
  window.location.reload();
});

// PWA Version Check - Force update when new deployment is detected
// This runs in the background and only reloads if version mismatch is found
(async function checkAppVersion() {
  const VERSION_KEY = 'app_build_version';
  const LAST_CHECK_KEY = 'app_version_last_check';
  const CHECK_INTERVAL = 60 * 1000; // Don't check more than once per minute

  try {
    // Skip in development mode
    if (import.meta.env.DEV) {
      return;
    }

    // Throttle checks to avoid excessive API calls
    const lastCheck = localStorage.getItem(LAST_CHECK_KEY);
    if (lastCheck && Date.now() - parseInt(lastCheck) < CHECK_INTERVAL) {
      console.log('[Version] Skipping check (checked recently)');
      return;
    }

    // Fetch current server version
    const response = await fetch('/api/system/version', {
      cache: 'no-store',
      headers: { 'Cache-Control': 'no-cache' }
    });

    if (!response.ok) {
      console.warn('[Version] Failed to fetch version:', response.status);
      return;
    }

    const data = await response.json();
    const serverVersion = data.version;
    const cachedVersion = localStorage.getItem(VERSION_KEY);

    // Update last check timestamp
    localStorage.setItem(LAST_CHECK_KEY, Date.now().toString());

    console.log('[Version] Server:', serverVersion, '| Cached:', cachedVersion || 'none');

    // First time - just store the version
    if (!cachedVersion) {
      localStorage.setItem(VERSION_KEY, serverVersion);
      console.log('[Version] First visit, version stored');
      return;
    }

    // Version mismatch - new deployment detected!
    if (cachedVersion !== serverVersion) {
      // Store new version regardless — the next non-skipped page load picks it up.
      localStorage.setItem(VERSION_KEY, serverVersion);

      if (shouldSkipRefreshNow()) {
        console.log('[NoRefresh] Skipping version-mismatch reload — page is in no-refresh list. Version updated to', serverVersion);
        return;
      }

      console.log('[Version] New version detected! Clearing caches with real progress...');

      // Helper to dispatch progress updates
      const updateProgress = (progress: number, status: string) => {
        window.dispatchEvent(new CustomEvent('pwa-update-progress', {
          detail: { progress, status }
        }));
      };

      // Step 1: Start update (0-10%)
      updateProgress(5, 'Update detected');
      await new Promise(r => setTimeout(r, 100));

      // Step 2: Send service worker message (10-25%)
      updateProgress(15, 'Notifying service worker');
      if ('serviceWorker' in navigator && navigator.serviceWorker.controller) {
        navigator.serviceWorker.controller.postMessage({
          type: 'CLEAR_API_CACHE',
          urlPattern: '' // Clear all
        });
      }
      await new Promise(r => setTimeout(r, 100));
      updateProgress(25, 'Service worker notified');

      // Step 3: Clear browser caches (25-90%)
      if ('caches' in window) {
        const cacheNames = await caches.keys();
        const totalCaches = cacheNames.length || 1;

        updateProgress(30, `Clearing ${totalCaches} caches`);

        for (let i = 0; i < cacheNames.length; i++) {
          await caches.delete(cacheNames[i]);
          // Progress from 30% to 90% based on cache clearing
          const cacheProgress = 30 + Math.round(((i + 1) / totalCaches) * 60);
          updateProgress(cacheProgress, `Cleared cache ${i + 1}/${totalCaches}`);
        }

        console.log('[Version] Cleared', cacheNames.length, 'caches');
      } else {
        updateProgress(90, 'Cache API not available');
      }

      // Step 4: Finalize (90-100%)
      updateProgress(95, 'Finalizing update');
      await new Promise(r => setTimeout(r, 200));
      updateProgress(100, 'Update complete');

      // Small delay then reload
      await new Promise(r => setTimeout(r, 300));
      window.location.reload();
    }
  } catch (error) {
    console.warn('[Version] Check failed (non-critical):', error);
  }
})();

// Notification polyfill for iOS Safari
if (typeof Notification === 'undefined') {
  window.Notification = {
    permission: 'default',
    requestPermission: () => Promise.resolve('denied')
  } as any;
}

// Simple error boundary component
function ErrorBoundary({ children }: { children: React.ReactNode }) {
  try {
    return <>{children}</>;
  } catch (error) {
    return <div>Something went wrong. Please refresh the page.</div>;
  }
}

// Initialize app
const rootElement = document.getElementById("root");
if (!rootElement) {
  throw new Error("Root element not found");
}

createRoot(rootElement).render(
  <ErrorBoundary>
    <QueryClientProvider client={queryClient}>
      <App />
    </QueryClientProvider>
  </ErrorBoundary>
);

// Initialize Capacitor notifications for Android APK
capacitorNotifications.initialize().then((success) => {
  if (success) {
    console.log('[Capacitor] Native notifications initialized');
  }
}).catch(err => {
  console.log('[Capacitor] Not running in native environment:', err);
});

// CRITICAL FIX: Listen for service worker activation messages
// This handles automatic reload when new version is deployed
if ('serviceWorker' in navigator) {
  navigator.serviceWorker.addEventListener('message', async (event) => {
    if (event.data && event.data.type === 'SW_ACTIVATED') {
      console.log('[SW] New service worker activated, version:', event.data.version);

      // If the service worker asks us to reload (new deployment), show overlay with real progress
      if (event.data.action === 'reload') {
        if (shouldSkipRefreshNow()) {
          console.log('[NoRefresh] Skipping SW activation reload — page is in no-refresh list');
          return;
        }
        console.log('[SW] New version ready, clearing caches with real progress...');

        // Helper to dispatch progress updates
        const updateProgress = (progress: number, status: string) => {
          window.dispatchEvent(new CustomEvent('pwa-update-progress', {
            detail: { progress, status }
          }));
        };

        // Step 1: Start (0-20%)
        updateProgress(10, 'New version activated');
        await new Promise(r => setTimeout(r, 100));

        // Step 2: Clear caches (20-90%)
        if ('caches' in window) {
          const cacheNames = await caches.keys();
          const totalCaches = cacheNames.length || 1;
          updateProgress(25, `Clearing ${totalCaches} caches`);

          for (let i = 0; i < cacheNames.length; i++) {
            await caches.delete(cacheNames[i]);
            const cacheProgress = 25 + Math.round(((i + 1) / totalCaches) * 65);
            updateProgress(cacheProgress, `Cleared cache ${i + 1}/${totalCaches}`);
          }
        } else {
          updateProgress(90, 'Ready');
        }

        // Step 3: Finalize (90-100%)
        updateProgress(95, 'Finalizing');
        await new Promise(r => setTimeout(r, 200));
        updateProgress(100, 'Update complete');

        // Small delay then reload
        await new Promise(r => setTimeout(r, 300));
        window.location.reload();
      }
    }
  });
}

// Service Worker registration with auto-update support
// Register service worker with automatic reload on updates
register({
  autoReload: true, // Automatically reload when new version is available
  onSuccess: (registration) => {
    console.log('[SW] Service worker registered and ready');
  },
  onUpdate: (registration) => {
    console.log('[SW] New version detected, preparing to update...');
  },
  onUpdateReady: () => {
    console.log('[SW] New version ready, app will reload shortly...');
  }
});

// Load the Replit dev banner only when running in development
if (import.meta.env.DEV) {
  const banner = document.createElement('script');
  banner.src = 'https://replit.com/public/js/replit-dev-banner.js';
  document.body.appendChild(banner);
}
