import type MapView from "@arcgis/core/views/MapView.js";
import type SceneView from "@arcgis/core/views/SceneView.js";
import type FeatureLayerView from "@arcgis/core/views/layers/FeatureLayerView.js";
import type { SupportedLayer } from "../../interfaces.js";
import type { WebChart, WebChartDataFilters, WebChartPieChartSlice, WebGaugeChart } from "../../spec/web-chart.js";
import type { WebChartDataItem, WebChartDataItemValue, WebChartDataTypes } from "../../spec/data-source.js";
import type { ActionModes, CursorRange } from "../../utils/cursor/interfaces.js";
import type { LoaderColors, SelectionData, SelectionTheme } from "../../utils/misc/interfaces.js";
import type { ChartElementLimit } from "../../utils/defaults/interfaces.js";

export interface NotifyOptions {
  closable?: boolean;
}

/**
 * Describes the signature of the callback function used to customize the pie chart legend value labels.
 *
 * @param value
 * @param percent
 */
export type PieChartLegendValueLabelFormatCallback = (value?: number, percent?: number) => string;

/**
 * Describes the signature of the callback function used to customize a guide's tooltip.
 *
 * @param props
 */
export type GuideTooltipFormatCallback = (props: GuideTooltipFormatCallbackProps) => string;

/**
 * Describes the signature of the callback function used to customize an axis labels.
 * - `value`: the displayed label.
 * - `originalValue`: provided when the `value` is an alias or coded domain description.
 *
 * @param value
 * @param originalValue
 */
export type AxisLabelFormatCallback = (value: Date | number | string | null, originalValue?: WebChartDataItemValue) => string;

/**
 * Describes the signature of the callback function used to customize a gauge inner label
 *
 * @param value
 */
export type GaugeInnerLabelFormatCallback = (value?: number) => string;

/**
 * Describes the signature of the callback function executed before rendering the chart.
 * The `slices` property will be only provided for the pie charts.
 *
 * @param props
 */
export type PreRenderCallback = (props: {
    chartConfig: WebChart | WebGaugeChart;
    data?: WebChartDataTypes;
    slices?: WebChartPieChartSlice[];
}) => Promise<WebChart | WebGaugeChart>;

