import { InjectionToken, Type } from '@angular/core';
import { AbstractControl, ValidationErrors, UntypedFormGroup, UntypedFormArray, UntypedFormControl, ControlValueAccessor, Validator } from '@angular/forms';
import { Observable, Subject } from 'rxjs';
import { Nilable } from 'tsdef';

type Controls<T> = {
    [K in keyof T]-?: AbstractControl;
};
type ControlsNames<T> = {
    [K in keyof T]-?: K;
};
type ControlMap<T, V> = {
    [K in keyof T]-?: V;
};
type ControlsType<T> = {
    [K in keyof T]-?: T[K] extends any[] ? TypedFormArray<T[K]> : TypedFormControl<T[K]> | (T[K] extends {} ? TypedFormGroup<T[K]> : never);
};
type OneOfControlsTypes<T = any> = ControlsType<T>[keyof ControlsType<T>];
type NewFormErrorsType<T> = {
    [K in keyof T]-?: T[K] extends any[] ? Record<number, ValidationErrors> : ValidationErrors;
};
type NewFormErrors<FormInterface> = null | Partial<NewFormErrorsType<FormInterface> & {
    formGroup?: ValidationErrors;
}>;
interface TypedAbstractControl<TValue> extends AbstractControl {
    value: TValue;
    valueChanges: Observable<TValue>;
    setValue(value: TValue, options?: Parameters<AbstractControl['setValue']>[1]): void;
    patchValue(value: Partial<TValue>, options?: Parameters<AbstractControl['patchValue']>[1]): void;
}
interface TypedFormGroup<TValue extends {}> extends UntypedFormGroup {
    value: TValue;
    valueChanges: Observable<TValue>;
    controls: ControlsType<TValue>;
    setValue(value: TValue, options?: Parameters<UntypedFormGroup['setValue']>[1]): void;
    patchValue(value: Partial<TValue>, options?: Parameters<UntypedFormGroup['patchValue']>[1]): void;
    getRawValue(): TValue;
}
interface TypedFormArray<TValue extends any[]> extends UntypedFormArray {
    value: TValue;
    valueChanges: Observable<TValue>;
    controls: TypedAbstractControl<TValue[0]>[];
    setValue(value: TValue, options?: Parameters<UntypedFormArray['setValue']>[1]): void;
    patchValue(value: TValue, options?: Parameters<UntypedFormArray['patchValue']>[1]): void;
    getRawValue(): TValue;
}
interface TypedFormControl<TValue> extends UntypedFormControl {
    value: TValue;
    valueChanges: Observable<TValue>;
    setValue(value: TValue, options?: Parameters<UntypedFormControl['setValue']>[1]): void;
    patchValue(value: Partial<TValue>, options?: Parameters<UntypedFormControl['patchValue']>[1]): void;
}
type KeysWithType<T, V> = {
    [K in keyof T]: T[K] extends V ? K : never;
}[keyof T];
type ArrayPropertyKey<T> = KeysWithType<T, Array<any>>;
type ArrayPropertyValue<T, K extends ArrayPropertyKey<T> = ArrayPropertyKey<T>> = T[K] extends Array<infer U> ? U : never;
declare function subformComponentProviders(component: any): {
    provide: InjectionToken<ControlValueAccessor>;
    useExisting: Type<any>;
    multi?: boolean;
}[];
declare class MissingFormControlsError<T extends string> extends Error {
    constructor(missingFormControls: T[]);
}
declare const NGX_SUB_FORM_HANDLE_VALUE_CHANGES_RATE_STRATEGIES: {
    debounce: <T>(time: number) => (obs: Observable<T>) => Observable<T>;
};
/**
 * Easily unsubscribe from an observable stream by appending `takeUntilDestroyed(this)` to the observable pipe.
 * If the component already has a `ngOnDestroy` method defined, it will call this first.
 */
declare function takeUntilDestroyed<T>(component: any): (source: Observable<T>) => Observable<T>;

type Nullable<T> = T | null;
type NullableObject<T> = {
    [P in keyof T]: Nullable<T[P]>;
};
type TypedValidatorFn<T extends {}> = (formGroup: TypedFormGroup<T>) => ValidationErrors | null;
type TypedAsyncValidatorFn<T extends {}> = (formGroup: TypedFormGroup<T>) => Promise<ValidationErrors | null> | Observable<ValidationErrors | null>;
interface FormGroupOptions<T extends {}> {
    /**
     * @description The list of validators applied to a control.
     */
    validators?: TypedValidatorFn<T> | TypedValidatorFn<T>[] | null;
    /**
     * @description The list of async validators applied to control.
     */
    asyncValidators?: TypedAsyncValidatorFn<T> | TypedAsyncValidatorFn<T>[] | null;
    /**
     * @description The event name for control to update upon.
     */
    updateOn?: 'change' | 'blur' | 'submit';
}
interface NgxFormWithArrayControls<T> {
    createFormArrayControl(key: ArrayPropertyKey<T>, value: ArrayPropertyValue<T>): UntypedFormControl;
}

