// SPDX-FileCopyrightText: 2026 Stan Grams <sjg@haxx.space>
//
// SPDX-License-Identifier: GPL-2.0-or-later

export interface DecoderDescriptor {
  id: string;
  label: string;
  activation: string;
  active_modes: string[];
  background_decode: boolean;
  bookmark_selectable: boolean;
}

interface DecoderRegistryBridge {
  decoderRegistry?: DecoderDescriptor[];
  onDecoderRegistryReady?: (callback: () => void) => void;
}

const bridge = window as DecoderRegistryBridge;
const readyCallbacks: Array<() => void> = [];

export let decoderRegistry: DecoderDescriptor[] = [];

export function onDecoderRegistryReady(callback: () => void): void {
  if (decoderRegistry.length > 0) callback();
  else readyCallbacks.push(callback);
}

export function applyDecoderRegistryVisibility(): void {
  const knownIds = new Set(decoderRegistry.map(({ id }) => id));
  const alwaysShow = new Set(["overview", "rds", "sat"]);

  document.querySelectorAll<HTMLElement>(
    "#tab-digital-modes > .sub-tab-bar > .sub-tab[data-subtab]",
  ).forEach((button) => {
    const id = button.dataset.subtab;
    if (!id || alwaysShow.has(id) || knownIds.has(id)) return;
    button.style.display = "none";
    const panel = document.getElementById(`subtab-${id}`);
    if (panel) panel.style.display = "none";
  });

  document.querySelectorAll<HTMLElement>('[id^="about-dec-"]').forEach((element) => {
    const id = element.id.replace("about-dec-", "");
    if (alwaysShow.has(id) || knownIds.has(id)) return;
    const row = element.closest<HTMLElement>("tr");
    if (row) row.style.display = "none";
  });

  document.querySelectorAll<HTMLElement>('[id^="settings-clear-"][id$="-history"]')
    .forEach((element) => {
      const match = /^settings-clear-(.+)-history$/.exec(element.id);
      const id = match?.[1];
      if (id && !alwaysShow.has(id) && !knownIds.has(id)) element.style.display = "none";
    });

  document.querySelectorAll<HTMLElement>("#subtab-overview .plugin-item[data-decoder]")
    .forEach((element) => {
      const id = element.dataset.decoder;
      if (id && !alwaysShow.has(id) && !knownIds.has(id)) element.style.display = "none";
    });
}

function isDecoderDescriptor(value: unknown): value is DecoderDescriptor {
  if (typeof value !== "object" || value === null) return false;
  const item = value as Partial<DecoderDescriptor>;
  return typeof item.id === "string"
    && typeof item.label === "string"
    && typeof item.activation === "string"
    && Array.isArray(item.active_modes)
    && item.active_modes.every((mode) => typeof mode === "string")
    && typeof item.background_decode === "boolean"
    && typeof item.bookmark_selectable === "boolean";
}

function decodeRegistry(value: unknown): DecoderDescriptor[] {
  if (!Array.isArray(value) || !value.every(isDecoderDescriptor)) {
    throw new TypeError("The decoder registry response is malformed");
  }
  return value;
}

export async function loadDecoderRegistry(onLoaded: () => void): Promise<void> {
  try {
    const response = await fetch("/decoders");
    if (!response.ok) return;
    decoderRegistry = decodeRegistry(await response.json());
    bridge.decoderRegistry = decoderRegistry;
    readyCallbacks.splice(0).forEach((callback) => { callback(); });
    applyDecoderRegistryVisibility();
    onLoaded();
  } catch (error: unknown) {
    console.error("Failed to fetch decoder registry:", error);
  }
}

// Temporary compatibility boundary for lazy modules that have not yet been
// changed to receive the registry service through their plugin context.
bridge.decoderRegistry = decoderRegistry;
bridge.onDecoderRegistryReady = onDecoderRegistryReady;
