#
# # Copyright © 2023 Peak AI Limited. or its affiliates. All Rights Reserved.
# #
# # Licensed under the Apache License, Version 2.0 (the "License"). You
# # may not use this file except in compliance with the License. A copy of
# # the License is located at:
# #
# # https://github.com/PeakBI/peak-sdk/blob/main/LICENSE
# #
# # or in the "license" file accompanying this file. This file 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.
# #
# # This file is part of the peak-sdk.
# # see (https://github.com/PeakBI/peak-sdk)
# #
# # You should have received a copy of the APACHE LICENSE, VERSION 2.0
# # along with this program. If not, see <https://apache.org/licenses/LICENSE-2.0>
#
"""Peak Images service commands."""
from typing import Any, Dict, List, Optional

import typer
from peak.cli import args, helpers
from peak.helpers import combine_dictionaries, map_user_options, parse_list_of_strings, variables_to_dict
from peak.resources.images import Image
from rich.console import Console
from typing_extensions import Annotated

app = typer.Typer(
    help="Create Docker images that Workflows, Workspaces, and other services use.",
    short_help="Create and manage Images.",
)
console = Console()

_IMAGE_ID = typer.Argument(..., help="ID of the image to be used in this operation.")

_COUNT = typer.Option(None, help="Number of builds required.")

_BUILD_STATUS = typer.Option(None, help="List of build status to filter image builds.")

_VERSIONS = typer.Option(None, help="List of version ids to filter image builds.")

_IMAGE_LIST_NAME = typer.Option(None, help="Image Name or version to search for.")

_IMAGE_LIST_STATUS = typer.Option(
    None,
    help="List of status of the latest version to filter the images by.",
)

_IMAGE_LIST_SCOPE = typer.Option(None, help="List of type of image to filter the images by.")

_IMAGE_LIST_BUILD_STATUS = typer.Option(
    None,
    help="List of build status of the latest version to filter the images by.",
)

_IMAGE_LIST_TAGS = typer.Option(None, help="List of tags on the latest version to filter the images by.")

_VERSION_LIST_VERSION = typer.Option(None, help="Version to search for.")

_VERSION_LIST_STATUS = typer.Option(None, help="List of statuses to filter the versions by.")

_VERSION_LIST_BUILD_STATUS = typer.Option(None, help="List of build statuses to filter the versions by.")

_VERSION_LIST_TAGS = typer.Option(None, help="List of tags to filter the versions by.")
_NAME = typer.Option(None, help="Name of the image.")

_VERSION = typer.Option(None, help="A valid semantic image version.")

_TYPE = typer.Option(None, help="Type of the image.")

_DESCRIPTION = typer.Option(None, help="Description of the image.")

_ARTIFACT_PATH = typer.Option(None, help="Path to the artifact.")

_ARTIFACT_IGNORE_FILES = typer.Option(None, help="Ignore files to use when creating artifact.")

_BUILD_DETAILS = typer.Option(None, help="Build details of the image. To be passed in stringified json format.")

_SOURCE = typer.Option(None, help="The source via which the image is to be created.")

_DOCKERFILE = typer.Option(None, help="The dockerfile to be used to create the image.")

_DOCKERFILE_PATH = typer.Option(None, help="Path to the dockerfile.")

_CONTEXT = typer.Option(None, help="The path within the artifact to the code to be executed by the Dockerfile.")

_REPOSITORY = typer.Option(None, help="When source is github, the repository where the Dockerfile content is stored.")

_BRANCH = typer.Option(None, help="When source is github, the Branch that contains the Dockerfile.")

_TOKEN = typer.Option(None, help="When source is github, the token to be used to clone the required repository.")

_USE_CACHE = typer.Option(None, help="Whether to enable image caching to reduce build time.")

_BUILD_ARGUMENTS = typer.Option(None, help="List of build arguments in the format arg1=value1.")

_SECRETS = typer.Option(None, help="List of secret names to be passed as build arguments.")

MAPPING = {
    "source": "buildDetails",
    "dockerfile": "buildDetails",
    "dockerfilePath": "buildDetails",
    "context": "buildDetails",
    "repository": "buildDetails",
    "branch": "buildDetails",
    "token": "buildDetails",
    "useCache": "buildDetails",
    "buildArguments": "buildDetails",
    "secrets": "buildDetails",  # pragma: allowlist secret
}


