import React, { EffectCallback, ChangeEvent } from 'react';
import * as zustand from 'zustand';
import * as yup from 'yup';

/**
 * Parameters for pagination when fetching entities from backend.
 */
interface PagingParameter {
    /**
     * Page number. Starts from 0.
     */
    pageNumber: number;
    /**
     * Number of entities to fetch.
     */
    pageSize: number;
}
/**
 * Response data regarding paging when fething entities from backend.
 */
interface PagingResponse {
    /**
     * Is this page the first one.
     */
    first: boolean;
    /**
     * Is this page the last one.
     */
    last: boolean;
    /**
     * Is this page empty.
     */
    empty: boolean;
    /**
     * Number of total pages for this search.
     */
    totalPages: number;
    /**
     * Number of total elements over all pages for this search.
     */
    totalElements: number;
    /**
     * Number of elements on current page.
     */
    numberOfElements: number;
}

interface RegistryConfiguration {
    /**
     * Base URL for all function in service.ts
     */
    baseURL?: string;
    /**
     * Additional configuration options to send with fetch request i.e. if some authentication information is required.
     */
    requestOptionsResolver?: () => RequestInit;
}

/**
 * Represents client property configuration that can be used when building form and grids on client side.
 */
interface RegistryPropertyConfiguration {
    /**
     * Property name.
     */
    name: string;
    /**
     * Property Javascript type (converted from property type).
     */
    javascriptType: string;
    /**
     * Property original type class name
     */
    originalType: string;
    /**
     * Whether this property is id.
     */
    id: boolean;
    /**
     * Whether this property is decimal (Javascript has no specific decimal type).
     */
    decimal: boolean;
    /**
     * Whether this property represents a singular association (JPA @OneToOne or @ManyToOne)
     */
    singularAssociation: boolean;
    /**
     * Singular association class
     */
    singularAssociationReferencedClass?: string;
    /**
     * Label for this property in form view.
     */
    formLabel: string;
    /**
     * Header for this property in table view.
     */
    columnHeader: string;
    /**
     * Whether this property is editable.
     */
    editable: boolean;
    /**
     * Whether this property is sortable.
     */
    sortable: boolean;
    /**
     * Whether this property is searchable.
     */
    searchable: boolean;
}
/**
 * Represents client entity configuration that can be used when building form and grids on client side.
 */
interface RegistryEntityConfiguration {
    /**
     * Fully qualified classname of this registry entity.
     */
    classFullName: string;
    /**
     * Simple class name.
     */
    name: string;
    /**
     * Name to be displayed.
     */
    displayName: string;
    /**
     * Group to which this registry belongs to.
     */
    groupId: string;
    /**
     * Whether this entity is read only.
     */
    readOnly: boolean;
    /**
     * Whether new instances of this entity can be created.
     */
    creatable: boolean;
    /**
     * Whether this entity can be updated.
     */
    updateable: boolean;
    /**
     * Whether this entity can be deleted.
     */
    deletable: boolean;
    /**
     * Whether this entity has identifier assigned.
     */
    identifierAssigned: boolean;
    /**
     * Whether this entity has IdClass identifier.
     */
    idClassIdentity: boolean;
    /**
     * Whether this entity has EmbeddedId identifier.
     */
    embeddedIdentity: boolean;
    /**
     * List of property names from which the id consists of.
     */
    idClassPropertyNameList: string[];
    /**
     * Whether history for this entity is available.
     */
    historyAvailable: boolean;
    /**
     * List of property configurations.
     */
    propertyConfigurationList: RegistryPropertyConfiguration[];
    /**
     * List of embedded id property configurations.
     */
    embeddedIdPropertyConfigurationList: RegistryPropertyConfiguration[];
    /**
     * List of history properties, only available if history exists.
     */
    historyPropertyConfigurationList: RegistryPropertyConfiguration[];
}
/**
 * Configuration for registry group (a group of registry entities).
 */
interface RegistryGroupConfiguration {
    /**
     * Unique id of group.
     */
    groupId: string;
    /**
     * Display label for group.
     */
    groupIdDisplayName: string;
    /**
     * List of entity configurations belonging to this group.
     */
    entityConfigurationList: RegistryEntityConfiguration[];
}

