import * as react from 'react';
import * as _kilpi_client from '@kilpi/client';
import { AnyKilpiClient, KilpiClientPolicy, KilpiClient } from '@kilpi/client';
import { PolicysetActions, Decision, GrantedDecision, DeniedDecision, AnyKilpiCore } from '@kilpi/core';

/**
 * Input type
 */
type UseAuthorizeOptions = {
    isDisabled?: boolean;
};
/**
 * UseAuthorizeStatus
 */
type UseAuthorizeStatus = "idle" | "pending" | "error" | "success";
/**
 * Sub-utility type for just the status and flags
 */
type UseAuthorizeStatusWithFlags<TStatus extends UseAuthorizeStatus> = {
    status: TStatus;
    isIdle: TStatus extends "idle" ? true : false;
    isError: TStatus extends "error" ? true : false;
    isSuccess: TStatus extends "success" ? true : false;
    isPending: TStatus extends "pending" ? true : false;
};
/**
 * Utility type to get status with flags to an object
 */
type UseAuthorizeReturnForStatus<TStatus extends UseAuthorizeStatus, TDecision extends null | Decision<any>> = UseAuthorizeStatusWithFlags<TStatus> & {
    error: TStatus extends "error" ? unknown : null;
    isDisabled: boolean;
    decision: TDecision;
    granted: TDecision extends {
        granted: true;
    } ? true : false;
};
/**
 * Discriminated return type by status. Implemented this way to ensure typescript
 * is better able to deduce the types when narrowing or using `Extract<...>`. Two
 * separate discriminators for `status = success` based on `granted` flag.
 */
type UseAuthorizeReturn<TClient extends AnyKilpiClient, TAction extends PolicysetActions<TClient["$$infer"]["policies"]>> = UseAuthorizeReturnForStatus<"idle", null> | UseAuthorizeReturnForStatus<"pending", null> | UseAuthorizeReturnForStatus<"error", null> | UseAuthorizeReturnForStatus<"success", GrantedDecision<TClient["$$infer"]["subject"]>> | UseAuthorizeReturnForStatus<"success", DeniedDecision>;

/**
 * The type of a KilpiClientPolicy extension.
 */
interface KilpiClientPolicyExtension_ReactClientPlugin<TClient extends AnyKilpiClient, TAction extends PolicysetActions<TClient["$$infer"]["policies"]>> {
    /**
     * Extend policies with a useAuthorize hook for usage in client-side React components.
     */
    useAuthorize(options?: UseAuthorizeOptions): UseAuthorizeReturn<TClient, TAction>;
}
/**
 * Augment KilpiClientPolicy with extension.
 */
declare module "@kilpi/client" {
    interface IKilpiClientPolicy<TClient extends AnyKilpiClient, TAction extends PolicysetActions<TClient["$$infer"]["policies"]>> extends KilpiClientPolicyExtension_ReactClientPlugin<TClient, TAction> {
    }
}

/**
 * The <Authorize /> component renders either children, Unauthorized or Pending
 * based on the state of an authorization decision.
 */
type AuthorizeClientProps<TClient extends AnyKilpiClient, TPolicy extends KilpiClientPolicy<TClient, TClient["$$infer"]["policies"]>> = {
    /**
     * The policy to evaluate.
     */
    policy: TPolicy;
    /**
     * Disables the authorization check when true.
     */
    isDisabled?: boolean;
    /**
     * Children that are rendered when the caller is authorized. May be a dynamic function
     * that depends on the decision.
     */
    children?: React.ReactNode | ((query: Extract<UseAuthorizeReturn<TClient, TPolicy["$action"]>, {
        status: "success";
        granted: true;
    }>) => React.ReactNode);
    /**
     * Children that are rendered when the caller is not authorized. May be a dynamic function
     * that depends on the decision.
     */
    Unauthorized?: React.ReactNode | ((query: Extract<UseAuthorizeReturn<TClient, TPolicy["$action"]>, {
        status: "success";
        granted: false;
    }>) => React.ReactNode);
    /**
     * Children that are rendered while the authorization decision is pending.
     */
    Pending?: React.ReactNode | ((query: Extract<UseAuthorizeReturn<TClient, TPolicy["$action"]>, {
        status: "pending" | "idle";
    }>) => React.ReactNode);
    /**
     * Children that are rendered while the authorization decision is idle (disabled).
     *
     * Unless idle is specifically overrided or set to `null`, will use the `Pending` component
     * instead.
     */
    Idle?: React.ReactNode | ((query: Extract<UseAuthorizeReturn<TClient, TPolicy["$action"]>, {
        status: "pending" | "idle";
    }>) => React.ReactNode);
    /**
     * Children that are rendered while the authorization decision is error.
     */
    Error?: React.ReactNode | ((query: Extract<UseAuthorizeReturn<TClient, TPolicy["$action"]>, {
        status: "error";
    }>) => React.ReactNode);
};

/**
 * React server component plugin for automatically providing a Kilpi scope
 * in React Server Components and for creating the React Server Component bindings
 * to work with Kilpi.
 */
declare function ReactClientPlugin<TCore extends AnyKilpiCore>(): _kilpi_client.KilpiClientPlugin<TCore, {
    $createReactClientComponents(): {
        AuthorizeClient: <TAction extends PolicysetActions<TCore["$$infer"]["policies"]>, TPolicy extends _kilpi_client.KilpiClientPolicy<KilpiClient<TCore>, TAction> & KilpiClientPolicyExtension_ReactClientPlugin<KilpiClient<TCore>, TAction>>({ policy, children, Unauthorized, Idle, Pending, Error, isDisabled, }: AuthorizeClientProps<KilpiClient<TCore>, TPolicy>) => react.ReactNode;
    };
}>;

export { ReactClientPlugin };