@app.command(short_help="Create a new image.")
def create(
    ctx: typer.Context,
    file: Annotated[
        Optional[str],
        typer.Argument(
            ...,
            help="Path to the file that defines the body for this operation, supports both `yaml` file or a `jinja` template.",
        ),
    ] = None,
    params_file: str = args.TEMPLATE_PARAMS_FILE,
    params: List[str] = args.TEMPLATE_PARAMS,
    name: Optional[str] = _NAME,
    version: Optional[str] = _VERSION,
    type: Optional[str] = _TYPE,  # noqa: A002
    description: Optional[str] = _DESCRIPTION,
    artifact_path: Optional[str] = _ARTIFACT_PATH,
    artifact_ignore_files: Optional[List[str]] = _ARTIFACT_IGNORE_FILES,
    build_details: Optional[str] = _BUILD_DETAILS,
    source: Optional[str] = _SOURCE,
    dockerfile: Optional[str] = _DOCKERFILE,
    dockerfile_path: Optional[str] = _DOCKERFILE_PATH,
    context: Optional[str] = _CONTEXT,
    repository: Optional[str] = _REPOSITORY,
    branch: Optional[str] = _BRANCH,
    token: Optional[str] = _TOKEN,
    use_cache: Optional[bool] = _USE_CACHE,
    build_arguments: Optional[List[str]] = _BUILD_ARGUMENTS,
    secrets: Optional[List[str]] = _SECRETS,
) -> None:
    """***Create*** a new image. This also adds the first version in the image.

    \b
    🧩 ***Input file schema(yaml):***<br/>
    ```yaml
      body (map):
        name (str): Name of the image.
        version (str | required: false): A valid semantic image version. If not provided, the version will be set to 0.0.1.
        type (str): Type of the image.
        description (str | required: false): Description of the image.
        buildDetails (map):
          source (str): The source via which the image is to be created.
          useCache (boolean | required: false): Whether to enable image caching to reduce build time, is enabled by default.
          buildArguments (list(map) | required: false):
            name (str): Name of the build argument.
            value (str): Value of the build argument.
          secrets (list(str)) | required: false): List of secret names to be passed as build arguments.
          context (str | required: false): The path within the artifact to the code to be executed by the Dockerfile.
          dockerfilePath (str | required: false): Path to the Dockerfile inside artifact or the repository.
          repository (str | required: false): When source is github, the repository where the Dockerfile content is stored.
          branch (str | required: false): When source is github, the Branch that contains the Dockerfile.
          token (str | required: false): When source is github, the token to be used to clone the required repository.
          dockerfile (str | required: false): When source is dockerfile, this represents the content of Dockerfile to build the image from.
      artifact (map | required: false):
        path (str): Path to the artifact.
        ignore_files (list(str) | required: false) : Ignore files to use when creating artifact.
    ```

    \b
    📝 ***Example usage:***
    ```bash
    peak images create '/path/to/image.yaml' --params-file '/path/to/values.yaml'
    ```

    We can also provide the required parameters from the command line or combine the YAML template and command line arguments in which case the command line arguments will take precedence.

    \b
    📝 ***Example usage without yaml:***
    ```bash
    peak images create --name <name> --type <type> --description <description> --version <version> --source <source> --dockerfile <dockerfile> --secrets <secret_1> --secrets <secret_2>
    ```

    \b
    🆗 ***Response:***
    ```json
    {
        "buildId": "build-image-mock:2198c71e-22c5-aca3eac3a55e",
        "imageId": 9999,
        "versionId": 1,
    }
    ```

    🔗 [**API Documentation**](https://service.peak.ai/image-management/api-docs/index.htm#/images/post_api_v2_images)
    """
    image_client: Image = ctx.obj["client"]

    user_options: Dict[str, Any] = variables_to_dict(
        name,
        description,
        type,
        version,
        build_details,
        source,
        dockerfile,
        repository,
        branch,
        token,
        dockerfile_path,
        context,
        use_cache,
        build_arguments,
        secrets,
    )

    user_options = map_user_options(user_options, MAPPING, dict_type_keys=["buildDetails"])

    body: Dict[str, Any] = {}
    if file:
        body = helpers.template_handler(file=file, params_file=params_file, params=params)
        body = helpers.remove_unknown_args(body, image_client.create_image)

    updated_body = combine_dictionaries(body.get("body") or {}, user_options)
    artifact = helpers.get_updated_artifacts(body, artifact_path, artifact_ignore_files)

    if build_arguments:
        updated_body["buildDetails"]["buildArguments"] = helpers.parse_build_arguments(build_arguments)

    if secrets:
        updated_body["buildDetails"]["secrets"] = parse_list_of_strings(secrets)

    response: Dict[str, Any] = image_client.create_image(body=updated_body, artifact=artifact)  # type: ignore  # noqa: PGH003
    console.print(response)


