import React, { ReactNode } from 'react';
import { RouteObject } from 'react-router';
import * as react_jsx_runtime from 'react/jsx-runtime';

/**
 * Result type indicating how the app was mounted
 * - "hydrated": App was hydrated over existing SSR/SSG content
 * - "rendered": App was rendered fresh (SPA mode)
 * - "not_found": Container element was not found in the DOM
 */
type MountAppResult = 'hydrated' | 'rendered' | 'not_found';
/**
 * Options for mounting the app
 */
type MountAppOptions = {
    /**
     * Whether to wrap the app element with React.StrictMode
     * @default true
     */
    strictMode?: boolean;
    /**
     * Optional custom wrapper component for additional providers
     * Applied after UnirendHeadProvider but before StrictMode (StrictMode is always outermost)
     * Must be a React component that accepts children
     */
    rootProviders?: React.ComponentType<{
        children: React.ReactNode;
    }>;
};
/**
 * Intelligently mounts a React Router-based app by detecting whether to hydrate or render.
 *
 * This is the primary function for client-side mounting in unirend. It provides a unified,
 * opinionated API that works seamlessly across different rendering contexts:
 * - SSR/SSG: Hydrates pre-rendered HTML content
 * - SPA: Creates a fresh root and renders the app
 *
 * The detection is based on whether the container already has child elements,
 * which indicates pre-rendered content that should be hydrated rather than replaced.
 *
 * @param containerID - The ID of the root DOM element (e.g., "root", "app")
 * @param routes - Your React Router routes configuration
 * @param options - Optional configuration for mounting behavior
 * @returns MountAppResult indicating the mounting strategy used or if it failed
 *
 * @example
 * ```typescript
 * import { mountApp } from 'unirend/client';
 * import { routes } from './Routes';
 *
 * const result = mountApp('root', routes);
 *
 * // With custom providers
 * const customWrapper = (node) => (
 *   <ThemeProvider>
 *     <StateProvider>
 *       {node}
 *     </StateProvider>
 *   </ThemeProvider>
 * );
 *
 * const result = mountApp('root', routes, { wrapApp: customWrapper });
 *
 * if (result === 'hydrated') {
 *   console.log('Hydrated SSR content');
 * } else if (result === 'rendered') {
 *   console.log('Rendered as SPA');
 * } else {
 *   console.error('Failed to mount app');
 * }
 * ```
 */
declare function mountApp(containerID: string, routes: RouteObject[], options?: MountAppOptions): MountAppResult;

/**
 * Parsed domain information derived from a request hostname.
 *
 * Used both on the Fastify request object (`request.domainInfo`) and in the
 * Unirend React context (`useDomainInfo()`).
 */
interface DomainInfo {
    /** Bare hostname with port stripped (IPv6-safe, e.g. `'app.example.com'` or `'::1'`). */
    hostname: string;
    /**
     * Apex domain without a leading dot (e.g. `'example.com'`).
     * Empty string for localhost and raw IP addresses where no root domain can be resolved.
     *
     * When empty, omit the `domain` attribute entirely — `domain=.localhost` is invalid
     * per RFC 6265 and browsers reject it. A cookie without a `domain` attribute becomes
     * a host-only cookie scoped to the exact hostname, which is correct for localhost and IPs.
     *
     * Prepend `.` when using as a cookie `domain` attribute to span subdomains:
     * ```ts
     * document.cookie = [
     *   'theme=dark',
     *   'path=/',
     *   'max-age=31536000',
     *   domainInfo.rootDomain ? `domain=.${domainInfo.rootDomain}` : null,
     * ].filter(Boolean).join('; ');
     * ```
     */
    rootDomain: string;
}

/**
 * Render mode type - SSR, SSG, or Client
 * - "ssr": Server-Side Rendering (runtime server rendering)
 * - "ssg": Static Site Generation (build-time server rendering)
 * - "client": Client-side execution (SPA or after a SSG build/SSR page hydration occurs)
 */
type UnirendRenderMode = 'ssr' | 'ssg' | 'client';
/**
 * Unirend context value type
 */
