#
# # 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>
#
"""Version management utilities for release publishing."""

from __future__ import annotations

import re
from pathlib import Path
from typing import Any

import tomli

from peak.press.wrappers.publish.utils.spec import is_spec_version_exists
from peak.tools.logging import LogLevel, PeakLogger, get_logger

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


def get_version_from_pyproject() -> str:
    """Retrieve the version string directly from pyproject.toml.

    Returns:
        The version string defined in pyproject.toml.

    Raises:
        FileNotFoundError: If pyproject.toml is not found.
        KeyError: If the version key is missing from pyproject.toml.
    """
    try:
        with Path("pyproject.toml").open("rb") as f:
            data = tomli.load(f)
        return str(data["tool"]["poetry"]["version"])
    except (FileNotFoundError, KeyError):
        logger.exception("Failed to read version from pyproject.toml")
        raise


def increment_release_version(version_str: str, *, is_alpha: bool = True) -> str:
    """Increment version string with optional alpha suffix.

    Args:
        version_str: Current version string (e.g., "0.0.5" or "0.0.6-a0000001")
        is_alpha: Whether to return an alpha (pre-release) version

    Returns:
        Incremented version string

    Raises:
        ValueError: If the version string format is invalid
    """
    if not version_str:
        version_str = "0.0.1"

    if "-" in version_str:
        main_version, pre_release = version_str.split("-")
    else:
        main_version, pre_release = version_str, ""

    try:
        major, minor, patch = map(int, main_version.split("."))
    except ValueError:
        msg = f"Invalid version format: {version_str}. Expected MAJOR.MINOR.PATCH"
        raise ValueError(msg) from None

    if is_alpha:
        if pre_release:
            # Already an alpha, increment its numeric part
            match = re.match(r"([a-zA-Z]+)(\d+)", pre_release)
            if not match:
                msg = f"Invalid pre-release format: {pre_release}"
                raise ValueError(msg)
            tag, tag_num = match.groups()
            new_tag_num = str(int(tag_num) + 1).zfill(len(tag_num))
            return f"{major}.{minor}.{patch}-{tag}{new_tag_num}"
        # Add alpha suffix to current version without incrementing patch
        tag = "a"
        new_tag_num = "0000001"
        return f"{major}.{minor}.{patch}-{tag}{new_tag_num}"

    if pre_release:
        return f"{major}.{minor}.{patch}"
    patch += 1
    return f"{major}.{minor}.{patch}"


def get_latest_version(existing_specs: list[dict[str, Any]], spec_name: str) -> str:
    """Get latest version of the spec if exists."""
    for spec in existing_specs:
        if spec["metadata"]["name"] == spec_name:
            return str(spec["latestRelease"]["version"])
    return "0.0.0"


def get_latest_alpha_version(existing_specs: list[dict[str, Any]], spec_name: str) -> str | None:
    """Get latest alpha version of the spec if exists, otherwise return None."""
    for spec in existing_specs:
        if spec["metadata"]["name"] == spec_name:
            latest_version = str(spec["latestRelease"]["version"])
            if "-" in latest_version:
                parts = latest_version.split("-", 1)
                expected_parts = 2
                if len(parts) == expected_parts:
                    pre_release = parts[1]
                    if re.match(r"^a\d+", pre_release):
                        return latest_version
    return None


def get_version(  # noqa: C901, PLR0911
    existing_specs: list[dict[str, Any]],
    name: str,
    custom_version: str | None = None,
    *,
    increment: bool = True,
    is_alpha: bool = False,
) -> str:
    """Get the next version for a spec."""

    def find_available_version(start_version: str, *, is_alpha_flag: bool) -> str:
        """Keep incrementing until we find a version that doesn't exist."""
        current_version = start_version
        max_iterations = 100  # Safety limit to prevent infinite loops
        iteration = 0

        while is_spec_version_exists(existing_specs, name, current_version):
            if not increment:
                msg = f"Version {current_version} already exists for {name}"
                raise ValueError(msg)
            if iteration == 0:
                logger.info("Version %s already exists for %s, incrementing...", current_version, name)
            current_version = increment_release_version(current_version, is_alpha=is_alpha_flag)
            iteration += 1
            if iteration >= max_iterations:
                msg = f"Could not find available version after {max_iterations} iterations for {name}"
                raise ValueError(msg)

        if iteration > 0:
            logger.info("Incremented to version %s for %s", current_version, name)

        return current_version

    if custom_version:
        if is_alpha:
            latest = get_latest_version(existing_specs, name)
            latest_alpha = get_latest_alpha_version(existing_specs, name)

            if latest_alpha:
                logger.info(
                    "Found existing alpha version %s for %s (custom version: %s), incrementing from latest alpha...",
                    latest_alpha,
                    name,
                    custom_version,
                )
                incremented = increment_release_version(latest_alpha, is_alpha=is_alpha)
                return find_available_version(incremented, is_alpha_flag=is_alpha)

            if latest == custom_version:
                logger.info(
                    "Custom version %s matches latest version %s for %s, incrementing...",
                    custom_version,
                    latest,
                    name,
                )
                incremented = increment_release_version(custom_version, is_alpha=is_alpha)
                return find_available_version(incremented, is_alpha_flag=is_alpha)

            alpha_version = increment_release_version(custom_version, is_alpha=is_alpha)
            if is_spec_version_exists(existing_specs, name, alpha_version):
                return find_available_version(alpha_version, is_alpha_flag=is_alpha)
            return alpha_version

        if is_spec_version_exists(existing_specs, name, custom_version):
            return find_available_version(custom_version, is_alpha_flag=is_alpha)

        return custom_version

    if is_alpha:
        latest_alpha = get_latest_alpha_version(existing_specs, name)
        if latest_alpha:
            logger.info("Found existing alpha version %s for %s, incrementing from it...", latest_alpha, name)
            # Increment from latest alpha, then check if incremented version exists
            incremented = increment_release_version(latest_alpha, is_alpha=is_alpha)
            return find_available_version(incremented, is_alpha_flag=is_alpha)

    latest = get_latest_version(existing_specs, name)
    incremented = increment_release_version(latest, is_alpha=is_alpha)
    return find_available_version(incremented, is_alpha_flag=is_alpha)
