#  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 RequestMethod
from evo.common.utils import get_header_metadata

from ..models import *  # noqa: F403

__all__ = ["ColumnOperationsApi"]


class ColumnOperationsApi:
    """API client for the Column_operations 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 notify_upload_complete(
        self,
        job_id: str,
        workspace_id: str,
        org_id: str,
        bm_id: str,
        additional_headers: dict[str, str] | None = None,
        request_timeout: int | float | tuple[int | float, int | float] | None = None,
    ) -> UpdatedResponse:  # noqa: F405
        """Advise that the update file has finished uploading

        Signals that you have finished uploading the update file to the server blob storage indicated by the `upload_url`, and that the update can continue. This moves the `job_status` from `PENDING_UPLOAD` to `QUEUED`.  After this endpoint is called, the `job_url` should then be polled with a GET request to wait for the `job_status` field of the job to become either `FAILED` or `COMPLETE`. On workflow success, a new version of the block model will be created that will contain the changes specified in the update request.  Note that this endpoint still needs to be hit even when an update does not require a file upload.  A minimum workspace role of Editor is required to use this endpoint.

        :param job_id: ID of the job this call is scoped to. Represented as a v4 UUID.
            Format: `uuid`
            Example: `'ff0b8078-c7ec-41ab-87fb-f3eb32812b32'`
        :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 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 = {
            "job_id": job_id,
            "workspace_id": workspace_id,
            "org_id": org_id,
            "bm_id": bm_id,
        }

        # 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 = {
            "201": UpdatedResponse,  # noqa: F405
        }

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

    async def update_block_model_from_latest_version(
        self,
        workspace_id: str,
        org_id: str,
        bm_id: str,
        update_data_lite_input: UpdateDataLiteInput,  # noqa: F405
        additional_headers: dict[str, str] | None = None,
        request_timeout: int | float | tuple[int | float, int | float] | None = None,
    ) -> UpdateWithUrl:  # noqa: F405
        """Start a block model data update

        Starts the workflow to update the latest version of the block model. On workflow success, a new version of the block model will be created that will contain the changes specified in the request.  When a user wishes to update the blocks of a block model, they need to hit multiple endpoints to complete the workflow. This includes:  1. An initial request; 2. An upload of data (for `new` and `update` operations); 3. And finally, a confirmation that the update job can proceed from the state `PENDING_UPLOAD` to `QUEUED`.  This endpoint is the initial request, where the server does a sanity check/validation on what the client is attempting to do.  With regards to the request body, - We require the `columns` field to be supplied, which is used to convey which operations the client wishes the service to perform on the block model during the update. The sub-fields of columns (or operations) `new`, `update`, `rename`, `update_metadata`, and `delete` specify the downstream behaviour on the specified columns during the update workflow. Note that `new`, `update`, and `delete` can be provided in concert, whilst `rename` can only be provided by itself. `update_metadata` can be provided along with `new`, `update`, and `delete`, but only when there are no columns being renamed (i.e. when only the unit ID is being updated). Some general rules/mechanics for all of these fields:     - The reserved system columns [`i`, `j`, `k`, `sidx`, `x`, `y`, `z`, `dx`, `dy`, `dz`, `version_id`] and any other model type specific sub-block index columns, cannot be targeted using any of the update operations.         - Note for Datamine file imports, this restriction extends to the fields `[IJK, XC, YC, ZC, XINC, YINC, ZINC]`. These will be mapped automatically from the file to their BMS equivalents.     - Any list of titles provided cannot contain duplicates.     - In all cases, unless specifically stated otherwise, columns that are targeted in one operation cannot be targeted in another. - There is an optional field `update_type`, which is used to determine which blocks are updated in the block model. The supported update types are `replace` and `merge`.     - If `update_type` is set to `replace`, the values of the columns provided in the update file are updated for all blocks within the block model. If a block is not provided in the update file, the column values for that block are set to `null`. The columns not provided in the update file are not affected.     - If `update_type` is set to `merge` (the default), only the blocks and columns that are provided in the update file are updated. The blocks and columns not provided in the update file are not affected. - Note that the optional field `geometry_change` can only be provided for sub-blocked models.     - If `geometry_change` is set to `false`, no new sub-blocks can be created nor destroyed, only sub-blocks that exist in the latest version can be updated.     - If `geometry_change` is set to `true`, new sub-blocks can be created and/or existing sub-blocks can be destroyed, but all columns must be provided. If `update_type` is set to `merge`, then all sub-blocks within a parent block must be provided. - For `new` and `update` operations, the details of the data file for upload can be specified using the optional `input_options` field.  If the request is successful, the server then sends back a pollable `job_url` and an `upload_url` (if required). The `upload_url` is where the data associated with a `new` or `update` operation should be uploaded to, which can be done with a PUT request. Note that the generated URL contains a SAS token, which is used to authenticate the upload. An upload can be started no later than 30 minutes after the generation of the SAS token.  With regards to the data file to be uploaded, - The file can either be in Parquet, CSV, or Datamine (.dm) format. - The file needs to contain either IJK (block index) or XYZ (centroid coordinate) information for each block/row, along with the data for the columns specified in the request body, using the correct data type. - If both IJK and XYZ columns are supplied in the data file, the service will only make use of the IJK information.     - Datamine is an exception to this rule - for these files, BlockSync only uses the XYZ information and ignores the IJK field. - The file must include the required system columns, which vary depending on the block model type:     - Regular models: Require `i`, `j`, `k` (block indices) or `x`, `y`, `z` (centroid coordinates).     - Fully sub-blocked models: Require `i`, `j`, `k`, `sidx` (sub-block index) or `x`, `y`, `z`, `dx`, `dy`, `dz` (sub-block sizes along each axis).     - Octree models: Requirements are the same as fully sub-blocked models.     - Flexible models: Require `i`, `j`, `k`, `start_si`, `start_sj`, `start_sk`, `end_si`, `end_sj`, `end_sk` (sub-block grid indices) or `x`, `y`, `z`, `dx`, `dy`, `dz`. - Reserved system columns must use the correct data types:     - Indices `i`, `j`, `k`, and `sidx` must be `UInt32`.     - Coordinates `x`, `y`, `z`, `dx`, `dy`, and `dz` must be `Float64`.     - Sub-block grid indices `start_si`, `start_sj`, `start_sk`, `end_si`, `end_sj`, `end_sk` must be `UInt8`.  A minimum workspace role of Editor is required to 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 update_data_lite_input:
            Example: `endpoints.UpdateDataLiteInput()`
        :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 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 = {
            "202": UpdateWithUrl,  # noqa: F405
        }

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