#
# # Copyright © 2024 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 blocks specs commands."""
from typing import Dict, List, Optional

import typer
from peak.cli import args, helpers
from peak.cli.args import DRY_RUN, GENERATE_YAML, OUTPUT_TYPES, PAGING
from peak.constants import OutputTypes, OutputTypesNoTable
from peak.output import Writer
from peak.press.blocks import Block
from rich.console import Console

app = typer.Typer()
console = Console()

_SPEC_ID = typer.Argument(..., help="ID of the Block spec to be used in this operation.")


@app.command("list", short_help="List Block specs.", options_metavar="list_block_specs")
def list_block_specs(
    ctx: typer.Context,
    page_size: Optional[int] = args.PAGE_SIZE,
    page_number: Optional[int] = args.PAGE_NUMBER,
    status: Optional[List[str]] = args.STATUS_FILTER_SPECS,
    name: Optional[str] = args.NAME_FILTER,
    title: Optional[str] = args.TITLE_FILTER,
    sort: Optional[List[str]] = args.SORT_KEYS,
    scope: Optional[List[str]] = args.SCOPES,
    featured: Optional[bool] = args.FEATURED,
    paging: Optional[bool] = PAGING,  # noqa: ARG001
    output_type: Optional[OutputTypes] = OUTPUT_TYPES,  # noqa: ARG001
) -> None:
    """***List*** all Block specs that exists in the tenant along with the public-scoped ones.

    \b
    📝 ***Example usage:***<br/>
    ```bash
    peak blocks specs list --featured --scope shared,private --page-size 10 --page-number 1
    ```

    \b
    🆗 ***Response:***
    ```
    {
        "specsCount": 1,
        "specs": [...],
        "pageCount": 1,
        "pageNumber": 1,
        "pageSize": 10
    }
    ```

    🔗 [**API Documentation**](https://press.peak.ai/api-docs/index.htm#/Block%20Specs/get_v1_blocks_specs)
    """
    block_client: Block = ctx.obj["client"]
    writer: Writer = ctx.obj["writer"]

    with writer.pager():
        response = block_client.list_specs(
            status=status,
            featured=featured,
            name=name,
            title=title,
            sort=sort,
            scope=scope,
            page_size=page_size,
            page_number=page_number,
            return_iterator=False,
        )
        writer.write(response)