interface ComponentHooks {
    onDestroy: Observable<void>;
    afterViewInit: Observable<void>;
}
interface FormBindings<ControlInterface> {
    readonly writeValue$: Observable<Nilable<ControlInterface>>;
    readonly registerOnChange$: Observable<(formValue: ControlInterface | null) => void>;
    readonly registerOnTouched$: Observable<() => void>;
    readonly setDisabledState$: Observable<boolean>;
}
type ControlValueAccessorComponentInstance = Object & Partial<Record<keyof ControlValueAccessor, never> & Record<keyof Validator, never>>;
interface NgxSubForm<ControlInterface, FormInterface extends {}> {
    readonly formGroup: TypedFormGroup<FormInterface>;
    readonly formControlNames: ControlsNames<FormInterface>;
    readonly formGroupErrors: NewFormErrors<FormInterface>;
    readonly createFormArrayControl: CreateFormArrayControlMethod<FormInterface>;
    readonly controlValue$: Observable<Nilable<ControlInterface>>;
}
type CreateFormArrayControlMethod<FormInterface> = <K extends ArrayPropertyKey<FormInterface>>(key: K, initialValue: ArrayPropertyValue<FormInterface, K>) => UntypedFormControl;
interface NgxRootForm<ControlInterface, FormInterface extends {}> extends NgxSubForm<ControlInterface, FormInterface> {
}
interface NgxSubFormArrayOptions<FormInterface> {
    createFormArrayControl?: CreateFormArrayControlMethod<FormInterface>;
}
interface NgxSubFormRemapOptions<ControlInterface, FormInterface> {
    toFormGroup: (obj: ControlInterface) => FormInterface;
    fromFormGroup: (formValue: FormInterface) => ControlInterface;
}
type AreTypesSimilar<T, U> = T extends U ? (U extends T ? true : false) : false;
type NgxSubFormRemap<ControlInterface, FormInterface> = AreTypesSimilar<ControlInterface, FormInterface> extends true ? Partial<NgxSubFormRemapOptions<ControlInterface, FormInterface>> : NgxSubFormRemapOptions<ControlInterface, FormInterface>;
type NgxSubFormArray<FormInterface> = ArrayPropertyKey<FormInterface> extends never ? {} : NgxSubFormArrayOptions<FormInterface>;
type NgxSubFormOptions<ControlInterface, FormInterface extends {} = ControlInterface extends {} ? ControlInterface : never> = {
    formType: FormType;
    formControls: Controls<FormInterface>;
    formGroupOptions?: FormGroupOptions<FormInterface>;
    emitNullOnDestroy?: boolean;
    emitInitialValueOnInit?: boolean;
    componentHooks?: ComponentHooks;
    touched$?: Observable<void>;
} & NgxSubFormRemap<ControlInterface, FormInterface> & NgxSubFormArray<FormInterface>;
type NgxRootFormOptions<ControlInterface, FormInterface extends {} = ControlInterface extends {} ? ControlInterface : never> = NgxSubFormOptions<ControlInterface, FormInterface> & {
    input$: Observable<ControlInterface | undefined>;
    output$: Subject<ControlInterface>;
    disabled$?: Observable<boolean>;
    manualSave$?: Observable<void>;
    outputFilterPredicate?: (currentInputValue: FormInterface, outputValue: FormInterface) => boolean;
    handleEmissionRate?: (obs$: Observable<FormInterface>) => Observable<FormInterface>;
};
declare enum FormType {
    SUB = "Sub",
    ROOT = "Root"
}
type NgxFormOptions<ControlInterface, FormInterface extends {}> = NgxSubFormOptions<ControlInterface, FormInterface> | NgxRootFormOptions<ControlInterface, FormInterface>;

declare const getFormGroupErrors: <ControlInterface, FormInterface extends {}>(formGroup: TypedFormGroup<FormInterface>) => NewFormErrors<FormInterface>;
interface FormArrayWrapper<FormInterface> {
    key: keyof FormInterface;
    control: UntypedFormArray;
}
declare function createFormDataFromOptions<ControlInterface, FormInterface extends {}>(options: NgxSubFormOptions<ControlInterface, FormInterface>): {
    formGroup: TypedFormGroup<FormInterface>;
    defaultValues: FormInterface;
    formControlNames: ControlsNames<FormInterface>;
    formArrays: FormArrayWrapper<FormInterface>[];
};
declare const handleFormArrays: <FormInterface>(formArrayWrappers: FormArrayWrapper<FormInterface>[], obj: FormInterface, createFormArrayControl: Required<NgxSubFormArrayOptions<FormInterface>>["createFormArrayControl"]) => void;

declare function createForm<ControlInterface, FormInterface extends {} = ControlInterface extends {} ? ControlInterface : never>(componentInstance: ControlValueAccessorComponentInstance, options: NgxRootFormOptions<ControlInterface, FormInterface>): NgxRootForm<ControlInterface, FormInterface>;
declare function createForm<ControlInterface, FormInterface extends {} = ControlInterface extends {} ? ControlInterface : never>(componentInstance: ControlValueAccessorComponentInstance, options: NgxSubFormOptions<ControlInterface, FormInterface>): NgxSubForm<ControlInterface, FormInterface>;

export { FormType, MissingFormControlsError, NGX_SUB_FORM_HANDLE_VALUE_CHANGES_RATE_STRATEGIES, createForm, createFormDataFromOptions, getFormGroupErrors, handleFormArrays, subformComponentProviders, takeUntilDestroyed };
export type { AreTypesSimilar, ArrayPropertyKey, ArrayPropertyValue, ComponentHooks, ControlMap, ControlValueAccessorComponentInstance, Controls, ControlsNames, ControlsType, CreateFormArrayControlMethod, FormBindings, FormGroupOptions, KeysWithType, NewFormErrors, NewFormErrorsType, NgxFormOptions, NgxFormWithArrayControls, NgxRootForm, NgxRootFormOptions, NgxSubForm, NgxSubFormArrayOptions, NgxSubFormOptions, NgxSubFormRemapOptions, NullableObject, OneOfControlsTypes, TypedAbstractControl, TypedAsyncValidatorFn, TypedFormArray, TypedFormControl, TypedFormGroup, TypedValidatorFn };
