/**
 * What an add-on gets from this app. admin.js is a
 * classic script named FlexatechTableReservationsAdmin (vite.config.ts), so everything
 * exported here sits on `window.FlexatechTableReservationsAdmin`. An add-on's own script
 * loads after it (PHP: the `fxtr_admin_enqueue` action) and:
 *
 *  - imports React, the JSX runtime and React Query from here, so there is
 *    one copy of each on the page,
 *  - adds a tab with registerTab(), served at #/bookings/<id>,
 *  - adds a panel to the reservation dialog with registerReservationPanel(),
 *  - adds a section to the top of the Reports tab with registerReportSection().
 *
 * The app draws first and picks up what is registered later.
 */
import * as React from "react";
import * as jsxRuntime from "react/jsx-runtime";
import * as ReactQuery from "@tanstack/react-query";
import { useSyncExternalStore, type ComponentType } from "react";
import type { DayReservation } from "@/lib/reservations";
import type { MountContext } from "@/lib/mounts";

export { React, jsxRuntime, ReactQuery };
export { Button, Card, Dialog, Field, Input, Notice, Select, Spinner, Textarea } from "@/components/ui";
export { ApiError, client, queryString, type Paged } from "@/lib/api";
export { cn } from "@/lib/cn";
export { formatDate, formatStamp, formatTime } from "@/lib/format";
export {
    reservationKeys,
    sourceLabel as reservationSourceLabel,
    statusLabel as reservationStatusLabel,
    type DayReservation,
    type GuestBadge,
    type ReservationSource,
    type ReservationStatus,
} from "@/lib/reservations";
export { TD, TH } from "@/features/Floor";

export interface TabProps {
    /** What follows the tab in the hash: "12" for #/bookings/guests/12. */
    path: string;
    /** Moves within the tab: navigate("12") opens #/bookings/<id>/12. */
    navigate: (path: string) => void;
    theme: MountContext["theme"];
}

export interface AddonTab {
    /** The hash segment. A built-in tab's name is refused. */
    id: string;
    label: string;
    Component: ComponentType<TabProps>;
    /** Shown only to users who may change the settings. */
    settings?: boolean;
}

export interface ReservationPanelProps {
    reservation: DayReservation;
}

export interface ReservationPanel {
    id: string;
    Component: ComponentType<ReservationPanelProps>;
}

export interface ReportSection {
    id: string;
    Component: ComponentType;
}

const RESERVED = ["", "list", "reservations", "reports", "floor", "shifts", "staff", "waitlist", "calendar", "settings", "setup"];

interface Registry {
    tabs: AddonTab[];
    panels: ReservationPanel[];
    reports: ReportSection[];
}

let registry: Registry = { tabs: [], panels: [], reports: [] };
const listeners = new Set<() => void>();

function publish(next: Registry): void {
    registry = next;
    listeners.forEach((listener) => listener());
}

/** Adds a tab after the built-in ones, or replaces the add-on's tab of the same id. */
export function registerTab(tab: AddonTab): void {
    if (RESERVED.includes(tab.id) || !/^[a-z][a-z0-9-]*$/.test(tab.id)) {
        console.warn(`Flexatech Table Reservations: "${tab.id}" cannot be a tab.`);
        return;
    }
    publish({ ...registry, tabs: [...registry.tabs.filter((t) => t.id !== tab.id), tab] });
}

/** Adds a panel under the details in the reservation dialog. */
export function registerReservationPanel(panel: ReservationPanel): void {
    publish({ ...registry, panels: [...registry.panels.filter((p) => p.id !== panel.id), panel] });
}

/** Adds a section to the top of the Reports tab, above Flexatech Table Reservations' own cards. */
export function registerReportSection(section: ReportSection): void {
    publish({ ...registry, reports: [...registry.reports.filter((r) => r.id !== section.id), section] });
}

function subscribe(listener: () => void): () => void {
    listeners.add(listener);

    return () => listeners.delete(listener);
}

/** What add-ons have registered so far. */
export function addons(): Registry {
    return registry;
}

/** addons(), redrawing the component when it changes. */
export function useAddons(): Registry {
    return useSyncExternalStore(subscribe, addons);
}

/** "guests/12?x=1" → { id: "guests", path: "12?x=1" }. */
export function splitTab(path: string): { id: string; path: string } {
    const [head = "", query] = path.split("?");
    const [id = "", ...rest] = head.split("/").filter(Boolean);
    const inner = rest.join("/");

    return { id, path: query === undefined ? inner : `${inner}?${query}` };
}