@app.command(short_help="Create a Block spec.", options_metavar="create_block_spec")
def create(
    ctx: typer.Context,
    file: str = args.TEMPLATE_PATH,
    params_file: Optional[str] = args.TEMPLATE_PARAMS_FILE,
    params: Optional[List[str]] = args.TEMPLATE_PARAMS,
    dry_run: Optional[bool] = DRY_RUN,  # noqa: ARG001
    paging: Optional[bool] = PAGING,  # noqa: ARG001
    output_type: Optional[OutputTypesNoTable] = OUTPUT_TYPES,  # noqa: ARG001
    generate: Optional[bool] = GENERATE_YAML,  # noqa: ARG001
) -> None:
    """***Create*** a Block spec. A Block spec is just a blueprint for a specific resource. This creates a version of the spec also know as a ***Release***.

    \b
    🧩 ***Input file schema (yaml):***<br/>
    ```yaml
    featured (bool | required: false): Boolean specifying whether to feature this spec.
    scope (str | required: false): Specify what tenants can discover and deploy this spec.
    tenants (list(str) | required: false): Given a shared scope, specify what other tenants can discover and deploy this spec.
    body (map):
        version (int): Version of the spec.
        kind (string): Specifies the type of spec.
        metadata (map):
            name (string): Name of the spec. Must be unique within the tenant.
            title (string | required: false): Title of the spec.
            summary (string): Summary of the spec.
            description (string | required: false): Description of the spec.
            descriptionContentType (string | required: false): Content type of the description. Should be one of "text/plain" or "text/markdown".
            imageUrl (string | required: false): URL of the image to be associated with the app spec.
            tags (list(map) | required: false):
                - name (string): Name of the tag.
        release (map):
            version (string): A valid semantic release version of the spec.
            notes (string | required: false): Notes for the release version.
        config (map):
            images(map | required: false):  # To be used only in case of workflow block.
                image-name (map): Dictionary containing the image configuration. Here the key is name of the image.
                    version (string | required: false): A valid semantic image version.
                    dockerfile (string | required: false): Path to the Dockerfile inside artifact.
                    context (string | required: false): The path within the artifact where the code to be executed by the Dockerfile is located.
                    useCache (boolean | required: false): Whether to enable image caching to reduce build time, is enabled by default.
                    buildArguments (map | required: false): Dictionary containing build args. Here the key is the name of the arg and value is the value of the arg.
                    secrets (list(str)) | required: false): List of secret names to be passed.
            steps (map | required: false):   # To be used only in case of workflow block.
                <stepName> (map): Dictionary containing the step configuration. Here the key is name of the step.
                    image: (map | required: false):
                        version (string | required: false): A valid semantic image version.
                        dockerfile (string | required: false): Path to the Dockerfile inside artifact.
                        context (string | required: false): The path within the artifact where the code to be executed by the Dockerfile is located.
                        useCache (boolean | required: false): Whether to enable image caching to reduce build time, is enabled by default.
                        buildArguments (map | required: false): Dictionary containing build args. Here the key is the name of the arg and value is the value of the arg.
                        secrets (list(str)) | required: false): List of secret names to be passed.
                    imageDetails (map | required: false):
                        id (int): ID of the existing image.
                        versionId: (int): ID of the existing image version.
                    imageRef (string | required: false): Name of the image defined above.
                    type (string | required: false): Type of workflow step. Currently only standard type is supported.
                    command (string): Command to run when step is executed.
                    clearImageCache (boolean | required: false): Whether to clear image cache on workflow execution.
                    stepTimeout (int | required: false): Time after which the step timeouts.
                    parameters (map | required: false):
                        env (map | required: false): Key-Value pair where key is the name of the env.
                        secrets (list(str) | required: false): List of secret names to be passed.
                    parents (list(str) | required: false): List containing names of steps on which this step is dependent on.
                    repository (map | required: false):
                        branch (string): Branch of the repository containing the required files.
                        token (string | required: false): The token to be used to clone the repository.
                        url (string): URL of the repository.
                    resources (map | required: false):
                        instanceTypeId (int): ID of the instance type to be used in the step.
                        storage (string): Storage in GB. For example, "10GB".
            triggers (list(map)):
                - cron (string | required: false): A valid cron expression.
                  webhook (boolean | required: false): Should be true if webhook type trigger is to be used.
                  webhookId (string | required: false): ID of the webhook.
                  webhookPolicy (string | required: false): Policy of the webhook to be used. Should be one of "generate" or "preserve". It is "generate" by default.
            watchers (list(map) | required: false):
                - events (map):
                    success (boolean | required: false): Whether to call event on success.
                    fail (boolean | required: false): Whether to call event on failure.
                    runtimeExceeded (int | required: false): The runtime after which event is called.
                  user (string | required: false): User to be notified. To be added in case of user watcher.
                  webhook (map | required: false): To be added in case of webhook watcher.
                    name (string): Name of the webhook.
                    url (string): URL of the webhook.
                    payload (string): Webhook payload.
            image (map | required: false):  # To be used only in case of webapp block.
                version (string | required: false): A valid semantic image version.
                dockerfile (string | required: false): Path to the Dockerfile inside artifact.
                context (string | required: false): The path within the artifact where the code to be executed by the Dockerfile is located.
                useCache (boolean | required: false): Whether to enable image caching to reduce build time, is enabled by default.
                buildArguments (map | required: false): Dictionary containing build args. Here the key is the name of the arg and value is the value of the arg.
                secrets (list(str)) | required: false): List of secret names to be passed.
            imageDetails (map | required: false):  # To be used only in case of webapp block.
                id (int): ID of the existing image.
                versionId: (int): ID of the existing image version.
            resources (map | required: false): To be used only in case of webapp block.
                instanceTypeId (int): ID of the instance type to be used in the webapp.
            sessionStickiness (boolean | required: false): Whether to enable session stickiness for the webapp. It is false by default. To be used only in case of webapp block.
    parameters (map | required: false):
        <parameterType> (list(map)): List containing the parameter objects. Here the key is the type of the parameter. Accepted values are "build" or "run".
            name (string): Name of the parameter.
            type (string): Type of the parameter. Should be one of "string", "number", "boolean" and "string_array".
            required (boolean): Whether the parameter is required.
            description (string | required: false): Description of the parameter.
            defaultValue (string | required: false): Default value of the parameter.
            title (string | required: false): Title of the parameter.
            options (list(str) | required: false): List of options for the parameter. If provided, it must have at least one object with "title" and "value" field.
            hideValue(boolean | required: false): Can be optionally provided to parameters of type "string", to mask the parameter's value when it has been set at deployment time.
    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 blocks specs create /path/to/body.yaml -v /path/to/params.yaml
    ```

    \b
    🆗 ***Response:***
    ```json
    {
        "id": "632a4e7c-ab86-4ecb-8f34-99b5da531ceb"
    }
    ```

    🔗 [**API Documentation**](https://press.peak.ai/api-docs/index.htm#/Block%20Specs/post_v1_blocks_specs)
    """
    block_client: Block = ctx.obj["client"]
    writer: Writer = ctx.obj["writer"]

    body = helpers.template_handler(file, params_file, params)
    body = helpers.remove_unknown_args(body, block_client.create_spec)

    with writer.pager():
        response: Dict[str, str] = block_client.create_spec(**body)
        writer.write(response)


