import type SpatialReference from "../../geometry/SpatialReference.js";
import type GraphQuery from "./GraphQuery.js";
import type InputQuantizationParameters from "./InputQuantizationParameters.js";
import type OutputQuantizationParameters from "./OutputQuantizationParameters.js";
import type { GraphQueryProperties } from "./GraphQuery.js";
import type { ValidBindParameterType } from "./types.js";
import type { SpatialReferenceProperties } from "../../geometry/SpatialReference.js";

export interface GraphQueryStreamingProperties extends GraphQueryProperties, Partial<Pick<GraphQueryStreaming, "bindGeometryQuantizationParameters" | "bindParameters" | "outputQuantizationParameters" | "provenanceBehavior">> {
  /**
   * Custom output spatial reference parameters for output geometry.
   * Overrides the default lossless WGS84 quantization.
   */
  outputSpatialReference?: SpatialReferenceProperties | null;
}

/**
 * Defines a streaming query operation performed on a [knowledge graph service's](https://developers.arcgis.com/javascript/latest/api-reference/esri-rest-knowledgeGraphService.html)
 * [graph](https://developers.arcgis.com/javascript/latest/api-reference/esri-rest-knowledgeGraph-KnowledgeGraph.html) resource.
 * The [entities](https://developers.arcgis.com/javascript/latest/api-reference/esri-rest-knowledgeGraph-Entity.html) and [relationships](https://developers.arcgis.com/javascript/latest/api-reference/esri-rest-knowledgeGraph-Relationship.html)
 * in the graph are queried by sending an Esri implementation of [openCypher](https://opencypher.org/) query.
 *
 * Streaming query returns [results](https://developers.arcgis.com/javascript/latest/api-reference/esri-rest-knowledgeGraph-GraphQueryStreamingResult.html) faster than query and in small chunks that can be processed immediately.
 * Streaming query also allows [bind parameters](https://developers.arcgis.com/javascript/latest/api-reference/esri-rest-knowledgeGraph-GraphQueryStreaming.html#bindParameters).
 *
 * @since 4.25
 * @see [Sample - Query a knowledge graph](https://developers.arcgis.com/javascript/latest/sample-code/knowledgegraph-query/)
 * @see [knowledgeGraphService.executeQueryStreaming](https://developers.arcgis.com/javascript/latest/api-reference/esri-rest-knowledgeGraphService.html#executeSearchStreaming)
 * @example
 * // sample streaming query using bind parameters
 * // to search for entities with a the `name` property that matches a specific string
 * // or have their geometry within a bounding box.
 * // get all parts bought by each supplier.
 * // query returns both the supplier and the part it buys.
 * const query = `MATCH (s:Supplier)-[:buys_part]-(p:Part)
 *                WHERE s.name=$name OR esri.graph.ST_Intersects($geom, s.geometry)
 *                RETURN s,p`;
 *
 * KnowledgeGraphModule.executeQueryStreaming(
 *   knowledgeGraph,
 *   {
 *     openCypherQuery: query,
 *     bindParameters: {
 *       name: "Supplier 1",
 *       geom: new Polygon({
 *         rings: [
 *           [
 *             [-78, 38],
 *             [-78, 39],
 *             [-76, 39],
 *             [-76, 38],
 *             [-78, 38]
 *           ],
 *        ],
 *     }),
 *   }
 * }).then((streamingQueryResult)=>{
 *   // streaming query returns a readableStream which must be read to access the returned graph data
 *   readStream(streamingQueryResult);
 * })
 * @example
 * // a function to read the readable stream returned from the above query
 * const readStream = async (streamingQueryResult) => {
 *   let time = Date.now();
 *   let reader = streamingQueryResult.resultRowsStream.getReader();
 *   try {
 *     while (true) {
 *       const { done, value } = await reader.read();
 *       if (done) {
 *         console.log(`All records returned, stream closed: ${(Date.now() - time) / 1000} seconds`);
 *         break;
 *       }
 *       console.log(`Streaming chunk returned in: ${(Date.now() - time) / 1000} seconds`, value);
 *       // use the results
 *       // list the parts bought by each supplier
 *       let supplierParts = {};
 *       // each element of the result array will contain one supplier and one part it buys
 *       for (var v in value){
 *         let supplier = value[v][0].properties.Name
 *         let part = value [v][1].properties.Name
 *         if(!(supplier in supplierParts)){
 *           supplierParts[supplier] = [];
 *         }
 *         // collect parts by supplier that buys them
 *         supplierParts[supplier].push(part);
 *         console.log(supplierParts);
 *         // result printed to the console: {Supplier 1:[Part1], Supplier 3:[Part2, Part3]}
 *       }
 *     }
 *   } catch (err) {
 *     if (err.name === "AbortError") {
 *       console.log("Request aborted as expected");
 *     } else {
 *       throw err;
 *     }
 *   }
 * };
 * @example
 * // result stream from above query after it has been read
 * "Streaming chunk returned in: 0.082 seconds"
 * [
 *   [{
 *     "declaredClass": "esri.rest.knowledgeGraph.Entity",
 *     "properties": {
 *       "Name": "Supplier 1",
 *       "City": "Washington DC",
 *       "EmployeeCount": 31
 *     },
 *     "typeName": "Supplier",
 *     "id": "{57FDF2F3-34C8-48EF-9A3B-76ED9314C4D2}"
 *   },{
 *     "declaredClass": "esri.rest.knowledgeGraph.Entity",
 *     "properties": {
 *       "Part_ID": 695401,
 *       "Name": "Part1",
 *       "Minimum_quantity": 360
 *     },
 *     "typeName": "Part",
 *     "id": "{IWN51W4-1AW8-A2W6-1AW5F-1AW8F9F4W51AS}",
 *   }],
 *   [{
 *     "declaredClass": "esri.rest.knowledgeGraph.Entity",
 *     "properties": {
 *       "Name": "Supplier 2",
 *       "City": "Baltimore",
 *       "EmployeeCount": 53
 *     },
 *     "typeName": "Supplier",
 *     "id": "{1A4W8F5W-1WA8-5W6A-A1W8-A1W5F8W3S482A}"
 *   },{
 *     "declaredClass": "esri.rest.knowledgeGraph.Entity",
 *     "properties": {
 *       "Part_ID": 695401,
 *       "Name": "Part2",
 *       "Minimum_quantity": 2500
 *     },
 *     "typeName": "Part",
 *     "id": "{A1W5F8W4F-A1W8-1894-16A5-A86WF4A8SFWD}",
 *   }],
 *   [{
 *     "declaredClass": "esri.rest.knowledgeGraph.Entity",
 *     "properties": {
 *       "Name": "Supplier 2",
 *       "City": "Baltimore",
 *       "EmployeeCount": 53
 *     },
 *     "typeName": "Supplier",
 *     "id": "{1A4W8F5W-1WA8-5W6A-A1W8-L5H4G8RT1PK3}"
 *   },{
 *     "declaredClass": "esri.rest.knowledgeGraph.Entity",
 *     "properties": {
 *       "Part_ID": 695401,
 *       "Name": "Part3",
 *       "Minimum_quantity": 5000
 *     },
 *     "typeName": "Part",
 *     "id": "{PTJ51FT-KY4H-1GY5-G1Y8-G1Y5K49G8Y4GHJ}",
 *   }]
 * ]
 * @example
 * // aborting the query
 * // create instance of native browser abort controller
 * const controller = new AbortController();
 * // create query
 * KnowledgeGraphModule
 *   .executeQueryStreaming(
 *     knowledgeGraph,
 *     {
 *       openCypherQuery: "MATCH (n) RETURN n LIMIT 100",
 *     },
 *     {
 *       signal: controller.signal,
 *     }
 *   )
 *   .then((streamingQueryResult) => {
 *     readStream(streamingQueryResult);
 *   })
 *   // indicate that the stream was aborted
 *   .catch((err) => {
 *     if (err.name === "AbortError") {
 *       console.log("Request aborted as expected");
 *     }
 *
 * // abort the query after half a second
 * setTimeout(() => {
 *   console.log("Sending abort signal");
 *   controller.abort();
 * }, 500);
 */