@app.command(short_help="Create a new image version.")
def create_version(
    ctx: typer.Context,
    image_id: int = _IMAGE_ID,
    file: Annotated[
        Optional[str],
        typer.Argument(
            ...,
            help="Path to the file that defines the body for this operation, supports both `yaml` file or a `jinja` template.",
        ),
    ] = None,
    params_file: str = args.TEMPLATE_PARAMS_FILE,
    params: List[str] = args.TEMPLATE_PARAMS,
    version: Optional[str] = _VERSION,
    description: Optional[str] = _DESCRIPTION,
    artifact_path: Optional[str] = _ARTIFACT_PATH,
    artifact_ignore_files: Optional[List[str]] = _ARTIFACT_IGNORE_FILES,
    build_details: Optional[str] = _BUILD_DETAILS,
    source: Optional[str] = _SOURCE,
    dockerfile: Optional[str] = _DOCKERFILE,
    dockerfile_path: Optional[str] = _DOCKERFILE_PATH,
    context: Optional[str] = _CONTEXT,
    repository: Optional[str] = _REPOSITORY,
    branch: Optional[str] = _BRANCH,
    token: Optional[str] = _TOKEN,
    use_cache: Optional[bool] = _USE_CACHE,
    build_arguments: Optional[List[str]] = _BUILD_ARGUMENTS,
    secrets: Optional[List[str]] = _SECRETS,
) -> None:
    """***Create*** a new version in an existing image.

    \b
    🧩 ***Input file schema(yaml):***<br/>
    ```yaml
      body (map):
        version (str | required: false): A valid semantic image version. If not provided, the next patch version of the latest version will be used.
        description (str | required: false): Description of the image version.
        buildDetails (map):
          source (str): Source via which image version can be created.
          useCache (boolean | required: false): Whether to enable image caching to reduce build time, is enabled by default.
          buildArguments (list(map) | required: false):
            name (str): Name of the build argument.
            value (str): Value of the build argument.
          secrets (list(str) | required: false): List of secret names to be passed as build arguments.
          context (str | required: false): The path within the artifact to code to be executed by the Dockerfile.
          dockerfilePath (str | required: false): Path to the Dockerfile inside artifact or repository.
          repository (str | required: false): When source is github, the repository where the Dockerfile content is stored.
          branch (str | required: false): When source is github, the Branch that contains the Dockerfile.
          token (str | required: false): When source is github, the token to be used to clone the required repository.
          dockerfile (str | required: false): When source is dockerfile, this represents the content of Dockerfile to build the image from.
        autodeployResources (list(map) | required: false): A list of resources that should be redeployed when the build completes.
            entityId (str): The id of the resource to be redeployed.
            entityType (str): The type of the resource to be redeployed. Allowed values are 'workflow', 'workspace', 'api' and 'webapp'.
      artifact (map | required: false):
        path (str): Path to the artifact.
        ignore_files (list(str) | required: false) : Ignore files to use when creating artifact.
    ```

    \b
    📝 ***Example usage:***
    ```bash
    peak images create-version <imageId> '/path/to/file.yaml' -v '/path/to/parmas.yaml'
    ```

    We can also provide the required parameters from the command line or combine the YAML template and command line arguments in which case the command line arguments will take precedence.

    \b
    📝 ***Example usage without yaml:***
    ```bash
    peak images create-version <image_id> --description <description> --version <version> --source <source> --dockerfile <dockerfile> --secrets <secret_1>,<secret_2>
    ```

    \b
    🆗 ***Response:***
    ```json
    {
        "buildId": "build-image-mock:2198c71e-22c5-aca3eac3a55e",
        "versionId": 101,
        "autodeploymentId": "0a12abcd-11ab-22d2-123a-a1234b333abc"
    }
    ```

    🔗 [**API Documentation**](https://service.peak.ai/image-management/api-docs/index.htm#/versions/post_api_v2_images__imageId__versions)
    """
    image_client: Image = ctx.obj["client"]

    user_options: Dict[str, Any] = variables_to_dict(
        description,
        version,
        build_details,
        source,
        dockerfile,
        repository,
        branch,
        token,
        dockerfile_path,
        context,
        use_cache,
        build_arguments,
        secrets,
    )

    user_options = map_user_options(user_options, MAPPING, dict_type_keys=["buildDetails"])

    body: Dict[str, Any] = {}
    if file:
        body = helpers.template_handler(file=file, params_file=params_file, params=params)
        body = helpers.remove_unknown_args(body, image_client.create_version)

    updated_body = combine_dictionaries(
        body.get("body") or {},
        user_options,
    )
    artifact = helpers.get_updated_artifacts(body, artifact_path, artifact_ignore_files)

    if build_arguments:
        updated_body["buildDetails"]["buildArguments"] = helpers.parse_build_arguments(build_arguments)

    if secrets:
        updated_body["buildDetails"]["secrets"] = parse_list_of_strings(secrets)

    response: Dict[str, Any] = image_client.create_version(image_id=image_id, body=updated_body, artifact=artifact)  # type: ignore  # noqa: PGH003
    console.print(response)