/** JSON representation of the runtime chart options for the update or initialization. */
export interface ChartOptions {
  /** Whether to allow the object id to be used as a statistic other than `COUNT`. */
  allowUsingObjectIdStat?: boolean;
  /**
   * Whether to consider a `null` statistic as valid. If set to `false` and the data has only
   * null values, the chart's dataset will be considered empty:
   * - the corresponding warning event will be sent
   * - the "no data" message will be displayed (unless `showUIMesssages` is set to `false`)
   */
  nullAsValid?: boolean;
  /**
   * Whether to display or hide all UI messages (e.g. no data warnings, or when the logarithmic scale can't be applied to an
   * axis when the data contains values <= 0).
   */
  showUIMessages?: boolean;
  /**
   * A property representing the selection to apply on the chart.
   *
   * The properties are considered in the following order:
   * - `selectionData.selectionOIDs`: an array of object Ids (for Feature Layer) representing a selection to apply to the chart.
   * - `selectionData.selectionItems`: an array of data items representing a selection to apply to the chart.
   */
  selectionData?: SelectionData;
  /**
   * Indicates if the object ids need to be computed whenever a selection is made on or passed to the chart.
   * Only considered for a data source using a feature layer.
   */
  returnSelectionOIDs?: boolean;
  /** Indicates if the selection indexes need to be computed whenever a selection is made on or passed to the chart. */
  returnSelectionIndexes?: boolean;
  /**
   * Allows the features extent to be returned, through the `arcgisChartsJSDataProcessComplete` and `arcgisChartsJSSelectionComplete` events payload.
   * Applies only to:
   *   - Charts using an aggregation
   *   - Data source using a feature layer compatible with envelope aggregation
   *
   * The extent (IExtent) is returned through a property called `arcgis_charts_features_extent`.
   */
  returnFeaturesExtent?: boolean;
  /**
   * Used to provide a customized theme for the selected and non selected elements.
   * If no style is provided for the selected elements, a default selection is applied.
   * If no style is provided for the non selected elements, the chart's style is applied.
   */
  selectionTheme?: SelectionTheme;
  /** Used to perform client-side queries. */
  view?: MapView | SceneView;
  /** Used to perform queries. */
  layer?: FeatureLayerView | SupportedLayer;
  /** Enable filter by selection. */
  filterBySelection?: boolean;
  /**
   * Re-initialize chart object if the chart object is disposed. Honored only during the update call.
   * Default is `true`.
   */
  autoRecreateChart?: boolean;
  /**
   * Indicates whether to automatically inverse the data label text color when it's too dark or too light.
   *
   * **Note**: Currently this feature is only available for stacked bar series and pie charts with `dataLabelsInside` set to true.
   */
  autoInverseDataLabelTextColor?: boolean;
  /** Optional create message to be displayed in the chart container. */
  createMessage?: string;
  /** Used to hide the loader animation (curtain and spinner), showed by default at every update. */
  hideLoaderAnimation?: boolean;
  /**
   * Hide the series from the chart if it doesn't have data (i.e. empty).
   * For example, after apply data filters, filter by attribute or geometry (as when using the filter by extent).
   */
  hideEmptySeries?: boolean;
  /**
   * Used to customize the loader's colors (spinner and curtain).
   * The loader is displayed when the chart is loading data or when the chart is being updated unless when `hideLoaderAnimation` is set to `true`.
   */
  loaderColors?: LoaderColors;
  /**
   * Whether to replace the missing categories from a count aggregation with 0s in the chart's data.
   *
   * When data is retrieved from the server, the server excludes all features having a count of 0 from the response; hence resulting in missing entries on the charts.
   * When this property is set to true, a process is applied to complete the dataset with 0s for the empty or missing categories, to display them on the chart without gaps.
   */
  replaceNoValueCategoryWithZero?: boolean;
  /**
   * Used to customize the chart's limits, especially the maximum number of elements (bars, markers...) to be displayed.
   * The chart's behavior once that limit is reached can be adjusted through the `behaviorAfterLimit` nested property, to either reject the creation
   * or update of the chart, or render the elements up to the given limits.
   *
   * Not applicable for scatterplot and histogram.
   */
  chartLimits?: ChartElementLimit;
  /**
   * Set the chart's mode.
   * - `zoom`: Allows to zoom on the chart. All selection is disabled.
   * - `mono-selection`: The selection is enabled but only one element can be selected. Zoom is disabled.
   * - `multi-selection`: The selection is enabled and allows selecting multiple elements. Zoom is disabled.
   * - `multi-selection-with-ctrl-key`: The selection is enabled and allows selecting multiple elements when the ctrl/cmd key is pressed. Zoom is disabled.
   * - `none`: The zoom and selection features are disabled.
   */
  actionMode?: ActionModes;
  /**
   * Sets the cursor range.
   * - `multi-axes`: Allows to draw a free rectangle
   * - `x-axis`: Allows to draw a rectangle bound to the x-axis
   * - `y-axis`: Allows to draw a rectangle bound to the y-axis
   * - `undefined`: Defaults to a preset behavior according to the chart type and action mode
   *
   * Applies only when the action mode is set to zoom or multi-selection.
   */
  cursorRange?: CursorRange;
  /** When `true`, the chart gets a full refresh. */
  dataUpdated?: boolean;
  /**
   * Helps to identify an update triggered after a view extent change.
   * This is used to support a selection workflow while the filter by extent mode is enabled.
   */
  viewExtentUpdated?: boolean;
  /** Used to apply runtime filters to the chart's data while in Feature Layer mode. */
  runtimeDataFilters?: WebChartDataFilters;
  /**
   * This property will be effective when the component's `view` is provided or when the `layer` is set to a `FeatureLayerView`.
   * By default the queries made by the chart will consider the view's extent as the unique geometry filter applicable. This implies ignoring any
   * geometry passed through the config (component.config.dataFilters) or the runtime filters (component.runtimeDataFilters).
   * Setting this property to `false` signals the queries to ignore the view's extent, whether an additional geometry filter is provided or not.
   */
  ignoreViewExtent?: boolean;
  /**
   * Can be used when creating or updating a chart compatible with time binning.
   *
   * When `true`, the series properties `unit` and `size` become optional and will be automatically set to values that fit the data set.
   */
  setTimeBinningInfoWhenNotProvided?: boolean;
  /**
   * Can be used to disable the default setting that uses debounce functions to handle the visibility of markers
   * (for scatterplots and line charts) that are outside of plotting area when min/max bound changes and/or
   * when zoom action is performed (via chart cursor or scrollbar), to increase performance.
   *
   * This property will be set when the chart is created and cannot be updated after that.
   *
   * It's recommended that this property should only be set to `true` for small datasets.
   */
  ignoreSmoothRenderingLimit?: boolean;
  /** Used to customize how the tooltips are rendered. The callback function can return an HTML string and the tags will be interpreted properly. */
  tooltipFormatter?: TooltipFormatters;
  dataLabelFormatter?: BarAndLineDataLabelFormatCallback | HistogramLabelFormatCallback | PieChartLabelFormatCallback | ScatterplotLabelFormatCallback;
  /** Used to customize how the guides' tooltip are rendered. The callback function can return an HTML string and the tags will be interpreted properly. */
  guideTooltipFormatter?: GuideTooltipFormatCallback;
  /** Used to customize how the x-axis labels are rendered. The callback function can return an HTML string and the tags will be interpreted properly. */
  xAxisLabelFormatter?: AxisLabelFormatCallback;
  /**
   * Used to customize how the y-axis labels are rendered. The callback function can return an HTML string and the tags will be interpreted properly.
   * Not applicable to gauge.
   */
  yAxisLabelFormatter?: AxisLabelFormatCallback;
  /**
   * Used to customize how the secondary y-axis labels are rendered on a dual axis chart. The callback function can return an HTML string and the tags will be interpreted properly.
   * Not applicable to gauge.
   */
  secondaryYAxisLabelFormatter?: AxisLabelFormatCallback;
  pieChartLegendValueLabelFormatter?: PieChartLegendValueLabelFormatCallback;
  /** Used to customize the gauge inner label. The callback function can return an HTML string and the tags will be interpreted properly. */
  gaugeInnerLabelFormatter?: GaugeInnerLabelFormatCallback;
  /**
   * Used to disable the toggling of legend items for xy chart series, pie slices, histogram overlays, and scatterplot trend line.
   *
   * Not applicable to gauge.
   */
  disableTogglingLegendItems?: boolean;
  /**
   * Callback function executed after the data has been processed and before the chart renders.
   * Can be used to alter the config from information extracted from the data for instance.
   * Only applicable to pie chart and gauge.
   */
  chartWillRender?: PreRenderCallback;
  /** When `true` read the fields from `layer.popupTemplate` and use their alias when rendering tooltips. */
  usePopupTemplateFieldsInfo?: boolean;
}

