#
# # 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 Webapps service commands."""

from typing import Any, 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.helpers import combine_dictionaries, map_user_options, parse_list_of_strings, variables_to_dict
from peak.output import Writer
from peak.resources.webapps import Webapp
from typing_extensions import Annotated

app = typer.Typer(
    help="Visualize your data and predictions using Webapps. Please note that only generic (EKS-based) webapps are supported with CLI.",
    short_help="Create and Manage Webapps.",
)

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

_LIST_STATUS = typer.Option(
    None,
    help="A list of webapp status to filter the list by. Valid values are `CREATING`, `DEPLOYING`, `AVAILABLE`, `DELETING`, `CREATE_FAILED`, `DELETE_FAILED`.",
)

_LIST_NAME = typer.Option(None, help="Webapp name to search for")

FILE_HELP_STRING = (
    "Path to the file that defines the body for this operation, supports both `yaml` file or a `jinja` template."
)

NAME = typer.Option(None, help="Name of the webapp")

DESCRIPTION = typer.Option(None, help="Description of the webapp")

TITLE = typer.Option(None, help="Title of the webapp")

IMAGE_ID = typer.Option(None, help="ID of the image to be used in this operation.")

VERSION_ID = typer.Option(
    None,
    help="ID of the version to be used in this operation. If version-id is not passed, webapp will be created using latest ready version of the image.",
)

INSTANCE_TYPE_ID = typer.Option(None, help="The ID of the instance type to be used in this operation.")

SESSION_STICKINESS = typer.Option(None, help="Enable session stickiness for the webapp.")

MAPPING = {"imageId": "imageDetails", "versionId": "imageDetails", "instanceTypeId": "resources"}


@app.command("list", short_help="List webapps.", options_metavar="list_webapps")
def list_webapps(
    ctx: typer.Context,
    page_size: Optional[int] = args.PAGE_SIZE,
    page_number: Optional[int] = args.PAGE_NUMBER,
    status: Optional[List[str]] = _LIST_STATUS,
    name: Optional[str] = _LIST_NAME,
    paging: Optional[bool] = PAGING,  # noqa: ARG001
    output_type: Optional[OutputTypes] = OUTPUT_TYPES,  # noqa: ARG001
) -> None:
    """***List*** all webapps that exist for the tenant.

    \b
    📝 ***Example usage:***<br/>
    ```bash
    peak webapps list --page-size 10 --page-number 1 --status CREATING --name test
    ```

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

    🔗 [**API Documentation**](https://service.peak.ai/webapps/api-docs/index.htm#/Webapps/list-webapp)
    """
    webapp_client: Webapp = ctx.obj["client"]
    writer: Writer = ctx.obj["writer"]

    with writer.pager():
        response = webapp_client.list_webapps(
            page_size=page_size,
            page_number=page_number,
            return_iterator=False,
            status=parse_list_of_strings(status),
            name=name,
        )
        writer.write(response)