@app.command(short_help="Create a new image/version or Update and existing version.")
def create_or_update(
    ctx: typer.Context,
    file: Annotated[
        Optional[str],
        typer.Argument(
            ...,
            help="Path to the file that defines the body for this operation, supports both `yaml` file or a `jinja` template.",
        ),
    ] = None,
    params_file: str = args.TEMPLATE_PARAMS_FILE,
    params: List[str] = args.TEMPLATE_PARAMS,
    name: Optional[str] = _NAME,
    version: Optional[str] = _VERSION,
    type: Optional[str] = _TYPE,  # noqa: A002
    description: Optional[str] = _DESCRIPTION,
    artifact_path: Optional[str] = _ARTIFACT_PATH,
    artifact_ignore_files: Optional[List[str]] = _ARTIFACT_IGNORE_FILES,
    build_details: Optional[str] = _BUILD_DETAILS,
    source: Optional[str] = _SOURCE,
    dockerfile: Optional[str] = _DOCKERFILE,
    dockerfile_path: Optional[str] = _DOCKERFILE_PATH,
    context: Optional[str] = _CONTEXT,
    repository: Optional[str] = _REPOSITORY,
    branch: Optional[str] = _BRANCH,
    token: Optional[str] = _TOKEN,
    use_cache: Optional[bool] = _USE_CACHE,
    build_arguments: Optional[List[str]] = _BUILD_ARGUMENTS,
    secrets: Optional[List[str]] = _SECRETS,
) -> None:
    """***Create*** a new image if it doesn't exist. This also adds the first version in the image. In case image exists, it will add a new version to the image. ***Update*** a version if exists.

    \b
    🧩 ***Input file schema(yaml):***<br/>
    ```yaml
      body (map):
        name (str): Name of the image.
        version (str | required: false): A valid semantic image version. If not provided, the next patch version of the latest version will be used.
        type (str): Type of the image.
        description (str | required: false): Description of the image.
        buildDetails (map):
          source (str): The source via which the image is to be created.
          useCache (boolean | required: false): Whether to enable image caching to reduce build time, is enabled by default.
          buildArguments (list(map) | required: false):
            name (str): Name of the build argument.
            value (str): Value of the build argument.
          secrets (list(str)) | required: false): List of secret names to be passed as build arguments.
          context (str | required: false): The path within the artifact to the code to be executed by the Dockerfile.
          dockerfilePath (str | required: false): Path to the Dockerfile inside artifact or the repository.
          repository (str | required: false): When source is github, the repository where the Dockerfile content is stored.
          branch (str | required: false): When source is github, the Branch that contains the Dockerfile.
          token (str | required: false): When source is github, the token to be used to clone the required repository.
          dockerfile (str | required: false): When source is dockerfile, this represents the content of Dockerfile to build the image from.
      artifact (map | required: false):
        path (str): Path to the artifact.
        ignore_files (list(str) | required: false) : Ignore files to use when creating artifact.
    ```

    \b
    📝 ***Example usage:***
    ```bash
    peak images create-or-update '/path/to/image.yaml' --params-file '/path/to/values.yaml'
    ```

    \b
    We can also provide the required parameters from the command line or combine the YAML template and command line arguments in which case the command line arguments will take precedence.

    \b
    📝 ***Example usage without yaml:***
    ```bash
    peak images create-or-update --name <name> --type <type> --description <description> --version <version> --source <source> --dockerfile <dockerfile> --secrets <secret_1> --secrets <secret_2>
    ```

    \b
    🆗 ***Response:***
    ```json
    {
        "buildId": "build-image-mock:2198c71e-22c5-aca3eac3a55e",
        "imageId": 9999,
        "versionId": 1,
    }
    ```

    🔗 [**API Documentation**](https://service.peak.ai/image-management/api-docs/index.htm#/images/post_api_v2_images)
    """
    image_client: Image = ctx.obj["client"]

    user_options: Dict[str, Any] = variables_to_dict(
        name,
        description,
        type,
        version,
        build_details,
        source,
        dockerfile,
        repository,
        branch,
        token,
        dockerfile_path,
        context,
        use_cache,
        build_arguments,
        secrets,
    )

    user_options = map_user_options(user_options, MAPPING, dict_type_keys=["buildDetails"])

    body: Dict[str, Any] = {}
    if file:
        body = helpers.template_handler(file=file, params_file=params_file, params=params)
        body = helpers.remove_unknown_args(body, image_client.create_or_update_image_version)

    updated_body = combine_dictionaries(body.get("body") or {}, user_options)
    artifact = helpers.get_updated_artifacts(body, artifact_path, artifact_ignore_files)

    if build_arguments:
        updated_body["buildDetails"]["buildArguments"] = helpers.parse_build_arguments(build_arguments)

    if secrets:
        updated_body["buildDetails"]["secrets"] = parse_list_of_strings(secrets)

    response: Dict[str, Any] = image_client.create_or_update_image_version(body=updated_body, artifact=artifact)  # type: ignore  # noqa: PGH003
    console.print(response)


