/* eslint-disable import/first */
import React from 'react';
import { createRoot } from 'react-dom/client';
import { enableMapSet } from 'immer';
import '@fontsource/inter/variable.css';
import '@fontsource-variable/jetbrains-mono/index.css';
import 'folds/dist/style.css';
import { configClass, varsClass } from 'folds';

enableMapSet();

import './app/styles/global.css';
import './index.css';

import { trimTrailingSlash } from './app/utils/common';
import App from './app/pages/App';

// import i18n (needs to be bundled ;))
import './app/i18n';
import { pushSessionToSW } from './sw-session';
import { getFallbackSession } from './app/state/sessions';
import { setupExternalLinkHandler } from './app/utils/capacitor';
import { setupExternalLinkHandler as setupElectronExternalLinkHandler } from './app/utils/electron';
import { proxy } from './app/plugins/proxy';

document.body.classList.add(configClass, varsClass);
setupExternalLinkHandler();
setupElectronExternalLinkHandler();

// Input-mode detector for hover/focus styling. Capacitor's Android Chromium
// WebView synthesises `:hover` and `:focus-visible` on the focused element
// after a tap and never clears them until the next interaction elsewhere,
// so without a gate every tap leaves a sticky highlight on the tapped
// button. The widget-telegram bundle uses the same pattern — see
// `apps/widget-telegram/src/main.tsx`. matchMedia interaction queries are
// unreliable on Android WebView (`hover: hover` reports TRUE on a pure-touch
// device); the only honest signal is `pointerdown.pointerType`, which the
// WebView reports truthfully. Initial mode is 'mouse' so desktop/hybrid
// users get hover affordances on first paint — a touch user cannot trigger
// hover before tapping, and our capture-phase listener moves the attribute
// to 'touch' in the same frame as any synthesised :hover would paint.
const setVojoInputMode = (mode: 'touch' | 'mouse'): void => {
  document.documentElement.dataset.input = mode;
};
setVojoInputMode('mouse');
window.addEventListener(
  'pointerdown',
  (event) => {
    setVojoInputMode(event.pointerType === 'mouse' ? 'mouse' : 'touch');
  },
  { passive: true, capture: true }
);

// Register Service Worker
if ('serviceWorker' in navigator) {
  const swUrl =
    import.meta.env.MODE === 'production'
      ? `${trimTrailingSlash(import.meta.env.BASE_URL)}/sw.js`
      : `/dev-sw.js?dev-sw`;

  const sendSessionToSW = () => {
    const session = getFallbackSession();
    pushSessionToSW(session?.baseUrl, session?.accessToken);
  };

  navigator.serviceWorker.register(swUrl).then(sendSessionToSW);
  navigator.serviceWorker.ready.then(sendSessionToSW);

  navigator.serviceWorker.addEventListener('message', (ev) => {
    const { type } = ev.data ?? {};

    if (type === 'requestSession') {
      sendSessionToSW();
      return;
    }

    if (type === 'notificationClick') {
      const { roomId, isInvite } = ev.data ?? {};
      window.dispatchEvent(new CustomEvent('vojo:pushNavigate', { detail: { roomId, isInvite } }));
      return;
    }

    if (type === 'pushSubscriptionChange') {
      window.dispatchEvent(new CustomEvent('vojo:pushSubscriptionChange'));
    }
  });
}

const mountApp = () => {
  const rootContainer = document.getElementById('root');

  if (rootContainer === null) {
    // Bootstrap failure — only surfaces if `index.html` is broken; logging
    // is the only path here since React hasn't mounted yet.
    // eslint-disable-next-line no-console
    console.error('Root container element not found!');
    return;
  }

  const root = createRoot(rootContainer);
  root.render(<App />);
};

// Apply a stored (enabled) Android proxy override BEFORE React boots, so the
// very first homeserver fetch (auto-discovery / login / sync) already rides
// the proxy on a blocked network (proxy_android_impl.md §2, F1). On a
// logged-in boot we narrow the override to the homeserver host immediately;
// pre-login it routes everything until the host is resolved. No-op on web /
// Electron, and time-bounded so a misbehaving bridge can never hang boot.
const applyProxyAtBoot = async (): Promise<void> => {
  let host: string | undefined;
  try {
    const session = getFallbackSession();
    host = session?.baseUrl ? new URL(session.baseUrl).hostname : undefined;
  } catch {
    host = undefined;
  }
  await Promise.race([
    proxy.applyAtBoot(host),
    new Promise<void>((resolve) => {
      setTimeout(resolve, 2500);
    }),
  ]).catch(() => undefined);
};

applyProxyAtBoot().finally(mountApp);
