import json
import logging
import re
import subprocess
from functools import cache
from packaging.version import Version

logger = logging.getLogger(__name__)


@cache
def fetch_releases(repo: str, *, include_prereleases: bool = True) -> list[dict]:
    result = subprocess.run(
        [
            "gh",
            "release",
            "list",
            "--limit",
            "1000",
            "--json",
            "tagName,publishedAt",
            "--exclude-drafts",
            *(["--repo", repo] if repo else []),
            *(["--exclude-pre-releases"] if not include_prereleases else []),
        ],
        capture_output=True,
        text=True,
        check=False,
    )
    if result.returncode != 0:
        raise RuntimeError(f"gh command failed: {result.stderr}")
    releases = json.loads(result.stdout)
    return releases


def find_latest_release(
    tag_regex: str, include_prereleases=True, repo=None
) -> dict | None:
    releases = fetch_releases(repo, include_prereleases=include_prereleases)

    if tag_regex:
        relevant_releases = [r for r in releases if re.match(tag_regex, r["tagName"])]
    else:
        relevant_releases = releases

    relevant_releases.sort(
        key=lambda r: Version(re.match(tag_regex, r["tagName"]).group("version")),
        reverse=True,
    )

    latest_release = relevant_releases[0] if relevant_releases else None

    return latest_release


def create_or_update_draft_release(
    tag_name: str,
    title: str,
    body: str,
    repo: str = None,
    dry_run: bool = False,
    prerelease: bool = False,
) -> None:
    result = subprocess.run(
        [
            "gh",
            "release",
            "view",
            tag_name,
            *(["--repo", repo] if repo else []),
            "--json",
            "tagName,name,body",
        ],
        capture_output=True,
        text=True,
        check=False,
    )
    exists = result.returncode == 0

    if exists:
        existing_release = json.loads(result.stdout)
        needs_update = not all(
            (
                tag_name == existing_release["tagName"],
                title.strip() == existing_release["name"].strip(),
                body.strip() == existing_release["body"].strip(),
            )
        )
        if not needs_update:
            logger.info(f"No changes for release {tag_name}, skipping update.")
            return
        elif dry_run:
            logger.info(
                f"Would update draft release {tag_name} in repo {repo or 'default'} with title '{title}' and body:\n{body}\n"
            )
            return

        logger.info(f"Updating existing release {tag_name}.")

        cmd = [
            "gh",
            "release",
            "edit",
            tag_name,
            *(["--repo", repo] if repo else []),
            *("--title", title),
            *("--notes", body),
            *(["--prerelease"] if prerelease else []),
        ]
    else:
        if dry_run:
            logger.info(
                f"Would create draft release {tag_name} in repo {repo or 'default'} with title '{title}' and body:\n{body}\n"
            )
            return

        logger.info(f"Creating new draft release {tag_name}.")

        cmd = [
            "gh",
            "release",
            "create",
            tag_name,
            "--draft",
            *(["--repo", repo] if repo else []),
            *("--title", title),
            *("--notes", body),
            *(["--prerelease"] if prerelease else []),
        ]

    result = subprocess.run(cmd, capture_output=True, text=True, check=False)
    if result.returncode != 0:
        raise RuntimeError(f"gh command failed: {result.stderr}")
