import type { JSONSupport } from "../../core/JSONSupport.js";
import type { FieldValueType, DomainUnion, FieldType } from "./types.js";
import type { InheritedDomainProperties } from "./InheritedDomain.js";
import type { CodedValueDomainProperties } from "./CodedValueDomain.js";
import type { RangeDomainProperties } from "./RangeDomain.js";

export interface FieldProperties extends Partial<Pick<Field, "alias" | "defaultValue" | "description" | "editable" | "length" | "name" | "nullable" | "type" | "valueType">> {
  /**
   * The domain associated with the field. Domains are used to constrain the values allowed
   * in a field. There are two types of domains: [RangeDomain](https://developers.arcgis.com/javascript/latest/references/core/layers/support/RangeDomain/)
   * and [CodedValueDomain](https://developers.arcgis.com/javascript/latest/references/core/layers/support/CodedValueDomain/).
   *
   * @example
   * // print out the coded domain values when the layer is loaded
   * const layerView = await view.whenLayerView(featureLayer)
   * await reactiveUtils.whenOnce(() => !layerView.updating);
   *
   * featureLayer.fields.forEach((field) => {
   *   if (!field.domain) {
   *     return;
   *   }
   *
   *   let domain = field.domain
   *   console.log(field.name, domain.type, domain.name);
   *
   *   if (domain.type === "coded-value"){
   *     domain.codedValues.forEach((codeValue) => {
   *       console.log("name:", codeValue.name, "code:", codeValue.code);
   *     });
   *   }
   * });
   */
  domain?: ((RangeDomainProperties & { type: "range" }) | (CodedValueDomainProperties & { type: "coded-value" }) | (InheritedDomainProperties & { type: "inherited" })) | null;
}

/**
 * Information about each field in a layer. Field objects must be constructed when
 * creating a [FeatureLayer](https://developers.arcgis.com/javascript/latest/references/core/layers/FeatureLayer/) from client-side graphics.
 * This class allows you to define the schema of each field in the FeatureLayer.
 * Note that you do not need to add fields to the constructor of a FeatureLayer loaded from a
 * service since they are already defined by the service. See the
 * sample below for more information about using this module.
 *
 * @since 4.0
 * @see [Layer](https://developers.arcgis.com/javascript/latest/references/core/layers/Layer/)
 * @see [FeatureLayer](https://developers.arcgis.com/javascript/latest/references/core/layers/FeatureLayer/)
 * @see [Sample - Create a FeatureLayer with client side graphics](https://developers.arcgis.com/javascript/latest/sample-code/layers-featurelayer-collection/)
 * @example
 * // Each object in this array is autocast as
 * // an instance of esri/layers/support/Field
 * let fields = [
 *   {
 *     name: "ObjectID",
 *     alias: "ObjectID",
 *     type: "oid"
 *   }, {
 *     name: "title",
 *     alias: "title",
 *     type: "string"
 *   }, {
 *     name: "type",
 *     alias: "type",
 *     type: "string"
 *   }, {
 *     name: "mag",
 *     alias: "Magnitude",
 *     type: "double"
 * }];
 *
 * // add the array of fields to a feature layer
 * // created from client-side graphics
 * featureLayer.set({
 *   fields: fields,
 *   objectIdField: "ObjectID"
 * });
 */
