#
# # Copyright © 2026 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>
#
"""Publish apps and blocks from YAML configuration with version management.

YAML format requires 'app' section with name/path/params, 'blocks' with name/path/params,
and optional 'external_blocks' with block_id/version. The IS_ALPHA param controls alpha
versioning (e.g., "0.1.0-a0000001"). Versions auto-increment if they exist.
"""

from __future__ import annotations

import os
from pathlib import Path
from typing import Any

import yaml

from peak.press import specs
from peak.press.wrappers.publish.utils.env import get_base_params, resolve_env_vars, resolve_path, to_bool
from peak.press.wrappers.publish.utils.publish_block import publish_block
from peak.press.wrappers.publish.utils.release_version import get_version
from peak.press.wrappers.publish.utils.spec import publish_data_model, publish_spec
from peak.tools.logging import LogLevel, PeakLogger, get_logger

logger: PeakLogger = get_logger(level=LogLevel.INFO)


def publish(
    publish_config_path: str,
    base_dir: str,
    *,
    publish_app: bool = True,
) -> tuple[str, str] | list[dict[str, Any]]:
    """Publish an app and its blocks or just blocks based on a YAML configuration.

    Args:
        publish_config_path: Path to the publish YAML configuration file
        base_dir: Base directory for resolving relative paths
        publish_app: Whether to publish an app (True) or just blocks (False)

    Returns:
        Tuple of (app_id, version) if publish_app=True, list of published blocks otherwise

    Raises:
        ValueError: If configuration is invalid or required fields are missing
    """
    with Path(resolve_path(publish_config_path, Path(base_dir))).open() as f:
        config = yaml.safe_load(f)

    existing_specs = list(specs.get_client().list_specs())
    base_params = get_base_params()

    if publish_app:
        app_spec = config.get("app")
        if not app_spec:
            msg = "Deployment spec missing required 'app' section"
            raise ValueError(msg)

        app_name = app_spec["name"]
        app_params = resolve_env_vars(app_spec.get("params", {}))
        app_version = get_version(
            existing_specs,
            app_name,
            app_params.get("VERSION"),
            increment=app_params.get("increment_release_version", True),
            is_alpha=to_bool(app_params.get("IS_ALPHA", False)),
        )
        if not app_version:
            msg = f"App version could not be resolved for app: {app_name}"
            raise ValueError(msg)

        merged_app_params = {
            **base_params,
            **app_params,
            "app_name": app_name,
            "VERSION": app_version,
        }

        block_metadata = {}
        for block in config.get("blocks", []):
            block_id, version = publish_block(block, base_dir, merged_app_params, existing_specs)
            block_metadata[block["name"]] = {"id": block_id, "version": version}

        # Handle external blocks - get block_id and version from config
        external_blocks = []
        for external_block in config.get("external_blocks", []):
            block_name = external_block["name"]
            block_id = external_block.get("block_id")
            block_version = external_block.get("version")

            if not block_id or not block_version:
                msg = f"Missing required fields for external block '{block_name}': required 'block_id' and 'version'"
                raise ValueError(msg)

            external_blocks.append(
                {
                    "id": block_id,
                    "version": block_version,
                    "run_on_deploy": external_block.get("run_on_deploy", False),
                },
            )

        # Combine regular blocks and external blocks
        all_blocks = [
            {
                "id": block_metadata[blk["name"]]["id"],
                "version": block_metadata[blk["name"]]["version"],
                "run_on_deploy": blk.get("run_on_deploy", False),
            }
            for blk in config.get("blocks", [])
        ] + external_blocks

        final_app_params = {
            **merged_app_params,
            "BLOCKS": all_blocks,
        }

        app_id = publish_spec(
            existing_specs=existing_specs,
            spec_name=app_name,
            version=app_version,
            template_path=resolve_path(app_spec["path"], Path(base_dir)),
            params=final_app_params,
            is_app=True,
        )

        if os.getenv("PUBLISH_DATA_MODEL"):
            publish_data_model(app_id, app_version)

        logger.info("✅ App and blocks published successfully.")
        return app_id, app_version

    blocks_list = config.get("blocks", [])
    if not blocks_list:
        msg = "No blocks found in deployment spec."
        raise ValueError(msg)

    published_blocks = []
    for block in blocks_list:
        block_id, version = publish_block(block, base_dir, {}, existing_specs)
        logger.info("✅ Block '%s' published successfully.", block["name"])
        published_blocks.append({"block_id": block_id, "version": version})

    return published_blocks