@app.command(short_help="Describe a Block spec.", options_metavar="describe_block_spec")
def describe(
    ctx: typer.Context,
    spec_id: str = _SPEC_ID,
    paging: Optional[bool] = PAGING,  # noqa: ARG001
    output_type: Optional[OutputTypesNoTable] = OUTPUT_TYPES,  # noqa: ARG001
) -> None:
    """***Describe*** a Block spec with details of its latest release.

    \b
    📝 ***Example usage:***<br/>
    ```bash
    peak blocks specs describe "632a4e7c-ab86-4ecb-8f34-99b5da531ceb"
    ```

    \b
    🆗 ***Response:***
    ```
    {
        "featured": true,
        "id": "632a4e7c-ab86-4ecb-8f34-99b5da531ceb"
        "kind": "app",
        "latestRelease": {...},
        "metadata": {...},
        "parameters": {...},
        "scope": "private"
    }
    ```

    🔗 [**API Documentation**](https://press.peak.ai/api-docs/index.htm#/Block%20Specs/get_v1_blocks_specs__specId_)
    """
    block_client: Block = ctx.obj["client"]
    writer: Writer = ctx.obj["writer"]

    with writer.pager():
        response = block_client.describe_spec(spec_id)
        writer.write(response)


@app.command(short_help="Update the Block spec metadata.", options_metavar="update_block_spec_metadata")
def update_metadata(
    ctx: typer.Context,
    spec_id: str = _SPEC_ID,
    file: str = args.TEMPLATE_PATH,
    params_file: Optional[str] = args.TEMPLATE_PARAMS_FILE,
    params: Optional[List[str]] = args.TEMPLATE_PARAMS,
    dry_run: Optional[bool] = DRY_RUN,  # noqa: ARG001
    paging: Optional[bool] = PAGING,  # noqa: ARG001
    output_type: Optional[OutputTypesNoTable] = OUTPUT_TYPES,  # noqa: ARG001
    generate: Optional[bool] = GENERATE_YAML,  # noqa: ARG001
) -> None:
    """***Update*** the Block spec metadata.

    \b
    🧩 ***Input file schema (yaml):***<br/>
    ```yaml
      body (map):
        featured (bool | required: false): Boolean specifying whether to feature this spec.
        scope (str | required: false): Specify what tenants can discover and deploy this spec.
        tenants (list(str) | required: false): Given a shared scope, specify what other tenants can discover and deploy this spec.
        metadata (map):
            name (string | required: false): Name of the spec. Must be unique within the tenant.
            title (string | required: false): Title of the spec.
            summary (string | required: false): Summary of the spec.
            description (string | required: false): Description of the spec.
            descriptionContentType (string | required: false): Content type of the description. Should be one of "text/plain" or "text/markdown".
            imageUrl (string | required: false): URL of the image to be associated with the block spec.
            status (string | required: false): Status of the block spec.
            tags (list(map) | required: false):
                - name (string): Name of the tag.
    ```

    \b
    📝 ***Example usage:***
    ```bash
    peak blocks specs update-metadata "632a4e7c-ab86-4ecb-8f34-99b5da531ceb" /path/to/body.yaml -v /path/to/params.yaml
    ```

    \b
    🆗 ***Response:***
    ```
    {
        "featured": false,
        "id": "632a4e7c-ab86-4ecb-8f34-99b5da531ceb"
        "kind": "workflow",
        "latestRelease": {...},
        "metadata": {...},
        "scope": "private"
    }
    ```

    🔗 [**API Documentation**](https://press.peak.ai/api-docs/index.htm#/Block%20Specs/patch_v1_blocks_specs__specId_)
    """
    body = helpers.template_handler(file, params_file, params)
    writer: Writer = ctx.obj["writer"]

    block_client: Block = ctx.obj["client"]
    body = helpers.remove_unknown_args(body, block_client.update_spec_metadata)

    with writer.pager():
        response: Dict[None, None] = block_client.update_spec_metadata(spec_id, **body)
        writer.write(response)