@app.command(short_help="Create a new webapp.", options_metavar="create_webapp")
def create(
    ctx: typer.Context,
    file: Annotated[Optional[str], typer.Argument(..., help=FILE_HELP_STRING)] = None,
    params_file: str = args.TEMPLATE_PARAMS_FILE,
    params: List[str] = args.TEMPLATE_PARAMS,
    name: str = NAME,
    title: str = TITLE,
    description: str = DESCRIPTION,
    image_id: int = IMAGE_ID,
    version_id: int = VERSION_ID,
    instance_type_id: int = INSTANCE_TYPE_ID,
    session_stickiness: bool = SESSION_STICKINESS,
    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 webapp and start its deployment.

    \b
    🧩 ***Input file schema(yaml):***<br/>
    ```yaml
      body (map):
        name (string): Name of the webapp.
        title (string | required: false): Title of the webapp.
        description (string | required: false): Description of the webapp.
        imageDetails (map):
            imageId (number): ID of the image.
            versionId (number | required: false): ID of the image version. If versionId is not passed, webapp will be created using latest ready version of the image.
        resources (map | required: false):
            instanceTypeId (number): ID of the instance type.
        sessionStickiness (boolean | required: false): Enable session stickiness for the webapp. Default value is false. Enabling session stickiness will tie each user to a specific server for all their requests.
    ```

    \b
    📝 ***Example usage:***
    ```bash
    peak webapps create '/path/to/file.yml' -v '/path/to/params.yml'
    ```

    \b
    📝 ***Example usage without yaml:***
    ```bash
    peak webapps create --name <name> --title <title> --description <description> --image-id <image-id> --version-id <version-id> --instance-type-id <instance-type-id> --session-stickiness
    ```

    \b
    🆗 ***Response:***
    ```json
    {
        "id": "db88c21d-1add-45dd-a72e-8c6b83b68dee"
    }
    ```

    🔗 [**API Documentation**](https://service.peak.ai/webapps/api-docs/index.htm#/Webapps/create-webapp)
    """
    webapp_client: Webapp = ctx.obj["client"]
    writer: Writer = ctx.obj["writer"]

    user_options: Dict[str, Any] = variables_to_dict(
        name,
        description,
        title,
        image_id,
        version_id,
        instance_type_id,
        session_stickiness,
    )
    user_options = map_user_options(user_options, MAPPING)

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

    body = combine_dictionaries(body.get("body") or {}, user_options)

    with writer.pager():
        response = webapp_client.create_webapp(body=body)
        writer.write(response)


@app.command(short_help="Update an existing webapp.", options_metavar="update_webapp")
def update(
    ctx: typer.Context,
    webapp_id: str = _WEBAPP_ID,
    file: Annotated[Optional[str], typer.Argument(..., help=FILE_HELP_STRING)] = None,
    params_file: str = args.TEMPLATE_PARAMS_FILE,
    params: List[str] = args.TEMPLATE_PARAMS,
    title: str = TITLE,
    description: str = DESCRIPTION,
    image_id: int = IMAGE_ID,
    version_id: int = VERSION_ID,
    instance_type_id: int = INSTANCE_TYPE_ID,
    session_stickiness: bool = SESSION_STICKINESS,
    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*** an existing webapp.

    \b
    When updating the webapp, it will trigger a redeployment only under specific conditions.
    Redeployment is triggered if you make changes to any of the following parameters: imageId, versionId, instanceTypeId or sessionStickiness.
    However, only modifying the title or description will not trigger a redeployment.

    \b
    🧩 ***Input file schema(yaml):***<br/>
    ```yaml
      body (map):
        title (string | required: false): Title of the webapp.
        description (string | required: false): Description of the webapp.
        imageDetails (map | required: false):
            imageId (number): ID of the image.
            versionId (number | required: false): ID of the image version. If versionId is not passed, webapp will be created using latest ready version of the image.
        resources (map | required: false):
            instanceTypeId (number): ID of the instance type.
        sessionStickiness (boolean | required: false): Enable session stickiness for the webapp. Default value is false. Enabling session stickiness will tie each user to a specific server for all their requests.
    ```

    \b
    📝 ***Example usage:***
    ```bash
    peak webapps update <webapp-id> '/path/to/file.yml' -v '/path/to/params.yml'
    ```

     \b
    📝 ***Example usage without yaml:***
    ```bash
    peak webapps update <webapp-id> --title <title> --description <description> --image-id <image-id> --version-id <version-id> --instance-type-id <instance-type-id> --session-stickiness
    ```

    \b
    🆗 ***Response:***
    ```json
    {
        "id": "ab11c21d-1add-45dd-a72e-8c6b83b68dee"
    }
    ```

    🔗 [**API Documentation**](https://service.peak.ai/webapps/api-docs/index.htm#/Webapps/update-webapp)
    """
    webapp_client: Webapp = ctx.obj["client"]
    writer: Writer = ctx.obj["writer"]

    user_options: Dict[str, Any] = variables_to_dict(
        description,
        title,
        image_id,
        version_id,
        instance_type_id,
        session_stickiness,
    )
    user_options = map_user_options(user_options, MAPPING)

    body: Dict[str, Any] = {}
    if file:
        body = helpers.template_handler(file=file, params_file=params_file, params=params)
        body = helpers.remove_unknown_args(body, webapp_client.update_webapp)
    body = combine_dictionaries(body.get("body") or {}, user_options)

    with writer.pager():
        response = webapp_client.update_webapp(webapp_id=webapp_id, body=body)
        writer.write(response)


@app.command(short_help="Create a new webapp or Update an existing webapp.", options_metavar="create_or_update_webapp")
def create_or_update(
    ctx: typer.Context,
    file: Annotated[Optional[str], typer.Argument(..., help=FILE_HELP_STRING)] = None,
    params_file: str = args.TEMPLATE_PARAMS_FILE,
    params: List[str] = args.TEMPLATE_PARAMS,
    name: str = NAME,
    title: str = TITLE,
    description: str = DESCRIPTION,
    image_id: int = IMAGE_ID,
    version_id: int = VERSION_ID,
    instance_type_id: int = INSTANCE_TYPE_ID,
    session_stickiness: bool = SESSION_STICKINESS,
    output_type: Optional[OutputTypesNoTable] = OUTPUT_TYPES,  # noqa: ARG001
    generate: Optional[bool] = GENERATE_YAML,  # noqa: ARG001
) -> None:
    """***Create*** a new webapp or ***Update*** an existing webapp based on webapp name and start its deployment.

    \b
    When updating the webapp, it will trigger a redeployment only under specific conditions.
    Redeployment is triggered if you make changes to any of the following parameters: imageId, versionId, instanceTypeId or sessionStickiness.
    However, only modifying the title or description will not trigger a redeployment.

    \b
    🧩 ***Input file schema(yaml):***<br/>
    ```yaml
      body (map):
        name (string): Name of the webapp.
        title (string | required: false): Title of the webapp.
        description (string | required: false): Description of the webapp.
        imageDetails (map):
            imageId (number): ID of the image.
            versionId (number | required: false): ID of the image version. If versionId is not passed, webapp will be created using latest ready version of the image.
        resources (map | required: false):
            instanceTypeId (number): ID of the instance type.
        sessionStickiness (boolean | required: false): Enable session stickiness for the webapp. Default value is false. Enabling session stickiness will tie each user to a specific server for all their requests.
    ```

    \b
    📝 ***Example usage:***
    ```bash
    peak webapps create-or-update '/path/to/file.yml' -v '/path/to/params.yml'
    ```

    \b
    📝 ***Example usage without yaml:***
    ```bash
    peak webapps create-or-update --name <name> --title <title> --description <description> --image-id <image-id> --version-id <version-id> --instance-type-id <instance-type-id> --session-stickiness
    ```

    \b
    🆗 ***Response:***
    ```json
    {
        "id": "db88c21d-1add-45dd-a72e-8c6b83b68dee"
    }
    ```

    🔗 [**API Documentation**](https://service.peak.ai/webapps/api-docs/index.htm#/Webapps/create-webapp)
    """
    webapp_client: Webapp = ctx.obj["client"]
    writer: Writer = ctx.obj["writer"]

    user_options: Dict[str, Any] = variables_to_dict(
        name,
        description,
        title,
        image_id,
        version_id,
        instance_type_id,
        session_stickiness,
    )
    user_options = map_user_options(user_options, MAPPING)

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

    body = combine_dictionaries(body.get("body") or {}, user_options)

    response = webapp_client.create_or_update_webapp(body=body)
    writer.write(response)


@app.command(short_help="Delete an existing webapp.", options_metavar="delete_webapp")
def delete(
    ctx: typer.Context,
    webapp_id: str = _WEBAPP_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*** an existing webapp.

    \b
    📝 ***Example usage:***<br/>
    ```bash
    peak webapps delete "ab11c21d-1add-45dd-a72e-8c6b83b68dee"
    ```

    \b
    🆗 ***Response:***
    ```json
    {
        "id": "ab11c21d-1add-45dd-a72e-8c6b83b68dee"
    }
    ```

    🔗 [**API Documentation**](https://service.peak.ai/webapps/api-docs/index.htm#/Webapps/delete-webapp)
    """
    webapp_client: Webapp = ctx.obj["client"]
    writer: Writer = ctx.obj["writer"]

    with writer.pager():
        response = webapp_client.delete_webapp(webapp_id=webapp_id)
        writer.write(response)


@app.command(short_help="Describe details of a webapp.", options_metavar="describe_webapp")
def describe(
    ctx: typer.Context,
    webapp_id: str = _WEBAPP_ID,
    paging: Optional[bool] = PAGING,  # noqa: ARG001
    output_type: Optional[OutputTypesNoTable] = OUTPUT_TYPES,  # noqa: ARG001
) -> None:
    """***Describe*** details for the specific webapp.

    \b
    📝 ***Example usage:***<br/>
    ```bash
    peak webapps describe "ab11c21d-1add-45dd-a72e-8c6b83b68dee"
    ```

    \b
    🆗 ***Response:***
    ```
    {
        "id": "ab11c21d-1add-45dd-a72e-8c6b83b68dee",
        "name": "awesome-webapp",
        "status": "AVAILABLE",
        "imageDetails": {
            "imageId": 1,
            "versionId": 1
        },
        "resources": {
            "instanceTypeId": 1
        },
        "sessionStickiness": false,
        "createdAt": "2020-01-01T18:00:00.000Z",
        "createdBy": "someone@peak.ai",
        "updatedAt": "2020-01-01T18:00:00.000Z",
        "updatedBy": "someone@peak.ai",
        "tags": [...]
    }
    ```

    🔗 [**API Documentation**](https://service.peak.ai/webapps/api-docs/index.htm#/Webapps/get-webapp)
    """
    webapp_client: Webapp = ctx.obj["client"]
    writer: Writer = ctx.obj["writer"]

    with writer.pager():
        response = webapp_client.describe_webapp(webapp_id=webapp_id)
        writer.write(response)