interface UnirendContextValue {
    /**
     * The render mode:
     * - 'ssr': Server-Side Rendering
     * - 'ssg': Static Site Generation
     * - 'client': Client-side execution (SPA or after a SSG build/SSR page hydration occurs)
     */
    renderMode: UnirendRenderMode;
    /**
     * Whether the app is running in development mode
     */
    isDevelopment: boolean;
    /**
     * The Fetch API Request object (available during SSR/SSG rendering)
     * Undefined on client-side after hydration
     */
    fetchRequest?: Request;
    /**
     * Public application configuration
     * This is a frozen (immutable) copy of the config passed to the server
     * Available on both server and client (injected into HTML during SSR/SSG)
     */
    publicAppConfig?: Record<string, unknown>;
    /**
     * CDN base URL for asset serving (e.g. 'https://cdn.example.com')
     * Available on both server (from app config or per-request override) and client
     * (read from window.__CDN_BASE_URL__ injected into HTML by the server)
     * Empty string when no CDN is configured
     */
    cdnBaseURL?: string;
    /**
     * Domain information computed server-side from the request hostname.
     * Available during SSR (computed per-request) and SSG when a `hostname` option is
     * provided at build time. `null` when hostname is not known (SSG without hostname
     * configured, or pure SPA — no server to compute it via the public suffix list).
     */
    domainInfo?: DomainInfo | null;
    /**
     * Request context revision counter for reactivity
     * Format: `${timestamp}-${counter}` (e.g., "1729123456789-0", "1729123456789-1")
     * Increments whenever request context is modified to trigger re-renders
     * @internal
     */
    requestContextRevision?: string;
}
/**
 * Request context management interface
 */
interface RequestContextManager {
    /**
     * Get a value from the request context
     * @param key - The key to retrieve
     * @returns The value associated with the key, or undefined if not found
     */
    get: (key: string) => unknown;
    /**
     * Set a value in the request context
     * @param key - The key to set
     * @param value - The value to associate with the key
     */
    set: (key: string, value: unknown) => void;
    /**
     * Check if a key exists in the request context
     * @param key - The key to check
     * @returns true if the key exists, false otherwise
     */
    has: (key: string) => boolean;
    /**
     * Delete a value from the request context
     * @param key - The key to delete
     * @returns true if the key existed and was deleted, false if it didn't exist
     */
    delete: (key: string) => boolean;
    /**
     * Clear all values from the request context
     * @returns The number of keys that were cleared
     */
    clear: () => number;
    /**
     * Get all keys from the request context
     * @returns An array of all keys
     */
    keys: () => string[];
    /**
     * Get the number of entries in the request context
     * @returns The number of key-value pairs
     */
    size: () => number;
}

/**
 * Hook to check if the app is rendering in SSR mode
 *
 * @returns true if rendering mode is 'ssr', false if 'ssg'
 *
 * @example
 * ```tsx
 * function MyComponent() {
 *   const isSSR = useIsSSR();
 *
 *   return <div>{isSSR ? 'Server-Side Rendered' : 'Static Generated'}</div>;
 * }
 * ```
 */
declare function useIsSSR(): boolean;
/**
 * Hook to check if the app is rendering in SSG mode
 *
 * @returns true if rendering mode is 'ssg', false otherwise
 *
 * @example
 * ```tsx
 * function MyComponent() {
 *   const isSSG = useIsSSG();
 *
 *   return <div>{isSSG ? 'Static Generated' : 'Not SSG'}</div>;
 * }
 * ```
 */
declare function useIsSSG(): boolean;
/**
 * Hook to check if the app is in client mode
 * Returns true for SPAs or after SSG build/SSR page hydration occurs
 *
 * @returns true if rendering mode is 'client', false otherwise
 *
 * @example
 * ```tsx
 * function MyComponent() {
 *   const isClient = useIsClient();
 *
 *   return <div>{isClient ? 'Client Mode' : 'Server Rendering'}</div>;
 * }
 * ```
 */
declare function useIsClient(): boolean;
/**
 * Hook to get the render mode
 *
 * @returns The current render mode ('ssr', 'ssg', or 'client')
 *
 * @example
 * ```tsx
 * function MyComponent() {
 *   const renderMode = useRenderMode();
 *
 *   return <div>Render Mode: {renderMode}</div>;
 * }
 * ```
 */
declare function useRenderMode(): UnirendRenderMode;
/**
 * Hook to check if the app is running in development mode
 *
 * @returns true if in development mode, false if in production
 *
 * @example
 * ```tsx
 * function MyComponent() {
 *   const isDev = useIsDevelopment();
 *
 *   return (
 *     <div>
 *       {isDev && <div>Development Mode - Debug Info</div>}
 *     </div>
 *   );
 * }
 * ```
 */
