const MANAGED_CATALOG_CACHE_PREFIX = 'radioplayer-managed-catalog-';
const MANAGED_CATALOG_SOURCE_HEADER = 'x-radioplayer-managed-source';

import {
  loadPersistedManagedStationCatalog,
  persistManagedStationCatalog,
} from '../storage/stationCatalogPersistence.js';

export type ManagedCatalogSource = 'remote' | 'cached-remote' | 'bundled' | 'unknown';

let lastManagedCatalogSource: ManagedCatalogSource = 'unknown';

type ManagedCatalogEnvelope = {
  stations?: unknown;
};

async function loadManagedCatalogFromCache(catalogUrl: string): Promise<Response | null> {
  if (typeof window === 'undefined' || !('caches' in window)) {
    return null;
  }

  try {
    const cacheKeys = await caches.keys();
    const managedCacheName = cacheKeys.find((cacheName) => cacheName.startsWith(MANAGED_CATALOG_CACHE_PREFIX));
    if (!managedCacheName) {
      return null;
    }

    const managedCache = await caches.open(managedCacheName);
    return await managedCache.match(catalogUrl) ?? null;
  } catch {
    return null;
  }
}

function normalizeManagedStationsPayload(payload: unknown): unknown[] {
  if (Array.isArray(payload)) {
    return payload;
  }

  if (payload && typeof payload === 'object' && Array.isArray((payload as ManagedCatalogEnvelope).stations)) {
    return (payload as ManagedCatalogEnvelope).stations as unknown[];
  }

  return [];
}

function setLastManagedCatalogSource(source: ManagedCatalogSource) {
  lastManagedCatalogSource = source;
}

function normalizeManagedCatalogSource(source: unknown): ManagedCatalogSource {
  return source === 'remote' || source === 'cached-remote' || source === 'bundled'
    ? source
    : 'unknown';
}

export function getLastManagedCatalogSource(): ManagedCatalogSource {
  return lastManagedCatalogSource;
}

export async function loadPersistedManagedStations(): Promise<unknown[] | null> {
  const persisted = await loadPersistedManagedStationCatalog();
  if (!persisted) {
    return null;
  }

  setLastManagedCatalogSource(normalizeManagedCatalogSource(persisted.source));
  return normalizeManagedStationsPayload(persisted.stations);
}

export async function loadManagedStations(): Promise<unknown[]> {
  const remoteCatalogUrl = `${import.meta.env.BASE_URL}api/managed-stations.json`;
  const bundledCatalogUrl = `${import.meta.env.BASE_URL}stations.json`;
  const hasServiceWorkerController = typeof navigator !== 'undefined'
    && 'serviceWorker' in navigator
    && Boolean(navigator.serviceWorker.controller);

  let response = null;

  if (hasServiceWorkerController) {
    response = await loadManagedCatalogFromCache(remoteCatalogUrl);
    if (response?.ok) {
      setLastManagedCatalogSource('cached-remote');
    }
  }

  if (!response?.ok) {
    try {
      response = await fetch(remoteCatalogUrl);
      if (response?.ok) {
        const responseSource = response.headers.get(MANAGED_CATALOG_SOURCE_HEADER);
        setLastManagedCatalogSource(
          responseSource === 'remote' || responseSource === 'cached-remote' || responseSource === 'bundled'
            ? responseSource
            : 'remote',
        );
      }
    } catch {
      response = null;
    }
  }

  if (!response?.ok) {
    response = await fetch(bundledCatalogUrl);
    if (response?.ok) {
      setLastManagedCatalogSource('bundled');
    }
  }

  if (!response.ok) {
    setLastManagedCatalogSource('unknown');
    throw new Error(`Failed to load managed stations: ${response.status}`);
  }

  const stations = normalizeManagedStationsPayload(await response.json());
  await persistManagedStationCatalog(stations, getLastManagedCatalogSource());
  return stations;
}