import { DOMContext } from '../dom/dom-context';
import { ProviderMark, Renderable, TNode } from '../types/domain';
/**
 * Converts an array of `Provider` types `T` into an array of their corresponding types.
 * @public
 */
export type ToProviderTypes<T extends unknown[]> = T extends [] ? [] : T extends [Provider<infer K>] ? [K] : T extends [Provider<infer K>, ...infer R] ? [K, ...ToProviderTypes<R>] : never;
/**
 * Represents a provider for a specific type `T`.
 * @public
 */
export type Provider<T, O = any> = {
    /** The provider mark. */
    mark: ProviderMark<T>;
    /** The function to create the provider. */
    create: (options: O | undefined, ctx: DOMContext) => {
        value: T;
        dispose: () => void;
        onUse?: () => void;
    };
};
/**
 * Represents an object with provider options.
 * @public
 */
export type ProviderOptions = {
    /** The function to use a provider. */
    use: <T, O = any>(provider: Provider<T, O>) => T;
    /** The function to set a provider. */
    set: <T, O = any>(provider: Provider<T, O>, options?: O) => void;
};
/**
 * Returns a renderable function that executes the given function with the
 * current DOMContext as argument.
 * The given function can return a TNode or void. If you need to perform some
 * actions when the Renderable is disposed, you can use `OnDispose` as the
 * return value.
 *
 * @param fn - The function to be executed with the DOMContext argument.
 * @returns A Clear function that can be used to clean up any resources associated with the execution.
 * @public
 */
export declare const WithProvider: (fn: (opts: ProviderOptions) => TNode | void) => Renderable;
/**
 * Makes a provider available to all child components in the component tree.
 *
 * This function creates a provider context that child components can access using `Use`.
 * The provider is automatically disposed when the component is unmounted.
 *
 * @example
 * ```typescript
 * // Create a theme provider
 * const ThemeProvider: Provider<Signal<string>> = {
 *   mark: makeProviderMark<Signal<string>>('Theme'),
 *   create: () => {
 *     const theme = prop('light')
 *     return {
 *       value: theme,
 *       dispose: () => theme.dispose()
 *     }
 *   }
 * }
 *
 * // Provide the theme to child components
 * const App = () =>
 *   Provide(
 *     ThemeProvider,
 *     {}, // options
 *     () => html.div(
 *       html.h1('My App'),
 *       ThemeToggle(), // Can access the theme
 *       MainContent()  // Can also access the theme
 *     )
 *   )
 * ```
 *
 * @example
 * ```typescript
 * // Provider with options
 * const ConfigProvider: Provider<Config, { apiUrl: string }> = {
 *   mark: makeProviderMark<Config>('Config'),
 *   create: (options) => {
 *     const config = new Config(options.apiUrl)
 *     return {
 *       value: config,
 *       dispose: () => config.cleanup()
 *     }
 *   }
 * }
 *
 * // Provide with specific options
 * Provide(
 *   ConfigProvider,
 *   { apiUrl: 'https://api.example.com' },
 *   () => AppContent()
 * )
 * ```
 *
 * @template T - The type of value provided by the provider
 * @template O - The type of options passed to the provider
 * @param provider - The provider definition containing mark and create function
 * @param options - Options to pass to the provider's create function
 * @param child - Function that returns the child components that can access the provider
 * @returns A renderable that provides the value to its children
 * @public
 */
export declare const Provide: <T, O>(provider: Provider<T, O>, options: O, child: () => TNode) => Renderable;
/**
 * Consumes a provider value and makes it available to a child component.
 *
 * This function looks up a provider in the component tree and passes its value
 * to the child function. The provider must have been made available by a parent
 * component using `Provide`.
 *
 * @example
 * ```typescript
 * // Use a theme provider
 * const ThemeToggle = () =>
 *   Use(
 *     ThemeProvider,
 *     (theme) => html.button(
 *       on.click(() => {
 *         theme.value = theme.value === 'light' ? 'dark' : 'light'
 *       }),
 *       'Current theme: ', theme
 *     )
 *   )
 * ```
 *
 * @example
 * ```typescript
 * // Use multiple properties from a provider
 * const UserProfile = () =>
 *   Use(
 *     UserProvider,
 *     (user) => html.div(
 *       html.h2('Welcome, ', user.map(u => u.name)),
 *       html.p('Email: ', user.map(u => u.email)),
 *       html.p('Role: ', user.map(u => u.role))
 *     )
 *   )
 * ```
 *
 * @example
 * ```typescript
 * // Use provider in conditional rendering
 * const ConditionalContent = () =>
 *   Use(
 *     AuthProvider,
 *     (auth) => auth.map(a => a.isLoggedIn)
 *       ? html.div('Welcome back!')
 *       : html.div('Please log in')
 *   )
 * ```
 *
 * @template T - The type of value provided by the provider
 * @param provider - The provider to consume (must be available in parent components)
 * @param child - Function that receives the provider value and returns content to render
 * @returns A renderable that consumes the provider and renders the child content
 * @throws {ProviderNotFoundError} When the provider is not found in the component tree
 * @public
 */
export declare const Use: <T>(provider: Provider<T>, child: (provider: T) => TNode) => Renderable;
/**
 * Consumes multiple providers and makes their values available to a child component.
 *
 * This function is a convenience wrapper for consuming multiple providers at once.
 * It's equivalent to nesting multiple `Use` calls but provides a cleaner API when
 * you need access to several providers simultaneously.
 *
 * @example
 * ```typescript
 * // Use multiple providers together
 * const Dashboard = () =>
 *   UseMany(
 *     UserProvider,
 *     ThemeProvider,
 *     ConfigProvider
 *   )(
 *     (user, theme, config) => html.div(
 *       attr.class(theme.map(t => `theme-${t}`)),
 *       html.h1('Dashboard for ', user.map(u => u.name)),
 *       html.p('API URL: ', config.map(c => c.apiUrl)),
 *       html.p('Current theme: ', theme)
 *     )
 *   )
 * ```
 *
 * @example
 * ```typescript
 * // Equivalent to nested Use calls
 * const Dashboard = () =>
 *   Use(UserProvider, user =>
 *     Use(ThemeProvider, theme =>
 *       Use(ConfigProvider, config =>
 *         html.div(
 *           // Same content as above
 *         )
 *       )
 *     )
 *   )
 * ```
 *
 * @example
 * ```typescript
 * // Type-safe access to multiple providers
 * const SettingsPanel = () =>
 *   UseMany(
 *     PreferencesProvider,
 *     UserProvider
 *   )(
 *     (preferences, user) => html.div(
 *       html.h2('Settings for ', user.map(u => u.name)),
 *       html.label(
 *         'Theme: ',
 *         html.select(
 *           attr.value(preferences.map(p => p.theme)),
 *           on.change(emitValue(value => {
 *             preferences.update(p => ({ ...p, theme: value }))
 *           })),
 *           html.option(attr.value('light'), 'Light'),
 *           html.option(attr.value('dark'), 'Dark')
 *         )
 *       )
 *     )
 *   )
 * ```
 *
 * @template T - Tuple type representing the types of all providers
 * @param providers - Variable number of providers to consume
 * @returns Function that takes a child function and returns a renderable
 * @throws {ProviderNotFoundError} When any of the providers is not found in the component tree
 * @public
 */
export declare const UseMany: <T extends Provider<unknown>[]>(...providers: T) => (child: (...values: ToProviderTypes<T>) => TNode) => Renderable;