/**
 * Describes the signature of the callback function used to customize the pie chart data labels and tooltips.
 *
 * @param category
 * @param value
 * @param percent
 * @param originalCategory
 */
export type PieChartLabelFormatCallback = (category: string, value?: number, percent?: number, originalCategory?: WebChartDataItemValue) => string;

/**
 * Describes the signature of the callback function used to customize the histogram tooltips.
 *
 * @param count
 * @param binMinValue
 * @param binMaxValue
 */
export type HistogramLabelFormatCallback = (count: number, binMinValue: number, binMaxValue: number) => string;

/**
 * Describes the signature of the callback function used to customize the scatterplot tooltips.
 *
 * @param x
 * @param y
 * @param sizePolicyValue
 * @param dataContext
 */
export type ScatterplotLabelFormatCallback = (x: number, y: number, sizePolicyValue?: number, dataContext?: WebChartDataItem) => string;

/**
 * Describes the signature of the callback function used to customize the bar and line chart data labels.
 *
 * @param statValue
 * @param percent
 */
export type BarAndLineDataLabelFormatCallback = (statValue: number, percent?: number) => string;

export interface GuideTooltipFormatCallbackProps {
  guideName: string;
  start: Date | number | string;
  end?: Date | number | string;
  guideIndex: number;
}

export type TooltipFormatters = BarAndLineTooltipFormatCallback | BoxPlotTooltipFormatCallback | HistogramLabelFormatCallback | PieChartLabelFormatCallback | ScatterplotLabelFormatCallback;

/**
 * Describes the signature of the callback function used to customize the bar and line chart plot tooltips.
 *
 * @param props
 */
export type BarAndLineTooltipFormatCallback = (props: BarAndLineTooltipFormatCallbackProps) => string;

/**
 * Describes the signature of the callback function used to customize the box plot tooltips.
 *
 * @param props
 */
export type BoxPlotTooltipFormatCallback = (props: BoxPlotTooltipFormatCallbackProps) => string;

/** Describes the interface of the object passed to the callback function used to customize the bar and line chart tooltips. */
export interface BarAndLineTooltipFormatCallbackProps {
  seriesName: string;
  statValue?: number;
  percentValue?: number;
  xValue?: Date | number | string;
  originalXValue?: WebChartDataItemValue;
  dateBinningRange?: [number, number];
}

export interface BoxPlotTooltipFormatCallbackProps {
  seriesName: string;
  dataContext: WebChartDataItem;
  xValue?: Date | number | string;
  originalXValue?: WebChartDataItemValue;
}