#  Copyright © 2025 Bentley Systems, Incorporated
#  Licensed under the Apache License, Version 2.0 (the "License");
#  you may not use this file except in compliance with the License.
#  You may obtain a copy of the License at
#      http://www.apache.org/licenses/LICENSE-2.0
#  Unless required by applicable law or agreed to in writing, software
#  distributed under the License is distributed on an "AS IS" BASIS,
#  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
#  See the License for the specific language governing permissions and
#  limitations under the License.
"""
Block Model API
=============


    The Block Model API provides the ability to manage and report on block models in your Evo workspaces. Enable your
    product with Evo connected workflows by integrating with the Seequent Block Model API.

    The Block Model API supports a range of sub-blocking options, and both full and partial updates to block models,
    including updates of specified columns and/or sub-volumes. Block models are versioned, and the service supports
    reporting on the material content of current or previous versions, and comparing the content between versions of
    a block model.

    For more information on the Block Model API, see [Overview](/docs/guides/blockmodel/), or the API references here.


This code is generated from the OpenAPI specification for Block Model API.
API version: 1.41.3
"""

from evo.common.connector import APIConnector
from evo.common.data import EmptyResponse, RequestMethod
from evo.common.utils import get_header_metadata

from ..models import *  # noqa: F403

__all__ = ["VersionsApi"]


