import type FeatureLayer from "@arcgis/core/layers/FeatureLayer.js";
import type Renderer from "@arcgis/core/renderers/Renderer.js";
import type FeatureLayerView from "@arcgis/core/views/layers/FeatureLayerView.js";
import type MapView from "@arcgis/core/views/MapView.js";
import type SceneView from "@arcgis/core/views/SceneView.js";
import type { SupportedLayer } from "../../interfaces.js";
import type { QueryObject, QueryObjectProps, RendererInfo, SubtypeInfo } from "./interfaces.js";
import type { IFeatureLayer, IField } from "../../spec/rest-js-types.js";
import type { WebChart, WebChartDataFilters, WebChartQuery } from "../../spec/web-chart.js";

/**
 * Returns the renderer type and the field and normalization field if applicable.
 *
 * @param layerRenderer
 * @internal
 */
export function getRendererInfo(layerRenderer?: Renderer): RendererInfo;

/**
 * Function creating a FeatureLayer from a set of parameters.
 * It returns the created Feature Layer.
 * It is memoized (cached) to avoid multiple requests for a same feature layer.
 *
 * @internal
 */
export const getFeatureLayer: (featureLayer: IFeatureLayer) => Promise<FeatureLayer>;

/**
 * Returns the proper query object to use according to the situation.
 * If can return either a FeatureLayer or a FeatureLayerView (and possibly nothing)
 *
 * @param props
 * @internal
 */
export function getQueryObject(props: QueryObjectProps): Promise<QueryObject>;

/**
 * Fetches all the fields info a feature layer.
 *
 * @param queryObject
 * @param usePopupTemplateFieldsInfo - whether to use the aliases from the popupTemplate
 * @internal
 */
export function fetchLayerFieldsInfo(queryObject: QueryObject, usePopupTemplateFieldsInfo?: boolean): Promise<IField[]>;

/**
 * Looks into a list of fields for one having a specific name, and returning it if found.
 * Returns nothing if the field is not found.
 *
 * @param fieldName - The list of fields containing all the information regarding the fields, including the name
 * @param fields
 * @internal
 */
export function getFieldFromName(fieldName: string, fields?: IField[]): IField | undefined;

/**
 * Returns the unique ID fields or the object ID field of the given layer.
 * Also return a boolean indicating whether the returned ID field is a traditional Object ID field.
 *
 * @param layer - The layer
 * @returns An object containing the unique ID fields or the object ID field and a boolean indicating if it is a traditional Object ID field
 * When the layer is undefined, returns undefined ID fields and false for the boolean.
 * @internal
 */
export function getLayerUniqueIdOrObjectIdFields(layer?: SupportedLayer): {
    idFields: string[] | undefined;
    isTraditionalTypeOID: boolean;
};

/**
 * Returns the object ID field of the given layer and a boolean indicating whether it is a traditional Object ID field.
 * If the layer has unique ID fields defined, the first field is returned.
 *
 * @param layer - The layer
 * @internal
 */
export function getObjectIdField(layer?: SupportedLayer): {
    idField: string;
    isTraditionalTypeOID: boolean;
};

/**
 * Checks whether the given field has a valid numeric type.
 * Fields descriptions are [here](http://resources.esri.com/help/9.3/ArcGISEngine/ArcObjects/esriGeoDatabase/esriFieldType.htm).
 *
 * @param field
 * @internal
 */
export function isNumericField(field: IField): boolean;

/**
 * Checks whether the given field has a valid date type.
 * Fields descriptions are [here](http://resources.esri.com/help/9.3/ArcGISEngine/ArcObjects/esriGeoDatabase/esriFieldType.htm).
 *
 * @param field
 * @internal
 */
export function isDateField(field: IField): boolean;

/**
 * Checks whether the given field has a valid integer type.
 * Fields descriptions are [here](http://resources.esri.com/help/9.3/ArcGISEngine/ArcObjects/esriGeoDatabase/esriFieldType.htm).
 *
 * @param field
 * @internal
 */
export function isIntegerField(field: IField): boolean;

/**
 * Returns list of date field names from an array of fields of type IField
 *
 * @param fieldsInfo
 * @internal
 */
export function getLayerDateFields(fieldsInfo: IField[]): string[];

/**
 * Returns list of numeric field names from an array of fields of type IField
 *
 * @param fieldsInfo
 * @internal
 */
export function getLayerNumericFields(fieldsInfo: IField[]): string[];

/**
 * Retrieve the subtype name from the subtype id, if available.
 * Otherwise, just simply returns the original value.
 *
 * @param props - The subtypeInfo which consists of a typeIdField and a list of subtypes.
 * @returns The matching subtype name if it's a subtype field; otherwise undefined.
 * //TODO: may need to revise regrading the typeIdField property (See https://devtopia.esri.com/WebGIS/arcgis-js-api/issues/42555)
 * @internal
 */
export function getSubtypeName(props: {
    subtypeInfo?: SubtypeInfo;
    fieldName: string;
    fieldValueToMatch: number | string;
}): string | undefined;

