import React from 'react';
import { createRoot } from 'react-dom/client';
import App from './App.jsx';
import serviceWorkerConfig from './shared/serviceWorkerConfig.json';
import './styles.css';

const APP_BUILD_ID = __APP_BUILD_ID__;

const hadServiceWorkerControllerAtLoad = 'serviceWorker' in navigator
  ? Boolean(navigator.serviceWorker.controller)
  : false;
let hasReloadedForServiceWorkerUpdate = false;
const STATION_SYNC_TAG = serviceWorkerConfig.stationSyncTag;
const STATION_PERIODIC_SYNC_TAG = serviceWorkerConfig.stationPeriodicSyncTag;
const STATION_PERIODIC_SYNC_INTERVAL = serviceWorkerConfig.stationPeriodicSyncIntervalMs;
const MANAGED_CATALOG_PERIODIC_SYNC_TAG = serviceWorkerConfig.managedCatalogPeriodicSyncTag;
const MANAGED_CATALOG_PERIODIC_SYNC_INTERVAL = serviceWorkerConfig.managedCatalogPeriodicSyncIntervalMs;
const STATION_SYNC_CACHE_PREFIX = serviceWorkerConfig.stationSyncCachePrefix;
const STATION_SYNC_META_URL = `${import.meta.env.BASE_URL}${serviceWorkerConfig.stationSyncMetaPath}`;

async function shouldScheduleImmediateStationSync() {
  if (!('caches' in window)) {
    return true;
  }

  try {
    const cacheKeys = await caches.keys();
    const syncCacheName = cacheKeys.find((cacheName) => cacheName.startsWith(STATION_SYNC_CACHE_PREFIX));
    if (!syncCacheName) {
      return true;
    }

    const syncCache = await caches.open(syncCacheName);
    const cachedMeta = await syncCache.match(STATION_SYNC_META_URL);
    if (!cachedMeta?.ok) {
      return true;
    }

    const payload = await cachedMeta.json();
    const syncedAt = typeof payload?.syncedAt === 'string'
      ? Date.parse(payload.syncedAt)
      : Number.NaN;

    if (!Number.isFinite(syncedAt)) {
      return true;
    }

    return (Date.now() - syncedAt) >= STATION_PERIODIC_SYNC_INTERVAL;
  } catch (error) {
    console.debug('Station sync freshness check failed:', error);
    return true;
  }
}

async function registerStationCatalogSync(registration) {
  if (!registration) {
    return;
  }

  if ('sync' in registration) {
    try {
      if (await shouldScheduleImmediateStationSync()) {
        await registration.sync.register(STATION_SYNC_TAG);
        console.debug('ServiceWorker background sync registered:', STATION_SYNC_TAG);
      } else {
        console.debug('ServiceWorker background sync skipped: cached station catalog is fresh.');
      }
    } catch (error) {
      console.debug('ServiceWorker background sync registration failed:', error);
    }
  }

  if ('periodicSync' in registration) {
    try {
      await registration.periodicSync.register(STATION_PERIODIC_SYNC_TAG, {
        minInterval: STATION_PERIODIC_SYNC_INTERVAL,
      });
      console.debug('ServiceWorker periodic sync registered:', STATION_PERIODIC_SYNC_TAG);
    } catch (error) {
      console.debug('ServiceWorker periodic sync registration failed:', error);
    }

    try {
      await registration.periodicSync.register(MANAGED_CATALOG_PERIODIC_SYNC_TAG, {
        minInterval: MANAGED_CATALOG_PERIODIC_SYNC_INTERVAL,
      });
      console.debug('ServiceWorker periodic sync registered:', MANAGED_CATALOG_PERIODIC_SYNC_TAG);
    } catch (error) {
      console.debug('ServiceWorker managed catalog periodic sync registration failed:', error);
    }
  }
}

function setupServiceWorker() {
  if (!('serviceWorker' in navigator)) {
    return;
  }

  if (import.meta.env.DEV) {
    navigator.serviceWorker.getRegistrations()
      .then((registrations) => Promise.all(registrations.map((reg) => reg.unregister())))
      .catch((err) => console.debug('ServiceWorker dev cleanup failed:', err));

    if ('caches' in window) {
      caches.keys()
        .then((keys) => Promise.all(keys.map((key) => caches.delete(key))))
        .catch((err) => console.debug('Cache dev cleanup failed:', err));
    }

    return;
  }

  const swUrl = `${import.meta.env.BASE_URL}sw.js?v=${encodeURIComponent(APP_BUILD_ID)}`;
  navigator.serviceWorker.register(swUrl, { updateViaCache: 'none' })
    .then(async (reg) => {
      try {
        await reg.update();
      } catch (e) {
        console.debug('ServiceWorker update check failed:', e);
      }
      await registerStationCatalogSync(reg);
      console.log('ServiceWorker registered:', reg.scope);
    })
    .catch((err) => console.debug('ServiceWorker registration failed:', err));

  window.addEventListener('online', () => {
    navigator.serviceWorker.ready
      .then((reg) => registerStationCatalogSync(reg))
      .catch((error) => console.debug('ServiceWorker sync scheduling failed:', error));
  });

  navigator.serviceWorker.addEventListener('controllerchange', () => {
    if (!hadServiceWorkerControllerAtLoad || hasReloadedForServiceWorkerUpdate) {
      return;
    }

    hasReloadedForServiceWorkerUpdate = true;
    window.location.reload();
  });
}

setupServiceWorker();

const rootEl = document.getElementById('root');
const root = createRoot(rootEl);

root.render(
  <React.StrictMode>
    <App />
  </React.StrictMode>,
);

document.getElementById('app-loading')?.remove();

import('./player.js')
  .then(({ initPlayer }) => initPlayer())
  .catch((err) => {
    console.error('Failed to initialise player controller', err);
  });