declare function useIsDevelopment(): boolean;
/**
 * Hook to check if the code is running on the server (SSR)
 * This checks if fetchRequest has the SSRHelper property attached
 *
 * @returns true if on SSR server (has SSRHelper), false if on client or SSG
 *
 * @example
 * ```tsx
 * function MyComponent() {
 *   const isServer = useIsServer();
 *
 *   return (
 *     <div>
 *       {isServer ? 'Running on SSR server' : 'Running on client or SSG'}
 *     </div>
 *   );
 * }
 * ```
 */
declare function useIsServer(): boolean;
/**
 * Hook to access public application configuration
 * This is a frozen (immutable) copy of the config passed to the server
 * Available on both server and client
 *
 * @returns The public app config object, or undefined if not provided
 *
 * @example
 * ```tsx
 * function MyComponent() {
 *   const config = usePublicAppConfig();
 *
 *   if (!config) {
 *     return <div>No config available</div>;
 *   }
 *
 *   return (
 *     <div>
 *       <p>API URL: {config.api_endpoint}</p>
 *       <p>App Name: {config.appName}</p>
 *     </div>
 *   );
 * }
 * ```
 */
declare function usePublicAppConfig(): Record<string, unknown> | undefined;
/**
 * Hook to get the CDN base URL configured for asset serving.
 *
 * Returns the effective CDN base URL for the current request — either a per-request
 * override (set in middleware via `request.CDNBaseURL`) or the app-level default
 * (`CDNBaseURL` in `serveSSRProd`/`registerProdApp` options). Returns an empty string
 * when no CDN is configured or when running Vite directly without the unirend server.
 *
 * Available on both server (during SSR rendering) and client (read from
 * `window.__CDN_BASE_URL__` injected into the page).
 *
 * @example
 * ```tsx
 * function AssetImage({ path }: { path: string }) {
 *   const cdnBase = useCDNBaseURL();
 *
 *   return <img src={`${cdnBase}${path}`} />;
 * }
 * ```
 */
declare function useCDNBaseURL(): string;
/**
 * Returns domain information computed server-side from the request hostname.
 *
 * - `hostname`: the bare requested hostname (port stripped), e.g. `'app.example.com'`
 * - `rootDomain`: the apex domain without a leading dot, e.g. `'example.com'`.
 *   Empty string for localhost / IP addresses.
 *   Prepend `.` when using as a cookie `domain` attribute to span subdomains:
 *   ```ts
 *   document.cookie = [
 *     'theme=dark',
 *     'path=/',
 *     'max-age=31536000',
 *     domainInfo?.rootDomain ? `domain=.${domainInfo.rootDomain}` : null,
 *   ].filter(Boolean).join('; ');
 *   ```
 *
 * Returns `null` when hostname is not known — SSG without a `hostname` option
 * configured, or pure SPA (no server to compute it via the public suffix list).
 *
 * @example
 * ```tsx
 * function ThemeProvider({ children }) {
 *   const domainInfo = useDomainInfo();
 *   // domainInfo?.hostname  → 'app.example.com'
 *   // domainInfo?.rootDomain → 'example.com'
 * }
 * ```
 */
declare function useDomainInfo(): DomainInfo | null;
/**
 * Hook to get the raw request context object for debugging purposes.
 * Returns a cloned, immutable copy of the entire request context.
 *
 * **Note:** This is primarily for debugging. Use `useRequestContextValue()`
 * or `useRequestContext()` for production code.
 *
 * @returns A cloned copy of the request context object, or undefined if not populated
 *
 * @example
 * ```tsx
 * function DebugPanel() {
 *   const rawContext = useRequestContextObjectRaw();
 *
 *   if (!rawContext) {
 *     return <div>Request context not populated</div>;
 *   }
 *
 *   return (
 *     <pre>{JSON.stringify(rawContext, null, 2)}</pre>
 *   );
 * }
 * ```
 */