/**
 * Registry entity search parameters.
 */
interface SearchParameter {
    /**
     * List of properties to search.
     */
    propertyNameList: string[];
    /**
     * Search query.
     */
    query: string;
}

/**
 * Sort direction.
 */
type SortDirection = "ASC" | "DESC";
/**
 * Combination of property to sort by and sort direction.
 */
interface SortProperty {
    /**
     * Property to sort by.
     */
    property: string;
    /**
     * Sort direction.
     */
    direction: SortDirection;
}
/**
 * Data regarding sorting when fetching entites from backend.
 */
interface SortResponse {
    /**
     * Is the result sorted.
     */
    sorted: boolean;
    /**
     * Is the result unsorted.
     */
    unsorted: boolean;
}

/**
 * Type of form on registry entity administration
 */
type FormType = "create" | "update" | "preview";

/**
 * Type for {@link #RegistryEntityContext} value
 */
interface RegistryEntityContextType {
    /**
     * Configuration of administered registry entity
     */
    entityConfiguration: RegistryEntityConfiguration;
    /**
     * Final list of properties to be shown on table or in form. For simple registry, contains only propertyConfigurationList.
     * For entities with complex ids it contains all fields from id object mapped for easier usage and UX
     */
    finalProperties: RegistryPropertyConfiguration[];
    /**
     * Map containing entity configurations for all sub-entities of this entity.
     * Key is full class name, and value is configuration
     */
    singularAssociationsMap: Record<string, RegistryEntityConfiguration>;
}
/**
 * Helper context used for easier data sharing in registry entity administration components
 */
declare const RegistryEntityContext: React.Context<RegistryEntityContextType>;
/**
 * Helper hook to get registry entity context. Throws if the user is not inside provider.
 */
declare const useRegistryEntityContext: () => RegistryEntityContextType;
/**
 * {@link #RegistryEntityContextProvider} props
 */
interface Props$1 {
    /**
     * Configuration of the administered entity
     */
    entityConfiguration: RegistryEntityConfiguration;
    /**
     * Children where this context is provided
     */
    children: React.ReactNode;
}
/**
 * Helper provider for {@link #RegistryEntityContext} used for easier data sharing in registry entity administration components
 * @param entityConfiguration configuration of the administered entity
 * @param children children where this context is provided
 */
declare const RegistryEntityContextProvider: ({ entityConfiguration, children }: Props$1) => JSX.Element;

interface RegistryConfigurationStore {
    /**
     * Array of group configurations.
     */
    groupConfigurations: RegistryGroupConfiguration[];
    /**
     * Load configuration
     */
    load: (groupConfigurations: any) => void;
    /**
     * Entity formatters used for rendering complex entity types
     */
    entityFormatters: Record<string, (value: any) => string>;
    /**
     * Entity formatters setter
     */
    setEntityFormatters: (entityFormatters: Record<string, (value: any) => string>) => void;
    /**
     * Registry configuration
     */
    registryConfiguration: RegistryConfiguration;
    /**
     * Registry configuration setter
     */
    setRegistryConfiguration: (registryConfiguration: RegistryConfiguration) => void;
}
declare const useRegistryConfigurationStore: zustand.UseBoundStore<zustand.StoreApi<RegistryConfigurationStore>>;

/**
 * {@link RegistryProvider} properties
 */
interface Props extends Partial<RegistryConfigurationStore> {
    /**
     * Children where registry configuration is provided
     */
    children: React.ReactNode;
}
/**
 * Provider for registry configuration. Until registry is fetched, renders loading screen.
 * @param children Children where registry configuration is provided
 * @param registryConfiguration Registry configuration for baseURL and requestOptionsResolver
 * @param entityFormatters Formatters for complex object types
 */
declare const RegistryProvider: ({ children, registryConfiguration, entityFormatters }: Props) => JSX.Element;

/**
 * Request when fetching a list of registry entries.
 */
type RegistryRequest = {
    /**
     * Full Java class name of the registry entity
     */
    classFullName: string;
    /**
     * List of sort rules for this search request
     */
    sortPropertyList?: SortProperty[];
    /**
     * Search configuration for this request
     */
    searchParameter?: SearchParameter;
} & PagingParameter;
/**
 * Response of a single registry entity request.
 */
