import { createContext, useContext } from 'react';

export type HashRouterConfig = {
  enabled?: boolean;
  basename?: string;
};

export type PushConfig = {
  vapidPublicKey: string;
  gatewayUrl: string;
  webAppId?: string;
  fcmAppId?: string;
};

export type BotConfig = {
  id?: string;
  mxid?: string;
  name?: string;
  /** Optional operator override of the localized description. Falls back
   * to i18n key `Bots.description.<id>` when absent. */
  description?: string;
  experience?: {
    /** `'matrix-widget'` (bridge bots — sandboxed iframe) or `'ai-chat'` (first-party
     * assistant bots — native in-client conversation surface, no widget). See
     * catalog.ts `normalizeBotExperience`. */
    type?: string;
    /** Widget iframe URL. Required for `'matrix-widget'`; absent for `'ai-chat'`. */
    url?: string;
    commandPrefix?: string;
    /** Base URL of the bridge's provisioning HTTP API as exposed by the
     * reverse proxy (e.g. `https://vojo.chat/_provision/telegram`). When
     * present (and valid per catalog.ts `normalizeProvisioningUrl`) it is
     * forwarded to the widget, which then drives the bridge over JSON
     * endpoints instead of bot text commands. Requires the `vojo.openid`
     * capability so the widget can authenticate. */
    provisioningUrl?: string;
    /** Declarative opt-in to elevated host verbs (e.g. `vojo.add_to_chat`).
     * Validated against an allowlist at load (catalog.ts `normalizeBotCaps`);
     * unknown entries are dropped, absent ⇒ `[]`. config.json is a trusted,
     * operator-controlled root — the widget cannot grant itself a capability. */
    capabilities?: string[];
  };
};

export type ChannelsConfig = {
  /** Default true. When false, M4 per-thread unread machinery (badges,
   * thread-aware receipt handler, drawer markAsRead) falls back to
   * pre-M4 blind-DELETE semantics. `threadSupport: true` stays on
   * unconditionally — drawer (M2) reads `room.getThread()` and
   * ThreadEvent emitters which silently NO-OP without it. */
  unreadThreading?: boolean;
};

export type ClientConfig = {
  defaultHomeserver?: number;
  homeserverList?: string[];
  allowCustomHomeservers?: boolean;

  featuredCommunities?: {
    openAsDefault?: boolean;
    spaces?: string[];
    rooms?: string[];
    servers?: string[];
  };

  hashRouter?: HashRouterConfig;

  push?: PushConfig;

  bots?: BotConfig[];

  channels?: ChannelsConfig;
};

export const isUnreadThreadingEnabled = (clientConfig: ClientConfig): boolean =>
  clientConfig.channels?.unreadThreading !== false;

const ClientConfigContext = createContext<ClientConfig | null>(null);

export const ClientConfigProvider = ClientConfigContext.Provider;

export function useClientConfig(): ClientConfig {
  const config = useContext(ClientConfigContext);
  if (!config) throw new Error('Client config are not provided!');
  return config;
}

// Single source of truth for the M4 kill switch. Default true (absent
// config = enabled). Consumers should call this hook instead of reading
// `useClientConfig()` + `isUnreadThreadingEnabled(...)` separately —
// keeps the predicate in one place and lets future telemetry / runtime
// flips slot in transparently.
export function useUnreadThreadingEnabled(): boolean {
  return isUnreadThreadingEnabled(useClientConfig());
}

export const clientDefaultServer = (clientConfig: ClientConfig): string =>
  clientConfig.homeserverList?.[clientConfig.defaultHomeserver ?? 0] ?? 'matrix.org';

export const clientAllowedServer = (clientConfig: ClientConfig, server: string): boolean => {
  const { homeserverList, allowCustomHomeservers } = clientConfig;

  if (allowCustomHomeservers) return true;

  return homeserverList?.includes(server) === true;
};