@app.command("list", short_help="List images.")
def list_images(
    ctx: typer.Context,
    page_size: Optional[int] = args.PAGE_SIZE,
    page_number: Optional[int] = args.PAGE_NUMBER,
    name: Optional[str] = _IMAGE_LIST_NAME,
    status: Optional[List[str]] = _IMAGE_LIST_STATUS,
    scope: Optional[List[str]] = _IMAGE_LIST_SCOPE,
    last_build_status: Optional[List[str]] = _IMAGE_LIST_BUILD_STATUS,
    tags: Optional[List[str]] = _IMAGE_LIST_TAGS,
) -> None:
    """***List*** all images that exists in the tenant along with details about their latest version.

    \b
    📝 ***Example usage:***<br/>
    ```bash
    peak images list --page-size 10 --page-number 1 --name "test" --status "ready,in-use" --scope "custom" --last-build-status "building" --tags "tag1"
    ```

    \b
    🆗 ***Response:***
    ```
    {
        "imageCount": 1,
        "images": [...],
        "pageCount": 1,
        "pageNumber": 1,
        "pageSize": 25
    }
    ```

    🔗 [**API Documentation**](https://service.peak.ai/image-management/api-docs/index.htm#/images/get_api_v2_images)
    """
    image_client: Image = ctx.obj["client"]

    response = image_client.list_images(
        page_size=page_size,
        page_number=page_number,
        return_iterator=False,
        name=name,
        status=parse_list_of_strings(status),
        scope=parse_list_of_strings(scope),
        last_build_status=parse_list_of_strings(last_build_status),
        tags=parse_list_of_strings(tags),
    )
    console.print(response)