type RegistryResponse<T> = {
    /**
     * List of result entities.
     */
    content: T[];
    /**
     * Result sort.
     */
    sort: SortResponse;
} & PagingResponse;
/**
 * Fetches whole registry configuration.
 */
declare const loadRegistryConfiguration: () => Promise<RegistryGroupConfiguration[]>;
/**
 * Fetches single registry entity list with given paging and sorting parameters.
 * @param request Single load request which contains entity class, paging, sorting and search options
 * @returns List of entity results
 */
declare const loadEntities: (request: RegistryRequest) => Promise<RegistryResponse<any>>;
/**
 * Fetches multiple registry entity lists for given registry requests.
 * @param requests List of registry requests
 * @returns Map of results where key is full class name and value is single entity response list
 */
declare const bulkLoadEntities: (requests: RegistryRequest[]) => Promise<Record<string, RegistryResponse<any>>>;
/**
 * Creates new entry for given registry entity
 * @param classFullName Full Java class name of the registry entity
 * @param createData Object with data of new entity
 * @returns Created entity
 */
declare const createEntity: (classFullName: string, createData: any) => Promise<any>;
/**
 * Updates existing entity with new data
 * @param classFullName Full Java class name of the registry entity
 * @param id Id of the existing entity
 * @param updateData Object with update date for existing entity
 * @returns Updated entity
 */
declare const updateEntity: (classFullName: string, id: any, updateData: any) => Promise<any>;
/**
 * Deletes existing entity
 * @param classFullName Full Java class name of the registry entity
 * @param id Id of the existing entity
 */
declare const removeEntity: (classFullName: string, id: any) => Promise<any>;

/**
 * Request when fetching a list of registry entries, without class specifics
 */
type EntityRegistryRequest = Omit<RegistryRequest, "classFullName">;
/**
 * Return type of {@link useRegistryEntity}. Contains main data and methods for managing registry entities.
 */
interface UseRegistryEntity {
    /**
     * Configuration of wanted entity
     */
    entityConfiguration: RegistryEntityConfiguration;
    /**
     * Id property of the entity
     */
    entityIdProperty: RegistryPropertyConfiguration;
    /**
     * Currently fetched data for this entity.
     */
    data: RegistryResponse<any[]>;
    /**
     * Method for reloading data explicitly with given request.
     * @param request search, filter and paging data of this request
     */
    load: (request: EntityRegistryRequest) => Promise<void>;
    /**
     * Adds new entity. Should conform to structure from {@link entityConfiguration}.
     * Automatically re-fetches with request provided to hook.
     * @param createData data for new entity
     */
    add: (createData: any) => Promise<any>;
    /**
     * Edits existing entity. Should conform to structure from {@link entityConfiguration}.
     * Automatically re-fetches with request provided to hook.
     * @param id id of the data for editing. When complex ids are used, it should be an object containing all id fields.
     * @param updateData data for editable entity
     */
    edit: (id: any, updateData: any) => Promise<any>;
    /**
     * Deletes existing entity.
     * Automatically re-fetches with request provided to hook.
     * @param id id of the data for editing. When complex ids are used, it should be an object containing all id fields.
     */
    remove: (id: any) => Promise<any>;
}
/**
 * Default request for fetching used when other is not provided. Starts on first page with size of 25.
 */
declare const DEFAULT_INITIAL_REQUEST: EntityRegistryRequest;
declare const useRegistryEntity: (name: string, initialRequest?: EntityRegistryRequest) => UseRegistryEntity;

/**
 * Return value of {@link useRegistryEntityAdministration} hook. Contains all configuration, data and handlers for most of the administration operations.
 */