@app.command(short_help="Delete a Block spec.", options_metavar="delete_block_spec")
def delete(
    ctx: typer.Context,
    spec_id: str = _SPEC_ID,
    dry_run: Optional[bool] = DRY_RUN,  # noqa: ARG001
    paging: Optional[bool] = PAGING,  # noqa: ARG001
    output_type: Optional[OutputTypesNoTable] = OUTPUT_TYPES,  # noqa: ARG001
) -> None:
    """***Delete*** a Block spec.

    \b
    📝 ***Example usage:***<br/>
    ```bash
    peak blocks specs delete "632a4e7c-ab86-4ecb-8f34-99b5da531ceb"
    ```

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

    🔗 [**API Documentation**](https://press.peak.ai/api-docs/index.htm#/Block%20Specs/delete_v1_blocks_specs__specId_)
    """
    block_client: Block = ctx.obj["client"]
    writer: Writer = ctx.obj["writer"]

    with writer.pager():
        response = block_client.delete_spec(spec_id)
        writer.write(response)


@app.command(short_help="Create a new release for a Block spec.", options_metavar="create_block_spec_release")
def create_release(
    ctx: typer.Context,
    spec_id: str = _SPEC_ID,
    file: str = args.TEMPLATE_PATH,
    params_file: Optional[str] = args.TEMPLATE_PARAMS_FILE,
    params: Optional[List[str]] = args.TEMPLATE_PARAMS,
    dry_run: Optional[bool] = DRY_RUN,  # noqa: ARG001
    paging: Optional[bool] = PAGING,  # noqa: ARG001
    output_type: Optional[OutputTypesNoTable] = OUTPUT_TYPES,  # noqa: ARG001
    generate: Optional[bool] = GENERATE_YAML,  # noqa: ARG001
) -> None:
    """***Create*** a new release for a Block spec.

    \b
    🧩 ***Input file schema (yaml):***<br/>
    ```yaml
    body (map):
        release (map):
            version (string): A valid semantic release version of the spec. Must be greater than previous release version.
            notes (string | required: false): Notes for the release version.
        config (map):
            images(map | required: false):  # To be used only in case of workflow block.
                image-name(map): Dictionary containing the image configuration. Here the key is name of the image.
                    version (string | required: false): A valid semantic image version.
                    dockerfile (string | required: false): Path to the Dockerfile inside artifact.
                    context (string | required: false): The path within the artifact where the code to be executed by the Dockerfile is located.
                    useCache (boolean | required: false): Whether to enable image caching to reduce build time, is enabled by default.
                    buildArguments (map | required: false): Dictionary containing build args. Here the key is the name of the arg and value is the value of the arg.
                    secrets (list(str)) | required: false): List of secret names to be passed.
            steps(map | required: false):   # To be used only in case of workflow block.
                <stepName> (map): Dictionary containing the step configuration. Here the key is name of the step.
                    image: (map | required: false):
                        version (string | required: false): A valid semantic image version.
                        dockerfile (string | required: false): Path to the Dockerfile inside artifact.
                        context (string | required: false): The path within the artifact where the code to be executed by the Dockerfile is located.
                        useCache (boolean | required: false): Whether to enable image caching to reduce build time, is enabled by default.
                        buildArguments (map | required: false): Dictionary containing build args. Here the key is the name of the arg and value is the value of the arg.
                        secrets (list(str)) | required: false): List of secret names to be passed.
                    imageDetails (map | required: false):
                        id (int): ID of the existing image.
                        versionId: (int): ID of the existing image version.
                    imageRef (string | required: false): Name of the image defined above.
                    type (string | required: false): Type of workflow step. Currently only standard type is supported.
                    command (string): Command to run when step is executed.
                    clearImageCache (boolean | required: false): Whether to clear image cache on workflow execution.
                    stepTimeout (int | required: false): Time after which the step timeouts.
                    parameters (map | required: false):
                        env (map | required: false): Key-Value pair where key is the name of the env.
                        secrets (list(str) | required: false): List of secret names to be passed.
                    parents (list(str) | required: false): List containing names of steps on which this step is dependent on.
                    repository (map | required: false):
                        branch (string): Branch of the repository containing the required files.
                        token (string | required: false): The token to be used to clone the repository.
                        url (string): URL of the repository.
                    resources (map | required: false):
                        instanceTypeId (int): ID of the instance type to be used in the step.
                        storage (string): Storage in GB. For example, "10GB".
            triggers (list(map)):
                cron (string | required: false): A valid cron expression.
                webhook (boolean | required: false): Should be true if webhook type trigger is to be used.
                webhookId (string | required: false): ID of the webhook.
                webhookPolicy (string | required: false): Policy of the webhook to be used. Should be one of "generate" or "preserve". It is "generate" by default.
            watchers (list(map) | required: false):
                - events (map):
                    success (boolean | required: false): Whether to call event on success.
                    fail (boolean | required: false): Whether to call event on failure.
                    runtimeExceeded (int | required: false): The runtime after which event is called.
                  user (string | required: false): User to be notified. To be added in case of user watcher.
                  webhook (map | required: false): To be added in case of webhook watcher.
                    name (string): Name of the webhook.
                    url (string): URL of the webhook.
                    payload (string): Webhook payload.
            image (map | required: false):  # To be used only in case of webapp block.
                version (string | required: false): A valid semantic image version.
                dockerfile (string | required: false): Path to the Dockerfile inside artifact.
                context (string | required: false): The path within the artifact where the code to be executed by the Dockerfile is located.
                useCache (boolean | required: false): Whether to enable image caching to reduce build time, is enabled by default.
                buildArguments (map | required: false): Dictionary containing build args. Here the key is the name of the arg and value is the value of the arg.
                secrets (list(str)) | required: false): List of secret names to be passed.
            imageDetails (map | required: false):  # To be used only in case of webapp block.
                id (int): ID of the existing image.
                versionId: (int): ID of the existing image version.
            resources (map | required: false): To be used only in case of webapp block.
                instanceTypeId (int): ID of the instance type to be used in the webapp.
            sessionStickiness (boolean | required: false): Whether to enable session stickiness for the webapp. It is false by default. To be used only in case of webapp block.
    parameters (map | required: false):
        <parameterType> (list(map)): List containing the parameter objects. Here the key is the type of the parameter. Accepted values are "build" or "run".
            name (string): Name of the parameter.
            type (string): Type of the parameter. Should be one of "string", "number", "boolean" and "string_array".
            required (boolean): Whether the parameter is required.
            description (string | required: false): Description of the parameter.
            defaultValue (string | required: false): Default value of the parameter.
            title (string | required: false): Title of the parameter.
            options (list(str) | required: false): List of options for the parameter. If provided, it must have at least one object with "title" and "value" field.
    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 blocks specs create-release "632a4e7c-ab86-4ecb-8f34-99b5da531ceb" '/path/to/body.yaml' -v '/path/to/params.yaml'
    ```

    \b
    🆗 ***Response:***
    ```json
    {
        "id": "632a4e7c-ab86-4ecb-8f34-99b5da531ceb",
        "release": {
            "version": "2.0.0"
        }
    }
    ```

    🔗 [**API Documentation**](https://press.peak.ai/api-docs/index.htm#/Block%20Specs/post_v1_blocks_specs__specId__releases)
    """
    body = helpers.template_handler(file, params_file, params)
    writer: Writer = ctx.obj["writer"]

    block_client: Block = ctx.obj["client"]
    body = helpers.remove_unknown_args(body, block_client.create_spec_release)

    with writer.pager():
        response: Dict[str, str] = block_client.create_spec_release(spec_id, **body)
        writer.write(response)