@app.command("list-versions", short_help="List image versions.")
def list_image_versions(
    ctx: typer.Context,
    image_id: int = _IMAGE_ID,
    page_size: Optional[int] = args.PAGE_SIZE,
    page_number: Optional[int] = args.PAGE_NUMBER,
    version: Optional[str] = _VERSION_LIST_VERSION,
    status: Optional[List[str]] = _VERSION_LIST_STATUS,
    last_build_status: Optional[List[str]] = _VERSION_LIST_BUILD_STATUS,
    tags: Optional[List[str]] = _VERSION_LIST_TAGS,
) -> None:
    """***List*** all versions for an image.

    \b
    📝 ***Example usage:***<br/>
    ```bash
    peak images list-versions 9999 --page-size 10 --page-number 1 --version "0.0.1" --status "ready,in-use" --last-build-status "failed"  --tags "tag1,tag2"
    ```

    \b
    🆗 ***Response:***
    ```
    {
        "versionCount": 1,
        "versions": [...],
        "pageCount": 1,
        "pageNumber": 1,
        "pageSize": 25
    }
    ```

    🔗 [**API Documentation**](https://service.peak.ai/image-management/api-docs/index.htm#/versions/get_api_v2_images__imageId__versions)
    """
    image_client: Image = ctx.obj["client"]
    response: Dict[str, Any] = image_client.list_image_versions(
        image_id=image_id,
        page_size=page_size,
        page_number=page_number,
        return_iterator=False,
        version=version,
        status=parse_list_of_strings(status),
        last_build_status=parse_list_of_strings(last_build_status),
        tags=parse_list_of_strings(tags),
    )
    console.print(response)


@app.command(short_help="Describe details of a specific image.")
def describe(ctx: typer.Context, image_id: int = _IMAGE_ID) -> None:
    """***Describe*** details of a specific image and its latest version.

    \b
    📝 ***Example usage:***<br/>
    ```bash
    peak images describe 9999
    ```

    \b
    🆗 ***Response:***
    ```
    {
        "id": 1,
        "name": "awesome-image",
        "type": "workflow",
        "scope": "custom",
        "createdAt": "2020-01-01T18:00:00.000Z",
        "createdBy": "someone@peak.ai",
        "latestVersion": {...}
    }
    ```

    🔗 [**API Documentation**](https://service.peak.ai/image-management/api-docs/index.htm#/images/get_api_v2_images__imageId_)
    """
    image_client: Image = ctx.obj["client"]
    response = image_client.describe_image(image_id=image_id)
    console.print(response)


@app.command(short_help="Describe details of a specific version.")
def describe_version(
    ctx: typer.Context,
    image_id: int = args.IMAGE_ID,
    version_id: int = args.VERSION_ID,
) -> None:
    """***Describe*** details of a specific version.

    \b
    📝 ***Example usage:***<br/>
    ```bash
    peak images describe-version --image-id 9999 --version-id 101
    ```

    \b
    🆗 ***Response:***
    ```
    {
        "imageName": "awesome-image",
        "imageType": "Workflow",
        "id": 101,
        "version": "0.0.1-python3.9",
        "description": "This is an awesome image",
        "status": "not-ready",
        "createdAt": "2020-01-01T18:00:00.000Z",
        "createdBy": "someone@peak.ai",
        "updatedAt": "2020-01-01T18:00:00.000Z",
        "updatedBy": "someone@peak.ai",
        "pullUrl": "<image-url>",
        "lastBuildStatus": "building",
        "lastBuildAt": "2020-01-01T18:00:00.000Z",
        "buildDetails": {...},
        "tags": [...]
    }
    ```

    🔗 [**API Documentation**](https://service.peak.ai/image-management/api-docs/index.htm#/versions/get_api_v2_images__imageId__versions__versionId_)
    """
    image_client: Image = ctx.obj["client"]
    response: Dict[str, Any] = image_client.describe_version(image_id=image_id, version_id=version_id)
    console.print(response)