declare function useRequestContextObjectRaw(): Record<string, unknown> | undefined;
/**
 * Hook to access and manage the request context
 *
 * Returns an object with methods to get, set, check, delete, and inspect
 * the request context. The returned methods can be safely called in callbacks,
 * effects, or event handlers.
 *
 * @returns RequestContextManager object with context management methods
 *
 * @example
 * ```tsx
 * function MyComponent() {
 *   const requestContext = useRequestContext();
 *
 *   const handleThemeChange = (theme: string) => {
 *     requestContext.set('theme', theme);
 *   };
 *
 *   const userID = requestContext.get('userID');
 *   const hasTheme = requestContext.has('theme');
 *   const allKeys = requestContext.keys();
 *
 *   return (
 *     <div>
 *       <p>User ID: {userID}</p>
 *       <p>Has theme: {hasTheme ? 'Yes' : 'No'}</p>
 *       <p>Total entries: {requestContext.size()}</p>
 *       <button onClick={() => handleThemeChange('dark')}>Dark Theme</button>
 *       <button onClick={() => requestContext.clear()}>Clear All</button>
 *     </div>
 *   );
 * }
 * ```
 */
declare function useRequestContext(): RequestContextManager;
/**
 * Hook to access and reactively update a single request context value
 *
 * Similar to useState, this hook returns a tuple of [value, setValue] and will
 * cause the component to re-render when the value changes.
 *
 * @param key - The key to track in the request context
 * @returns A tuple of [value, setValue] similar to useState
 *
 * @example
 * ```tsx
 * function ThemeToggle() {
 *   const [theme, setTheme] = useRequestContextValue<string>('theme');
 *
 *   return (
 *     <div>
 *       <p>Current theme: {theme || 'default'}</p>
 *       <button onClick={() => setTheme('dark')}>Dark</button>
 *       <button onClick={() => setTheme('light')}>Light</button>
 *     </div>
 *   );
 * }
 * ```
 */
declare function useRequestContextValue<T = unknown>(key: string): [T | undefined, (value: T) => void];

/**
 * Framework-native document head manager.
 *
 * Place <title>, <meta>, and <link> tags as direct children.
 * Works identically in SSR, SSG, and SPA modes.
 *
 * Server: collects tags via context for injection into the HTML template.
 * Client: renders tags directly; React 19 hoists them to <head>.
 *
 * @example
 * ```tsx
 * import { UnirendHead } from 'unirend/client';
 *
 * function HomePage() {
 *   return (
 *     <>
 *       <UnirendHead>
 *         <title>Home - My App</title>
 *         <meta name="description" content="Welcome to my app" />
 *         <link rel="canonical" href="https://example.com/" />
 *       </UnirendHead>
 *       <main>...</main>
 *     </>
 *   );
 * }
 * ```
 */
declare function UnirendHead({ children }: {
    children?: ReactNode;
}): react_jsx_runtime.JSX.Element | null;

/**
 * Returns the current URL's query parameters parsed with qs, supporting nested
 * objects and arrays (e.g. `?filters[status]=active&ids[]=1&ids[]=2`).
 *
 * The returned object matches the shape of `params.queryParams` in page data
 * loader handlers — so what you read in a component is consistent with what
 * the server handler receives.
 *
 * Re-parses only when the search string changes.
 *
 * @example
 * ```tsx
 * import { useQueryParams } from 'unirend/client';
 *
 * interface ProductsQueryParams {
 *   filters?: { status?: string; tags?: string[] };
 * }
 *
 * function ProductsPage() {
 *   const { filters } = useQueryParams<ProductsQueryParams>();
 *   return <div>Status: {filters?.status}</div>;
 * }
 * ```
 */
declare function useQueryParams<T = Record<string, unknown>>(): T;
/**
 * Serializes a params object into a query string using qs, supporting nested
 * objects and arrays. Returns the string without a leading `?` — prepend one
 * yourself when passing to `navigate()` or building a `<Link to>`.
 *
 * @example
 * ```ts
 * import { stringifyQueryParams } from 'unirend/router-utils';
 * import { useNavigate } from 'react-router';
 *
 * const navigate = useNavigate();
 * navigate(`?${stringifyQueryParams({ filters: { status: 'active' }, ids: [1, 2] })}`);
 * // → ?filters%5Bstatus%5D=active&ids%5B0%5D=1&ids%5B1%5D=2
 * ```
 */
declare function stringifyQueryParams(params: Record<string, unknown>): string;

export { type DomainInfo, type MountAppOptions, type MountAppResult, type RequestContextManager, type UnirendContextValue, UnirendHead, type UnirendRenderMode, mountApp, stringifyQueryParams, useCDNBaseURL, useDomainInfo, useIsClient, useIsDevelopment, useIsSSG, useIsSSR, useIsServer, usePublicAppConfig, useQueryParams, useRenderMode, useRequestContext, useRequestContextObjectRaw, useRequestContextValue };