@app.command(short_help="Describe a Block spec release.", options_metavar="describe_block_spec_release")
def describe_release(
    ctx: typer.Context,
    spec_id: str = args.SPEC_ID,
    version: str = args.RELEASE_VERSION,
    paging: Optional[bool] = PAGING,  # noqa: ARG001
    output_type: Optional[OutputTypesNoTable] = OUTPUT_TYPES,  # noqa: ARG001
) -> None:
    """***Describe*** a Block spec release.

    \b
    📝 ***Example usage:***<br/>
    ```bash
    peak blocks specs describe-release --spec-id "632a4e7c-ab86-4ecb-8f34-99b5da531ceb" --version 1.0.0
    ```

    \b
    🆗 ***Response:***
    ```
    {
        "artifact": {
            "id": "721d738a-29f3-43b2-af52-c9055abe60b6",
            "version": 1
        },
        "config": {
            "images": {...},
            "steps": {...},
            "triggers": [
                {...}
            ],
            "watchers": [
                {...}
            ]
        },
        "createdAt": "2020-01-01T18:00:00.000Z",
        "createdBy": "someoneh@peak.ai",
        "id": "df113d64-ff44-4aa0-9278-edb03dae7a3f",
        "notes": "This is the original release",
        "parameters": {...},
    }
    ```

    🔗 [**API Documentation**](https://press.peak.ai/api-docs/index.htm#/Block%20Specs/get_v1_blocks_specs__specId__releases__release_)
    """
    block_client: Block = ctx.obj["client"]
    writer: Writer = ctx.obj["writer"]

    with writer.pager():
        response = block_client.describe_spec_release(spec_id, version)
        writer.write(response)