export default class Field extends JSONSupport {
  constructor(properties?: FieldProperties);
  /** The alias of the field. */
  accessor alias: string | null | undefined;
  /**
   * The default value set for the field.
   *
   * @since 4.9
   */
  accessor defaultValue: string | number | null | undefined;
  /**
   * Contains information describing the purpose of each field.
   *
   * @since 4.12
   * @see [Blog - Describing layer attributes with field descriptions](https://www.esri.com/arcgis-blog/products/arcgis-online/data-management/describing-layer-attributes-with-field-descriptions-march-2019/)
   * @see [Describe attribute fields - ArcGIS Online Help](https://doc.arcgis.com/en/arcgis-online/manage-data/describe-fields.htm)
   */
  accessor description: string | null | undefined;
  /**
   * The domain associated with the field. Domains are used to constrain the values allowed
   * in a field. There are two types of domains: [RangeDomain](https://developers.arcgis.com/javascript/latest/references/core/layers/support/RangeDomain/)
   * and [CodedValueDomain](https://developers.arcgis.com/javascript/latest/references/core/layers/support/CodedValueDomain/).
   *
   * @example
   * // print out the coded domain values when the layer is loaded
   * const layerView = await view.whenLayerView(featureLayer)
   * await reactiveUtils.whenOnce(() => !layerView.updating);
   *
   * featureLayer.fields.forEach((field) => {
   *   if (!field.domain) {
   *     return;
   *   }
   *
   *   let domain = field.domain
   *   console.log(field.name, domain.type, domain.name);
   *
   *   if (domain.type === "coded-value"){
   *     domain.codedValues.forEach((codeValue) => {
   *       console.log("name:", codeValue.name, "code:", codeValue.code);
   *     });
   *   }
   * });
   */
  get domain(): DomainUnion | null | undefined;
  set domain(value: ((RangeDomainProperties & { type: "range" }) | (CodedValueDomainProperties & { type: "coded-value" }) | (InheritedDomainProperties & { type: "inherited" })) | null | undefined);
  /**
   * Indicates whether the field is editable.
   *
   * @default true
   */
  accessor editable: boolean;
  /** The field length. */
  accessor length: number | null | undefined;
  /** The name of the field. */
  accessor name: string;
  /**
   * Indicates if the field can accept `null` values. *Requires ArcGIS Server version 10.1 or greater.*
   *
   * @default true
   */
  accessor nullable: boolean;
  /**
   * The data type of the field.
   *
   * > [!CAUTION]
   * >
   * > **Notes**
   * >
   * > Support for `big-integer`, `date-only`, `time-only` and `timestamp-offset` fields was added in beta for [FeatureLayer](https://developers.arcgis.com/javascript/latest/references/core/layers/FeatureLayer/) and [MapImageLayer](https://developers.arcgis.com/javascript/latest/references/core/layers/MapImageLayer/) at version 4.28.
   * > The `big-integer` field type is in beta and may not be fully supported in the JavaScript SDK.
   * > The JavaScript SDK only supports safe integers that fall between  -9007199254740991 [(minimum)](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Number/MIN_SAFE_INTEGER)
   * > and 9007199254740991 [(maximum)](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Number/MAX_SAFE_INTEGER). Numbers that fall outside of this
   * > range will be incorrect due to rounding, resulting in graphical or performance issues.
   * > For example, a calculation using Arcade expressions that results in a value exceeding these safe integers may be incorrect since rounding can occur.
   * > Web applications can only edit whole numbers between -9007199254740991 and 9007199254740991.
   *
   * @see [ArcGIS field data types](https://pro.arcgis.com/en/pro-app/latest/help/data/geodatabases/overview/arcgis-field-data-types.htm)
   */
  type: FieldType;
  /**
   * The types of values that can be assigned to a field. See the table below for a list of possible values.
   * | Mode | Description |
   * | ------|------------ |
   * | binary | Only one of two values are possible for each feature. Some examples include the following: `On or off`, `Yes or no`, `True or false`, or `Inhabited or vacant`. |
   * | coordinate | These fields store a geographic coordinate value such as x, y, z, latitude, or longitude. |
   * | count-or-amount | Integers (no decimal) that represent how many or how much there is of a specific attribute. |
   * | currency | Numeric values that represent a monetary value. |
   * | date-and-time | Values in this field store explicit dates and times or date references such as days of the week, months, or years. |
   * | description | Text that provides a longer description of the feature, more than just a name or title. |
   * | email-address | Text that represents an email address. |
   * | location-or-place-name | Values in this field represent a geographic location. Examples of values in such a field include a street address, city name, region, building name (such as A.K. Smiley Public Library), attraction name (such as Alameda County Fairgrounds or Cairngorms National Park), postal code, or country. |
   * | measurement | A number that reflects a characteristic that you can precisely measure. |
   * | name-or-title | Text that represents a name, title, label, or keyword for each feature. |
   * | none | No specified type. |
   * | ordered-or-ranked | The values in this field represent a feature's status in an ordered or ranked list. For example, a feature could be one of the following: `Small, medium, large`, `First, second, third, fourth`, or `Informational, warning, error, failure`.|
   * | percentage-or-ratio | Number values in this field reflect the relationship between different quantities. |
   * | phone-number | Text that represents a phone number. |
   * | type-or-category | Types or categories that group features based on common characteristics. |
   * | unique-identifier | The values in this field are used to positively distinguish one feature or entity from another. |
   *
   * @since 4.12
   * @see [Blog - Describing layer attributes with field descriptions](https://www.esri.com/arcgis-blog/products/arcgis-online/data-management/describing-layer-attributes-with-field-descriptions-march-2019/)
   * @see [Describe attribute fields - ArcGIS Online Help](https://doc.arcgis.com/en/arcgis-online/manage-data/describe-fields.htm)
   */
  accessor valueType: FieldValueType | null | undefined;
}