@app.command(short_help="Update an image version.")
def update_version(
    ctx: typer.Context,
    image_id: int = args.IMAGE_ID,
    version_id: int = args.VERSION_ID,
    file: Annotated[
        Optional[str],
        typer.Argument(
            ...,
            help="Path to the file that defines the body for this operation, supports both `yaml` file or a `jinja` template.",
        ),
    ] = None,
    params_file: str = args.TEMPLATE_PARAMS_FILE,
    params: List[str] = args.TEMPLATE_PARAMS,
    description: Optional[str] = _DESCRIPTION,
    artifact_path: Optional[str] = _ARTIFACT_PATH,
    artifact_ignore_files: Optional[List[str]] = _ARTIFACT_IGNORE_FILES,
    build_details: Optional[str] = _BUILD_DETAILS,
    source: Optional[str] = _SOURCE,
    dockerfile: Optional[str] = _DOCKERFILE,
    dockerfile_path: Optional[str] = _DOCKERFILE_PATH,
    context: Optional[str] = _CONTEXT,
    repository: Optional[str] = _REPOSITORY,
    branch: Optional[str] = _BRANCH,
    token: Optional[str] = _TOKEN,
    use_cache: Optional[bool] = _USE_CACHE,
    build_arguments: Optional[List[str]] = _BUILD_ARGUMENTS,
    secrets: Optional[List[str]] = _SECRETS,
) -> None:
    """***Update*** an image version. Only the versions that are in `not-ready` state can be updated and you can not update their version while updating them.

    \b
    🧩 ***Input file schema(yaml):***<br/>
    ```yaml
    body (map):
      description (str | required: false): Description of the image version.
      buildDetails (map):
        source (str): Source via which image version can be created.
        useCache (boolean | required: false): Whether to enable image caching to reduce build time, is enabled by default.
        buildArguments (list(map) | required: false):
          name (str): Name of the build argument.
          value (str): Value of the build argument.
        secrets (list(str) | required: false): List of secret names to be passed as build arguments.
        context (str | required: false): The path within the artifact to code to be executed by the Dockerfile.
        dockerfilePath (str | required: false): Path to the Dockerfile inside artifact or repository.
        repository (str | required: false): When source is github, the repository where the Dockerfile content is stored.
        branch (str | required: false): When source is github, the Branch that contains the Dockerfile.
        token (str | required: false): When source is github, the token to be used to clone the required repository.
        dockerfile (str | required: false): When source is dockerfile, this represents the content of Dockerfile to build the image from.
      autodeployResources (list(map) | required: false): A list of resources that should be redeployed when the build completes.
            entityId (str): The id of the resource to be redeployed.
            entityType (str): The type of the resource to be redeployed. Allowed values are 'workflow', 'workspace', 'api' and 'webapp'.
    artifact (map | required: false):
      path (str): Path to the artifact.
      ignore_files (list(str) | required: false) : Ignore files to use when creating artifact.
    ```

    \b
    📝 ***Example usage:***
    ```bash
    peak images update-version --image-id 9999 --version-id 101 '/path/to/file.yaml' -v '/path/to/parmas.yaml'
    ```

    We can also provide the required parameters from the command line or combine the YAML template and command line arguments in which case the command line arguments will take precedence.

    \b
    📝 ***Example usage without yaml:***
    ```bash
    peak images update-version --image-id <image_id> --version-id <version_id> --description <description> --source <source> --dockerfile <dockerfile> --secrets <secret_1>,<secret_2>
    ```

    \b
    🆗 ***Response:***
    ```json
    {
        "buildId": "build-image-mock:2198c71e-22c5-aca3eac3a55e",
        "versionId": 101,
        "autodeploymentId": "0a12abcd-11ab-22d2-123a-a1234b333abc"
    }
    ```

    🔗 [**API Documentation**](https://service.peak.ai/image-management/api-docs/index.htm#/versions/patch_api_v2_images__imageId__versions__versionId_)
    """
    image_client: Image = ctx.obj["client"]

    user_options: Dict[str, Any] = variables_to_dict(
        description,
        build_details,
        source,
        dockerfile,
        repository,
        branch,
        token,
        dockerfile_path,
        context,
        use_cache,
        build_arguments,
        secrets,
    )

    user_options = map_user_options(user_options, MAPPING, dict_type_keys=["buildDetails"])

    body: Dict[str, Any] = {}
    if file:
        body = helpers.template_handler(file=file, params_file=params_file, params=params)
        body = helpers.remove_unknown_args(body, image_client.update_version)

    updated_body = combine_dictionaries(
        body.get("body") or {},
        user_options,
    )
    artifact = helpers.get_updated_artifacts(body, artifact_path, artifact_ignore_files)

    if build_arguments:
        updated_body["buildDetails"]["buildArguments"] = helpers.parse_build_arguments(build_arguments)

    if secrets:
        updated_body["buildDetails"]["secrets"] = parse_list_of_strings(secrets)

    response: Dict[str, str] = image_client.update_version(
        image_id=image_id,
        version_id=version_id,
        body=updated_body,
        artifact=artifact,  # type: ignore  # noqa: PGH003
    )
    console.print(response)