interface UseRegistryEntityAdministration {
    /**
     * Configuration of wanted entity
     */
    entityConfiguration: RegistryEntityConfiguration;
    /**
     * Id property of the entity
     */
    entityIdProperty: RegistryPropertyConfiguration;
    /**
     * Currently fetched data for this entity.
     */
    data: RegistryResponse<any[]>;
    /**
     * Currently used request for fetching.
     */
    request: EntityRegistryRequest;
    /**
     * Handler when paging (page number or size) is changed. Refeteches data with new paging configuration.
     * Reuses filter configuration if any is used.
     * @param newPagingRequest new paging configuration for fetching. Non paging fields are ignored.
     */
    handlePagingUpdate: (newPagingRequest: EntityRegistryRequest) => Promise<void>;
    /**
     * Handler when filter (query and search fields) is changed. Refeteches with new filter configuration.
     * Reuses page size and resets page to 0.
     */
    handleFilterUpdate: (searchParameter: SearchParameter) => Promise<void>;
    /**
     * Handler for adding new entry. Opens up modal for form to be filled.
     */
    handleAddClick: () => void;
    /**
     * Handler for adding new entry from copied one. Opens up modal with filled data from copied entry.
     * @param row data of the entry to be edited
     */
    handleAddFromCopyClick: (row: any) => void;
    /**
     * Handler for editing existing entry. Opens up modal with filled data to be edited.
     * @param id id of the entry to be edited
     * @param row data of the entry to be edited
     */
    handleEditClick: (id: any, row: any) => void;
    /**
     * Handler for entry preview. Opens up modal with filled data from entry.
     * @param row data of the entry to be previewed
     */
    handlePreviewClick: (row: any) => void;
    /**
     * Handler for submitting add or edit form. Closes modal and tries to save new data.
     * @param values data of new/edited entry for saving
     */
    handleSubmitClick: (values: any) => Promise<any>;
    /**
     * Current form type (create or update). Undefined if no form is currently active.
     */
    formType?: FormType;
    /**
     * Initial data when opening form. Empty object for adding, and row data for editing.
     */
    formData: any;
    /**
     * Flag which marks if form modal is open or not.
     */
    formModalOpen: boolean;
    /**
     * Method for explicit closing of form modal.
     */
    closeFormModal: () => void;
    /**
     * Flag if data is currently loading.
     */
    loading: boolean;
    /**
     * Deletes existing entity.
     * Automatically re-fetches with request provided to hook.
     * @param id id of the data for editing. When complex ids are used, it should be an object containing all id fields.
     */
    remove: (id: any) => Promise<any>;
}
/**
 * Helper hook which handles most of the administration for registry entity. For given entity name you get all the data handling out of the box.
 * This hook assumes that modal is used for forms, but usage can be what fits the case.
 * @param entityName name of the entity for administration
 */
declare const useRegistryEntityAdministration: (entityName: string) => UseRegistryEntityAdministration;

/**
 * Helper hook for fetching validation configuration for given entity
 * @param classFullName full class name of the entity
 * @param type type of form
 */
declare const useRegistryEntityYupFormConfiguration: (classFullName: string, type: FormType) => yup.ObjectSchema<any, yup.AnyObject, any, "">;

/**
 * Helper hook, similar to useEffect, but doesn't trigger on initial load.
 * @param effect effect to be triggered
 * @param dependencies dependency array of the effect
 */
declare const useUpdateEffect: (effect: EffectCallback, dependencies?: React.DependencyList) => void;
/**
 * Helper hook, similar to useEffect, but has additional debounce effect. Doesn't trigger on initial load.
 * Calls effect only once for multiple triggers, when debounce timer expires. Usefully in search/filters, to filter out unnecessary requests.
 * @param effect effect to be triggered
 * @param debounceTime time in milliseconds for debounce
 * @param dependencies dependecy array of the effect
 */
declare const useDebouncedUpdateEffect: (effect: EffectCallback, debounceTime: number, dependencies?: React.DependencyList) => void;

/**
 * Return value of {@link useRegistryFilter}. Contains configuration, value and change handlers for registry filter.
 */
interface UseRegistryFilter {
    /**
     * List of available fields for filtering
     */
    availableFields: {
        value: string;
        label: string;
    }[];
    /**
     * Current value of request {@link SearchParameter}
     */
    searchParameter: SearchParameter;
    /**
     * Change handler for search fields
     * @param event change event (usually from multiselect field)
     */
    handleFieldsChange: (event: ChangeEvent) => void;
    /**
     * Change handler for query
     * @param event change event (usually from text field)
     */
    handleQueryChange: (event: ChangeEvent) => void;
}
/**
 * Helper hook that handles filtering states for administered registry entity. Only handles fields and leaves refeching to hook user.
 * @param initialFilterValue initial filter value
 */