class VersionsApi:
    """API client for the Versions endpoint.

    NOTE: This class is auto generated by OpenAPI Generator
    Ref: https://openapi-generator.tech

    Do not edit the class manually.

    :param connector: Client for communicating with the API.
    """

    def __init__(self, connector: APIConnector):
        self.connector = connector

    async def get_deltas_for_block_model(
        self,
        version_id: str,
        workspace_id: str,
        org_id: str,
        bm_id: str,
        delta_request_data: DeltaRequestData,  # noqa: F405
        additional_headers: dict[str, str] | None = None,
        request_timeout: int | float | tuple[int | float, int | float] | None = None,
    ) -> DeltaResponseData:  # noqa: F405
        """Check for deltas

        Checks if there have been any changes to the block model within a given bounding box, between two specified versions.  Changes are searched for within a range of versions. The first version searched for changes is the version after `version_id`. The last version that is searched is the version that is specified by `end_version_id` (inclusive), or if `end_version_id` isn't specified, the latest version of the block model.  This request only checks for updates or deletions for specified columns. The columns are specified as list of `column_id` UUIDs or a wildcard (`\"*\"`), for all columns that exist within the `version_id` version (the starting version). Using column titles is not supported on this endpoint.  For this endpoint, changes that are considered are: - Addition of any new columns, regardless of the columns specified in this request - Deletions of any of the columns specified - Updates to any of the columns specified, within the given bounding box. When block models are updated, a bounding box that encloses all updated blocks is computed. Only updates that have a bounding box that intersects with the specified bounding box are included.  Column renames aren't considered as part of this and they do not count as a change.  If no changes are found, then the response will have a status code of 304, with no content in the response.  If there are changes found, then the `verbose` flag inside the request body changes the response. If `verbose` is false, then the response body will be empty. If `verbose` is true, then the response body will contain information about the difference in versions. For clarity, if no changes are found, then the `verbose` flag does nothing, as in that case the system will always return a 304 with no content.  All workspace roles can use this endpoint.

        :param version_id: ID of the version of the block model this call is scoped to. Represented as a v4 UUID.
            Format: `uuid`
            Example: `'3e9ce8de-f6ba-4920-8c6e-0882e90f0ed7'`
        :param workspace_id: ID of the workspace this call is scoped to. Represented as a v4 UUID.
            Format: `uuid`
            Example: `'860be2f5-fe06-4c1b-ac8b-7d34d2b6d2ef'`
        :param org_id: ID of the organization this call is scoped to. Represented as a v4 UUID.
            Format: `uuid`
            Example: `'bf1a040c-8c58-4bc2-bec2-c5ae7de8bd84'`
        :param bm_id: ID of the block model this call is scoped to. Represented as a v4 UUID.
            Format: `uuid`
            Example: `'e3c277c2-edc6-4a7a-8380-251dd19231f2'`
        :param delta_request_data:
            Example: `endpoints.DeltaRequestData()`
        :param additional_headers: (optional) Additional headers to send with the request.
        :param request_timeout: (optional) Timeout setting for this request. If one number is provided, it will be the
            total request timeout. It can also be a pair (tuple) of (connection, read) timeouts.

        :return: Returns the result object.

        :raise evo.common.exceptions.BadRequestException: If the server responds with HTTP status 400.
        :raise evo.common.exceptions.UnauthorizedException: If the server responds with HTTP status 401.
        :raise evo.common.exceptions.ForbiddenException: If the server responds with HTTP status 403.
        :raise evo.common.exceptions.NotFoundException: If the server responds with HTTP status 404.
        :raise evo.common.exceptions.BaseTypedError: If the server responds with any other HTTP status between
            400 and 599, and the body of the response contains a descriptive `type` parameter.
        :raise evo.common.exceptions.EvoAPIException: If the server responds with any other HTTP status between 400
            and 599, and the body of the response does not contain a `type` parameter.
        :raise evo.common.exceptions.UnknownResponseError: For other HTTP status codes with no corresponding response
            type in `response_types_map`.
        """
        # Prepare the path parameters.
        _path_params = {
            "version_id": version_id,
            "workspace_id": workspace_id,
            "org_id": org_id,
            "bm_id": bm_id,
        }

        # Prepare the header parameters.
        _header_params = {
            "Content-Type": "application/json",
            "Accept": "application/json",
        } | get_header_metadata(__name__)
        if additional_headers is not None:
            _header_params.update(additional_headers)

        # Define the collection formats.
        _collection_formats = {}

        _response_types_map = {
            "200": DeltaResponseData,  # noqa: F405
            "304": EmptyResponse,
        }

        return await self.connector.call_api(
            method=RequestMethod.POST,
            resource_path="/blockmodel/orgs/{org_id}/workspaces/{workspace_id}/block-models/{bm_id}/versions/{version_id}/delta",
            path_params=_path_params,
            header_params=_header_params,
            body=delta_request_data,
            collection_formats=_collection_formats,
            response_types_map=_response_types_map,
            request_timeout=request_timeout,
        )

    async def list_block_model_versions(
        self,
        workspace_id: str,
        org_id: str,
        bm_id: str,
        filter: str | None = None,
        offset: int | None = None,
        limit: int | None = None,
        additional_headers: dict[str, str] | None = None,
        request_timeout: int | float | tuple[int | float, int | float] | None = None,
    ) -> PaginatedResponseWithUnitsVersion:  # noqa: F405
        """List version metadata for all versions of a block model

        Gets all versions of the block model `bm_id`.  The list is ordered from the newest version to the oldest version.  This endpoint is paginated, therefore by default this lists, at most, the first 50 versions. To get other versions, use the `offset` and `limit` query parameters to select the desired part of the list. An `offset` beyond the total number of versions for the block model will result in an empty `results` list. The `limit` must be an integer from 1 to 100. The response includes `total`, which is the total number of versions within the list. All workspace roles can use this endpoint.

        :param workspace_id: ID of the workspace this call is scoped to. Represented as a v4 UUID.
            Format: `uuid`
            Example: `'860be2f5-fe06-4c1b-ac8b-7d34d2b6d2ef'`
        :param org_id: ID of the organization this call is scoped to. Represented as a v4 UUID.
            Format: `uuid`
            Example: `'bf1a040c-8c58-4bc2-bec2-c5ae7de8bd84'`
        :param bm_id: ID of the block model this call is scoped to. Represented as a v4 UUID.
            Format: `uuid`
            Example: `'e3c277c2-edc6-4a7a-8380-251dd19231f2'`
        :param filter: (optional) Optional filter to apply to the results. `latest` will return only the latest version.
            Example: `'filter_example'`
        :param offset: (optional) Index of the first item to return.
            Example: `0`
        :param limit: (optional) Maximum number of items to return from the list. Must be greater than 0 and less than or equal to 100.
            Example: `50`
        :param additional_headers: (optional) Additional headers to send with the request.
        :param request_timeout: (optional) Timeout setting for this request. If one number is provided, it will be the
            total request timeout. It can also be a pair (tuple) of (connection, read) timeouts.

        :return: Returns the result object.

        :raise evo.common.exceptions.BadRequestException: If the server responds with HTTP status 400.
        :raise evo.common.exceptions.UnauthorizedException: If the server responds with HTTP status 401.
        :raise evo.common.exceptions.ForbiddenException: If the server responds with HTTP status 403.
        :raise evo.common.exceptions.NotFoundException: If the server responds with HTTP status 404.
        :raise evo.common.exceptions.BaseTypedError: If the server responds with any other HTTP status between
            400 and 599, and the body of the response contains a descriptive `type` parameter.
        :raise evo.common.exceptions.EvoAPIException: If the server responds with any other HTTP status between 400
            and 599, and the body of the response does not contain a `type` parameter.
        :raise evo.common.exceptions.UnknownResponseError: For other HTTP status codes with no corresponding response
            type in `response_types_map`.
        """
        # Prepare the path parameters.
        _path_params = {
            "workspace_id": workspace_id,
            "org_id": org_id,
            "bm_id": bm_id,
        }

        # Prepare the query parameters.
        _query_params = {}
        if filter is not None:
            _query_params["filter"] = filter
        if offset is not None:
            _query_params["offset"] = offset
        if limit is not None:
            _query_params["limit"] = limit

        # Prepare the header parameters.
        _header_params = {
            "Accept": "application/json",
        } | get_header_metadata(__name__)
        if additional_headers is not None:
            _header_params.update(additional_headers)

        # Define the collection formats.
        _collection_formats = {}

        _response_types_map = {
            "200": PaginatedResponseWithUnitsVersion,  # noqa: F405
        }

        return await self.connector.call_api(
            method=RequestMethod.GET,
            resource_path="/blockmodel/orgs/{org_id}/workspaces/{workspace_id}/block-models/{bm_id}/versions",
            path_params=_path_params,
            query_params=_query_params,
            header_params=_header_params,
            collection_formats=_collection_formats,
            response_types_map=_response_types_map,
            request_timeout=request_timeout,
        )

    async def query_block_model_latest_as_post(
        self,
        bm_id: str,
        workspace_id: str,
        org_id: str,
        query_criteria: QueryCriteria,  # noqa: F405
        additional_headers: dict[str, str] | None = None,
        request_timeout: int | float | tuple[int | float, int | float] | None = None,
    ) -> QueryResult:  # noqa: F405
        """Start a block model data query

        Starts a query job against the block model identified by `bm_id`. This will produce a file that contains block model data for selected columns, and within the bounding box if provided.  This request will respond with a reflection of the query criteria, as well as a pollable `job_url`. The `job_url` should be polled with a GET request to wait for the `job_status` field of the job to become either `FAILED` or `COMPLETE`. When the `job_status` becomes `COMPLETE`, the response will look like this:  ``` {     \"job_status\": \"COMPLETE\",     \"payload\": {         \"download_url\": \"<download url>\"     } } ```  The `download_url` points to the output file of the query job, and can be downloaded using either a GET request or using the Azure SDK. The download URL that is generated every time the completed `job_url` is polled has a 30 minute time-to-live (TTL), so the download process must be started within 30 minutes. The download  itself can take longer than 30 minutes, as long as the connection remains open.  The downloaded file will either be an Apache Parquet file or a CSV file, depending on the value specified in the `file_format` field under `output_options`. The columns included within the file depend on the type of sub-blocking and the values specified in the `geometry_columns` and `columns` fields. The `geometry_columns` field determines whether the blocks within the output file are primarily identified by their coordinates or by their block indices.  If `geometry_columns` is set to \"indices\" (the default), then the first columns in the output file will be `i`, `j`, `k` followed by the sub-block index columns (if applicable). If `geometry_columns` is set to \"coordinates\", then the output file will contain the columns `x`, `y`, `z` for regular block models and `x`, `y`, `z`, `dx`, `dy`, `dz` for sub-blocked block models. These columns are referred to as the \"geometry\" columns.  The remaining columns within the output file are determined by the `columns` field. The `columns` field supports selecting columns by either their title or ID, and can also include a wildcard (`\"*\"`) placeholder, which will expand to all user columns, ordered alphabetically by title, in a case-insensitive manner. Please note that the wildcard does not cover the system column `version_id` and `sub_block_derivation`. To include `version_id` and `sub_block_derivation` in the output file, they must also be explicitly specified in the `columns` field alongside the wildcard. The order of columns in the output file will match the order in the `columns` field. Columns that are part of the initial \"geometry\" columns will be ignored if specified.  The `sub_block_derivation` column indicates the source of the block values with one of the following statuses: - `\"user specified\"`: Column values come directly from uploaded file, or the block has no data available. - `\"derived from parent\"`: Column values were calculated entirely by the system based on parent block values, with no corresponding data in the original uploaded file. - `\"some columns derived\"`: Column values are a mix of original data and system-calculated values.  Examples: Given a model has the following user columns: `A`(with a column ID of `d718abe4-56a5-4e27-ad51-813e69eb8aac`), `B`, and `C`. The table below shows the output file columns for different model types and parameters:  | Model Type | `geometry_columns` field | `columns` field | Output File Columns | |------------|--------------------------|-----------------|---------------------| | Regular           | \"indices\"         | `[\"d718abe4-56a5-4e27-ad51-813e69eb8aac\", \"B\"]`        | `i`, `j`, `k`, `A`, `B` | | Regular           | \"coordinates\"     | `[]`                | `x`, `y`, `z` | | Fully sub-blocked | \"indices\"         | `[\"*\"]`             | `i`, `j`, `k`, `sidx`, `A`, `B`, `C` | | Flexible          | \"coordinates\"     | `[\"*\", \"start_si\"]` | `x`, `y`, `z`, `dx`, `dy`, `dz`, `A`, `B`, `C`, `start_si` |   The column headers (header row values in CSV files and schema field names in Parquet files) will either be the name of the columns or the column ID, depending on the value specified in the `column_headers` field.  Example: for a fully sub-blocked model with a `columns` field: `[\"col_1\", \"dx\", \"x\", \"z\", \"d635868c-24ae-4ebe-9358-acb433b3c8cb\"]` (where `d635868c-24ae-4ebe-9358-acb433b3c8cb` is the column ID of `col_2`) and `column_headers`: `name`, the output file will contain the following columns in order: [`i`, `j`, `k`, `sidx`, `col_1`, `dx`, `x`, `z`, `col_2`].  For CSV files, the delimiter that separate values in the output file can also be configured using the `delimiter` field.  For Parquet files, the output file will have the following characteristics: - Row group size of 100,000 - Compressed using Zstd - Parquet data page version set to 1.0 and Parquet version set to 2.6  This request may be cached, so the `job_url` may point to an already completed job.  All workspace roles can use this endpoint.

        :param bm_id: ID of the block model this call is scoped to. Represented as a v4 UUID.
            Format: `uuid`
            Example: `'e3c277c2-edc6-4a7a-8380-251dd19231f2'`
        :param workspace_id: ID of the workspace this call is scoped to. Represented as a v4 UUID.
            Format: `uuid`
            Example: `'860be2f5-fe06-4c1b-ac8b-7d34d2b6d2ef'`
        :param org_id: ID of the organization this call is scoped to. Represented as a v4 UUID.
            Format: `uuid`
            Example: `'bf1a040c-8c58-4bc2-bec2-c5ae7de8bd84'`
        :param query_criteria:
            Example: `endpoints.QueryCriteria()`
        :param additional_headers: (optional) Additional headers to send with the request.
        :param request_timeout: (optional) Timeout setting for this request. If one number is provided, it will be the
            total request timeout. It can also be a pair (tuple) of (connection, read) timeouts.

        :return: Returns the result object.

        :raise evo.common.exceptions.BadRequestException: If the server responds with HTTP status 400.
        :raise evo.common.exceptions.UnauthorizedException: If the server responds with HTTP status 401.
        :raise evo.common.exceptions.ForbiddenException: If the server responds with HTTP status 403.
        :raise evo.common.exceptions.NotFoundException: If the server responds with HTTP status 404.
        :raise evo.common.exceptions.BaseTypedError: If the server responds with any other HTTP status between
            400 and 599, and the body of the response contains a descriptive `type` parameter.
        :raise evo.common.exceptions.EvoAPIException: If the server responds with any other HTTP status between 400
            and 599, and the body of the response does not contain a `type` parameter.
        :raise evo.common.exceptions.UnknownResponseError: For other HTTP status codes with no corresponding response
            type in `response_types_map`.
        """
        # Prepare the path parameters.
        _path_params = {
            "bm_id": bm_id,
            "workspace_id": workspace_id,
            "org_id": org_id,
        }

        # Prepare the header parameters.
        _header_params = {
            "Content-Type": "application/json",
            "Accept": "application/json",
        } | get_header_metadata(__name__)
        if additional_headers is not None:
            _header_params.update(additional_headers)

        # Define the collection formats.
        _collection_formats = {}

        _response_types_map = {
            "200": QueryResult,  # noqa: F405
        }

        return await self.connector.call_api(
            method=RequestMethod.POST,
            resource_path="/blockmodel/orgs/{org_id}/workspaces/{workspace_id}/block-models/{bm_id}/blocks",
            path_params=_path_params,
            header_params=_header_params,
            body=query_criteria,
            collection_formats=_collection_formats,
            response_types_map=_response_types_map,
            request_timeout=request_timeout,
        )

    async def retrieve_block_model_version(
        self,
        version_id: str,
        workspace_id: str,
        org_id: str,
        bm_id: str,
        include_changes: bool | None = None,
        additional_headers: dict[str, str] | None = None,
        request_timeout: int | float | tuple[int | float, int | float] | None = None,
    ) -> VersionWithChanges | Version:  # noqa: F405
        """Request version metadata for a specific block model version

        Gets the version of the block model `bm_id` identified by the `version_id` UUID.  If the `include_changes` query parameter is set to `true`, then the response will include the changes made in the update. Returned object will be of type `VersionWithChanges`, this object extends the `Version` object to include the field `changes`. The changes field contains the `VersionChanges` object.  The fields of the `VersionChanges` object are as follows: - blocks_uploaded: The number of blocks (rows) present in the update file if one was used for the update, null otherwise. - total_blocks: The total number of blocks in the block model. Will remain static for regular models, will change for sub-blocked models when the sub-block count changes due to geometry change updates. - update_type: Whether the update that created the Version was of type `merge` or `replace`; field contains null if the update did not update any blocks. - columns: - columns->new: List of columns (specified by title) created in the update that created the Version. Empty if the update did not create any columns. - columns->deleted: List of columns (specified by title) deleted in the update that created the Version. Empty if the update did not delete any columns. - columns->updated: List of columns (specified by title) updated in the update that created the Version. Empty if the update did not update any columns. - columns->renamed: List of the `ColumnRename` object, denoting which columns were renamed in the update the created the version. Empty if the update did not rename any columns. `ColumnRename` contains the fields: (`old_title`, `new_title`). - columns->metadata_updated: List of the `UpdateMetadataLite` object, containing column metadata changes if they were made in the update. This includes the `title` of the column, the new title, and updated unit ID, respective of what was modified in the update. Empty if the update did not update any column metadata.   All workspace roles can use this endpoint.

        :param version_id: ID of the version of the block model this call is scoped to. Represented as a v4 UUID.
            Format: `uuid`
            Example: `'3e9ce8de-f6ba-4920-8c6e-0882e90f0ed7'`
        :param workspace_id: ID of the workspace this call is scoped to. Represented as a v4 UUID.
            Format: `uuid`
            Example: `'860be2f5-fe06-4c1b-ac8b-7d34d2b6d2ef'`
        :param org_id: ID of the organization this call is scoped to. Represented as a v4 UUID.
            Format: `uuid`
            Example: `'bf1a040c-8c58-4bc2-bec2-c5ae7de8bd84'`
        :param bm_id: ID of the block model this call is scoped to. Represented as a v4 UUID.
            Format: `uuid`
            Example: `'e3c277c2-edc6-4a7a-8380-251dd19231f2'`
        :param include_changes: (optional) Whether or not to include the changes made in the update that created the specified block model version.
            Example: `False`
        :param additional_headers: (optional) Additional headers to send with the request.
        :param request_timeout: (optional) Timeout setting for this request. If one number is provided, it will be the
            total request timeout. It can also be a pair (tuple) of (connection, read) timeouts.

        :return: Returns the result object.

        :raise evo.common.exceptions.BadRequestException: If the server responds with HTTP status 400.
        :raise evo.common.exceptions.UnauthorizedException: If the server responds with HTTP status 401.
        :raise evo.common.exceptions.ForbiddenException: If the server responds with HTTP status 403.
        :raise evo.common.exceptions.NotFoundException: If the server responds with HTTP status 404.
        :raise evo.common.exceptions.BaseTypedError: If the server responds with any other HTTP status between
            400 and 599, and the body of the response contains a descriptive `type` parameter.
        :raise evo.common.exceptions.EvoAPIException: If the server responds with any other HTTP status between 400
            and 599, and the body of the response does not contain a `type` parameter.
        :raise evo.common.exceptions.UnknownResponseError: For other HTTP status codes with no corresponding response
            type in `response_types_map`.
        """
        # Prepare the path parameters.
        _path_params = {
            "version_id": version_id,
            "workspace_id": workspace_id,
            "org_id": org_id,
            "bm_id": bm_id,
        }

        # Prepare the query parameters.
        _query_params = {}
        if include_changes is not None:
            _query_params["include_changes"] = include_changes

        # Prepare the header parameters.
        _header_params = {
            "Accept": "application/json",
        } | get_header_metadata(__name__)
        if additional_headers is not None:
            _header_params.update(additional_headers)

        # Define the collection formats.
        _collection_formats = {}

        _response_types_map = {
            "200": VersionWithChanges | Version,  # noqa: F405
        }

        return await self.connector.call_api(
            method=RequestMethod.GET,
            resource_path="/blockmodel/orgs/{org_id}/workspaces/{workspace_id}/block-models/{bm_id}/versions/{version_id}",
            path_params=_path_params,
            query_params=_query_params,
            header_params=_header_params,
            collection_formats=_collection_formats,
            response_types_map=_response_types_map,
            request_timeout=request_timeout,
        )