@app.command(short_help="List releases of a Block spec.", options_metavar="list_block_spec_releases")
def list_releases(
    ctx: typer.Context,
    spec_id: str = args.SPEC_ID,
    sort: Optional[List[str]] = args.SORT_KEYS,
    page_size: Optional[int] = args.PAGE_SIZE,
    page_number: Optional[int] = args.PAGE_NUMBER,
    paging: Optional[bool] = PAGING,  # noqa: ARG001
    output_type: Optional[OutputTypes] = OUTPUT_TYPES,  # noqa: ARG001
) -> None:
    """***List*** all releases for a given Block spec.

    \b
    📝 ***Example usage:***<br/>
    ```bash
    peak blocks specs list-releases --spec-id "632a4e7c-ab86-4ecb-8f34-99b5da531ceb" --sort createdBy:asc,createdAt --page-size 10 --page-number 1
    ```

    \b
    🆗 ***Response:***
    ```
    {
        "pageCount": 1,
        "pageNumber": 1,
        "pageSize": 10,
        "releaseCount": 1,
        "releases": [...]
    }
    ```

    🔗 [**API Documentation**](https://press.peak.ai/api-docs/index.htm#/Block%20Specs/get_v1_blocks_specs__specId__releases)
    """
    block_client: Block = ctx.obj["client"]
    writer: Writer = ctx.obj["writer"]

    with writer.pager():
        response = block_client.list_spec_releases(
            spec_id,
            sort=sort,
            page_size=page_size,
            page_number=page_number,
            return_iterator=False,
        )
        writer.write(response)