/**
 * Retrieve subtype information from the query object.
 *
 * @param queryObject - The query object from which to retrieve the feature layer.
 * @returns subtype info like typeIdField and the subtypes, if defined; otherwise undefined.
 * //TODO: may need to revise regrading the typeIdField property (See https://devtopia.esri.com/WebGIS/arcgis-js-api/issues/42555)
 * @internal
 */
export function getLayerSubtypeInfo(queryObject: QueryObject): SubtypeInfo | undefined;

/**
 * Get the field description if the field contains subtype or domain value.
 * This field description is mainly for displaying purpose (while its id is used in queries).
 *
 * @param props - the name of the field
 * @returns The field description if available; otherwise undefined.
 * @internal
 */
export function getFieldDescription(props: {
    fieldName: string;
    fieldValueToMatch: number | string;
    subtypeInfo?: SubtypeInfo;
    fieldList?: IField[];
}): string | undefined;

/**
 * Returns feature layer field names used in its renderer.
 *
 * @param layer
 * @internal
 */
export function getRendererFieldNames(layer: SupportedLayer): Promise<string[]>;

/**
 * Returns a boolean to indicate if the feature layer is on standard feature data store or not.
 * https://doc.arcgis.com/en/arcgis-online/reference/faq.htm#anchor7
 *
 * @param layer
 * @internal
 */
export function isStandardLayer(layer: SupportedLayer): Promise<boolean>;

/**
 * Finds the matching field name for a given layer based on the fieldToMatch parameter.
 *
 * @param props
 * @internal
 */
export function findMatchingLayerFieldName(props: {
    queryObject: QueryObject;
    fieldToMatch: string;
    returnUndefinedIfNotFound?: boolean;
}): Promise<string>;

/**
 * Find the matching field in a list of fields based on the fieldToMatch parameter.
 * It first looks for a perfect match (case-sensitive), then for an approximate match (case-insensitive).
 *
 * We look for a match in 2 steps:
 *
 * 1. Perfect match: the case of the fields is exactly the same. This helps when the layer has fields with the same name but with a
 *    different case (e.g. "field" and "FiElD"). Refer to https://devtopia.esri.com/WebGIS/arcgis-charts/issues/8305
 *
 * 2: Approximate match: we upper-case the fields to compare them
 *
 * @param layerFieldList - The list of fields to search in.
 * @param fieldToMatch - The field name to match.
 * @returns The matching field if found, otherwise undefined.
 * @internal
 */
export function findMatchingField(layerFieldList: IField[], fieldToMatch: string): IField | undefined;

/**
 * Finds the matching layer fields for a list of fields.
 *
 * @param props
 * @internal
 */
export function matchFieldsList(props: {
    fieldsList: string[] | undefined;
    queryObject: SupportedLayer;
    returnUndefinedIfNotFound?: boolean;
}): Promise<string[] | undefined>;

/**
 * Builds a WebChartQuery based on information coming from the runtime filters (chart options), the config and the data source.
 * The filters by attributes from all sources are all combined together (using the AND operator).
 * Regarding the other filters (by geometry, gdb version and time extent) only one of each can be applied. The runtime filters are chosen over the config filters.
 *
 * @param props - The query object (feature layer or feature layer view)
 * @internal
 */
export function buildQueryConfig(props: {
    queryObject?: QueryObject;
    chartConfig: WebChart;
    runtimeDataFilters?: WebChartDataFilters;
    ignoreViewExtent?: boolean;
}): WebChartQuery;

/**
 * Ger the list of the fields used in the charts
 *
 * @param layer - the supported layer
 * @returns The list of fields used in the charts
 * @internal
 */
export function getFieldsUsedInCharts(layer: SupportedLayer): Promise<string[]>;

/**
 * Fetches the fields used in the chart configuration and returns the matching fields from the feature layer.
 *
 * @param config - The chart configuration
 * @param layer - The feature layer
 * @returns The list of matching fields
 * @internal
 */
export function extractFieldsUsedInConfig(config: WebChart, layer: SupportedLayer | undefined): Promise<string[]>;

/**
 * Get the list of layer fields from the series list of the chart config.
 *
 * @param layer - The layer
 * @returns The list of layer fields
 * @internal
 */
export function extractFieldsFromConfigList(layer: SupportedLayer): Promise<string[]>;

/**
 * Finds the matching layer in the view based on the provided layer's portal item id.
 * - The matching is done based on the portalItem id.
 * - If no matching layer is found, returns `undefined`.
 *
 * @param props - The map or scene view
 * @returns The matching layer in the view or undefined if not found
 * @example
 * ```ts
 * const matchingLayer = findMatchingLayerFromView({ view, portalItemId: 'abc123' });
 * ```
 * @requires
 * - view
 * - portalItemId
 * @internal
 */
export function findMatchingLayerFromView(props: {
    view?: MapView | SceneView;
    portalItemId: string;
}): FeatureLayerView | SupportedLayer | undefined;