@app.command(short_help="Delete an image.")
def delete(
    ctx: typer.Context,
    image_id: int = _IMAGE_ID,
) -> None:
    """***Delete*** an image. If multiple versions are present in the image, this will delete only the ***latest version***.

    \b
    📝 ***Example usage:***<br/>
    ```bash
    peak images delete 9999
    ```

    \b
    🆗 ***Response:***
    ```json
    {}
    ```

    🔗 [**API Documentation**](https://service.peak.ai/image-management/api-docs/index.htm#/images/delete_api_v1_images__imageId_)
    """
    image_client: Image = ctx.obj["client"]
    response = image_client.delete_image(image_id=image_id)
    console.print(response)


@app.command(short_help="Delete an image version.")
def delete_version(
    ctx: typer.Context,
    image_id: int = args.IMAGE_ID,
    version_id: int = args.VERSION_ID,
) -> None:
    """***Delete*** an image version. An image cannot be deleted if it is being used by any other resource.

    \b
    📝 ***Example usage:***<br/>
    ```bash
    peak images delete-version --image-id 9999 --version-id 1
    ```

    \b
    🆗 ***Response:***
    ```json
    {}
    ```

    🔗 [**API Documentation**](https://service.peak.ai/image-management/api-docs/index.htm#/versions/delete_api_v2_images__imageId__versions__versionId_)
    """
    image_client: Image = ctx.obj["client"]
    response: Dict[None, None] = image_client.delete_version(image_id=image_id, version_id=version_id)
    console.print(response)


@app.command(short_help="List image builds.")
def list_builds(
    ctx: typer.Context,
    image_id: int = _IMAGE_ID,
    count: Optional[int] = _COUNT,
    version_ids: Optional[List[str]] = _VERSIONS,
    build_status: Optional[List[str]] = _BUILD_STATUS,
    date_from: Optional[str] = args.DATE_FROM,
    date_to: Optional[str] = args.DATE_TO,
    page_size: Optional[int] = args.PAGE_SIZE,
    page_number: Optional[int] = args.PAGE_NUMBER,
) -> None:
    """***List*** image builds for a specific image. If you want to view builds for a specific version you can pass in Version's ID to the command.

    \b
    📝 ***Example usage:***<br/>
    ```bash
    peak images list-builds 9999 --version-ids 1,2,3 --page-size 10 --page-number 1
    ```

    \b
    🆗 ***Response:***
    ```
    {
        "buildCount": 1,
        "builds": [...],
        "pageCount": 1,
        "pageNumber": 1,
        "pageSize": 25
    }
    ```

    🔗 [**API Documentation**](https://service.peak.ai/image-management/api-docs/index.htm#/builds/get_api_v2_images__imageId__builds)
    """
    image_client: Image = ctx.obj["client"]
    response = image_client.list_image_builds(
        image_id=image_id,
        count=count,
        build_status=parse_list_of_strings(build_status),
        version_ids=parse_list_of_strings(version_ids),
        date_from=date_from,
        date_to=date_to,
        page_size=page_size,
        page_number=page_number,
        return_iterator=False,
    )
    console.print(response)