declare const useRegistryFilter: (initialFilterValue?: SearchParameter) => UseRegistryFilter;

/**
 * Return value of {@link useRegistryForm}. Contains configuration part of the form, with initial values, while leaving user to
 * handle form changes and errors.
 */
interface UseRegistryForm {
    /**
     * Configuration of wanted entity
     */
    entityConfiguration: RegistryEntityConfiguration;
    /**
     * Validation schema of the form
     */
    yupSchema: yup.ObjectSchema<any>;
    /**
     * Flattened initial values for easier access in form.
     */
    finalInitialValues: any;
    /**
     * Properties to be shown in the form.
     */
    properties: any;
}
/**
 * Helper hook used in registry forms. Returns initial data and configuration to be used in the form.
 * @param initialValues initial values of the form. Row data for update and empty object for create form.
 * @param type type of the form (update or create)
 */
declare const useRegistryForm: (initialValues: any, type: FormType) => UseRegistryForm;

/**
 * Return value of {@link useRegistrySort} hook. Contains state and sort handler which uses natural flow for sorting (asc -> desc -> none)
 */
interface UseRegistrySort {
    /**
     * Current sort direction. Undefined if no sort is applied.
     */
    sortDirection?: SortDirection;
    /**
     * Change handler for sort, usually triggered on table header click. Changes sorting to next state. (asc -> desc -> none)
     */
    sortChangeHandler: () => void;
}
/**
 * Helper hook used for table headers to add functionality of sorting by specific field
 * @param propertyConfiguration configuration of the column property
 * @param value current list of sort properties
 * @param onChange change handler to be called with new sort properties
 * @param allowMultiple flag if multiple properties can be sorted at the same time. Default is true
 */
declare const useRegistrySort: (propertyConfiguration: RegistryPropertyConfiguration, value: SortProperty[], onChange: (newValue: SortProperty[]) => void, allowMultiple?: boolean) => UseRegistrySort;

/**
 * Returns an id representation for given entity configuration and data. Can be single value (e.g. when id is numeric) or an object
 * when embedded id or id class is used
 * @param entityConfiguration
 * @param data
 */
declare const resolveId: (entityConfiguration: any, data: any) => any;
/**
 * Resolves a value for flat property from nested data.
 * @param propertyConfiguration
 * @param data
 */
declare const resolveValue: (propertyConfiguration: any, data: any) => any;
/**
 * Rearranges object from flat structure to nested one, e.g.
 *  { "id.author": { "id": 1 }, "id.book": { "id": 3 }, "edition": "First edition" } to
 *  { "id": { "author": { "id": 1 }, "book": { "id": 3 } }, "edition": "First edition" }
 *  @param value form value for submission
 */
declare const restructureSubmitValue: (value: any) => {};
/**
 * Formats id field as Class[id=3]. Used as default for rendering objects
 * @param classFullName full name of class for formatting
 * @param idFieldName name of the id field
 * @param id value of the id field
 */
declare const formatIdName: (classFullName: string, idFieldName: string, id: any) => string;
/**
 * Returns id field of the configuration, used when entity does not have embedded id nor id class.
 * @param entityConfiguration configuration of the registry entity
 */
declare const findIdField: (entityConfiguration: RegistryEntityConfiguration) => RegistryPropertyConfiguration;

export { DEFAULT_INITIAL_REQUEST, EntityRegistryRequest, FormType, PagingParameter, PagingResponse, RegistryConfiguration, RegistryConfigurationStore, RegistryEntityConfiguration, RegistryEntityContext, RegistryEntityContextProvider, RegistryGroupConfiguration, RegistryPropertyConfiguration, RegistryProvider, RegistryRequest, RegistryResponse, SearchParameter, SortDirection, SortProperty, SortResponse, UseRegistryEntity, bulkLoadEntities, createEntity, findIdField, formatIdName, loadEntities, loadRegistryConfiguration, removeEntity, resolveId, resolveValue, restructureSubmitValue, updateEntity, useDebouncedUpdateEffect, useRegistryConfigurationStore, useRegistryEntity, useRegistryEntityAdministration, useRegistryEntityContext, useRegistryEntityYupFormConfiguration, useRegistryFilter, useRegistryForm, useRegistrySort, useUpdateEffect };