export default class GraphQueryStreaming extends GraphQuery {
  constructor(properties?: GraphQueryStreamingProperties);
  /**
   * Custom quantization parameters for input geometry that compresses geometry for transfer to the server.
   * Overrides the default lossless WGS84 quantization.
   *
   * @example
   * // sample implementation of an input quantization parameter
   * // query entities within a bounding box
   * const query = "MATCH (n) WHERE esri.graph.ST_Intersects($geom, n.geometry) RETURN n"
   *
   * KnowledgeGraphModule.executeQueryStreaming(
   *   knowledgeGraph,
   *   {
   *     openCypherQuery: query,
   *     bindParameters: {
   *       param_filter_geom: new Polygon({
   *         rings: [
   *           [
   *             [-89, -89],
   *             [89, -89],
   *             [89, 89],
   *             [-89, 89],
   *             [-89, -89],
   *           ],
   *         ],
   *       }),
   *     },
   *     inputQuantizationParameters: {
   *       xyResolution: 0.003,
   *       xFalseOrigin: 25,
   *       yFalseOrigin: 25,
   *       zResolution: 1,
   *       zFalseOrigin: 1,
   *       mResolution: 1,
   *       mFalseOrigin: 1,
   *     },
   *    }
   *   }
   * );
   */
  accessor bindGeometryQuantizationParameters: InputQuantizationParameters | null | undefined;
  /**
   * Specifies a set of parameters containing data to be included in the query.
   *
   * > [!WARNING]
   * >
   * > **Note**
   * >
   * > Check the service definition for the [knowledgeGraphService](https://developers.arcgis.com/javascript/latest/api-reference/esri-rest-knowledgeGraphService.html)
   * > for valid geometry types. Not all external graph stores will supported all geometry types.
   *
   * @example
   * // bind a polygon to search for entities withing a bounding box.
   * const query = "MATCH (n) WHERE esri.graph.ST_Intersects($geom, n.geometry) RETURN n";
   *
   * KnowledgeGraphModule.executeQueryStreaming(
   *   knowledgeGraph,
   *   {
   *     openCypherQuery: query,
   *     bindParameters: {
   *       geom: new Polygon({
   *         rings: [
   *           [
   *             [-89, -89],
   *             [89, -89],
   *             [89, 89],
   *             [-89, 89],
   *             [-89, -89],
   *           ],
   *        ],
   *     }),
   *   }
   * }).then((streamingQueryResult)=>{
   *   // streaming query returns a readableStream which must be read to access the returned graph data
   *   readStream(streamingQueryResult);
   * })
   * @example
   * // bind a list of ids
   * // get all relationships between suppliers and a select list of parts.
   * const query = "MATCH (s:Supplier)-[rel]-(p:Part) WHERE p.globalid IN $ids RETURN rel";
   *
   * KnowledgeGraphModule.executeQueryStreaming(
   *   knowledgeGraph,
   *   {
   *     openCypherQuery: query,
   *     bindParameters: {
   *       ids:[
   *          "{WIHPTR45-1RO5-9HY1-FK50-P1J5U8D1FER7}",
   *          "{SSOE5G4O-T7T8-4G8Y-2RD1-P8D1A7G4S7EA}",
   *          "{2SE8HE8D-81ES-9HY1-1SE8-OPAWON41869S}",
   *          "{1SE8E6G8-5DT8-S8E1-6S8E-D8R4FJ8S8S1S}",
   *          "{SE86I2BD-8R73-2SE8-6S8E-7R8DR86S4SD6}",
   *          "{N2T8K52R-2SDE-1SEG-S8ES-6SE8HUKY6AIN}",
   *          "{2AWAGP86-SESR-5SE8-4SE8-68RD66846SPL}",
   *       ],
   *     }),
   *   }
   * }).then((streamingQueryResult)=>{
   *   // streaming query returns a readableStream which must be read to access the returned graph data
   *   readStream(streamingQueryResult);
   * })
   */
  accessor bindParameters: Record<string, ValidBindParameterType> | null | undefined;
  /**
   * Used to project the geometry onto a virtual grid, likely representing
   * pixels on the screen. Geometry coordinates are converted to integers
   * by building a grid with a resolution matching the
   * [OutputQuantizationParameters.tolerance](https://developers.arcgis.com/javascript/latest/api-reference/esri-rest-knowledgeGraph-OutputQuantizationParameters.html#tolerance).
   * Each coordinate is then snapped to one pixel on the grid.
   *
   * @example
   * // sample implementation of an output quantization parameter
   *
   * // query entities within a bounding box.
   * const query = "MATCH (n) WHERE esri.graph.ST_Intersects($geom, n.geometry) RETURN n"
   *
   * KnowledgeGraphModule.executeQueryStreaming(
   *   knowledgeGraph,
   *   {
   *     openCypherQuery: query,
   *     bindParameters: {
   *       geom: new Polygon({
   *         rings: [
   *           [
   *             [-89, -89],
   *             [89, -89],
   *             [89, 89],
   *             [-89, 89],
   *             [-89, -89],
   *           ],
   *         ],
   *       }),
   *     },
   *     outputQuantizationParameters: {
   *       extent: {
   *         xmax: 30,
   *         xmin: 20,
   *         ymax: 30,
   *         ymin: 20,
   *       },
   *       tolerance: 0.001,
   *       quantizeMode: "view",
   *     }
   *    }
   *   }
   * );
   */
  accessor outputQuantizationParameters: OutputQuantizationParameters | null | undefined;
  /**
   * Custom output spatial reference parameters for output geometry.
   * Overrides the default lossless WGS84 quantization.
   */
  get outputSpatialReference(): SpatialReference | null | undefined;
  set outputSpatialReference(value: SpatialReferenceProperties | null | undefined);
  /**
   * Enables provenance to be used and returned in the query.
   * This will only be available if the service has [provenance enabled](https://developers.arcgis.com/javascript/latest/api-reference/esri-rest-knowledgeGraph-ServiceDefinition.html#supportsProvenance).
   *
   * @example
   * // query entities within a bounding box that have a specific provenance.
   * const query = "MATCH (n), (prov:Provenance)
   * WHERE esri.graph.ST_Intersects($geom, n.geometry) and prov.instanceID in $idList
   * RETURN n, prov"
   *
   * KnowledgeGraphModule.executeQueryStreaming(
   *   knowledgeGraph,
   *   {
   *     openCypherQuery: query,
   *     provenanceBehavior: "include",
   *     bindParameters: {
   *      idList: ['{59ACF2B8-1A28-4825-9228-E0CE1AFF184A}',
   *               '{AFF1A752-1A28-4825-9228-EFFEE152184A}',
   *               '{4825AABB-1A28-4825-9228-E0CE1AFF9712}'],
   *       geom: new Polygon({
   *         rings: [
   *           [
   *             [-89, -89],
   *             [89, -89],
   *             [89, 89],
   *             [-89, 89],
   *             [-89, -89],
   *           ],
   *         ],
   *       }),
   *     },
   *     outputQuantizationParameters: {
   *       extent: {
   *         xmax: 30,
   *         xmin: 20,
   *         ymax: 30,
   *         ymin: 20,
   *       },
   *       tolerance: 0.001,
   *       quantizeMode: "view",
   *     }
   *   }
   * );
   */
  accessor provenanceBehavior: GraphQueryStreamingProvenanceBehavior | null | undefined;
}

export type GraphQueryStreamingProvenanceBehavior = "exclude" | "include";