import datetime
import logging
import re
from time import time
from typing import Literal

from drafteleu.bump import bump_version
from drafteleu.changelog import compile_changelog, get_prs
from drafteleu.releases import find_latest_release, create_or_update_draft_release

logger = logging.getLogger(__name__)


def preload_prs(repo_slug: str, configurations):
    start = time()

    all_labels = set()
    earliest_release_date = datetime.datetime.now().strftime("%Y-%m-%dT%H:%M:%SZ")
    for config in configurations:
        include_labels = config.get("include_labels", [])
        all_labels.update(include_labels)
        tag_regex = config.get("tag_regex", "v(?P<version>.+)")
        stage = config.get("stage", "both")

        for current_stage in ("pre", "final") if stage == "both" else (stage,):
            include_prerelease = current_stage == "pre"
            latest_release = find_latest_release(
                tag_regex,
                repo=repo_slug,
                include_prereleases=include_prerelease,
            )
            if latest_release is None:
                logger.info("No previous release found, assuming initial release")
                earliest_release_date = "1970-01-01T00:00:00Z"
                # No point in looking for earlier releases
                break
            else:
                release_date = latest_release["publishedAt"]
            if release_date < earliest_release_date:
                earliest_release_date = release_date

    # Now that we have the earliest release date and all labels, fetch PRs once
    prs = get_prs(
        merged_since=earliest_release_date,
        repo=repo_slug,
        labels=list(all_labels),
    )
    logger.info(
        f"Preloaded {len(prs)} PRs since {earliest_release_date} in {time() - start:.2f}s"
    )
    return prs


def pr_has_any_label(pr, labels: set) -> bool:
    return not labels or labels.intersection(pr["labels"])


def drafteleu(
    repo_slug: str,
    *,
    stage: Literal["pre", "final", "both"] = "both",
    dry_run: bool = False,
    configurations: list[dict],
):
    all_prs = preload_prs(repo_slug, configurations)

    for config in configurations:
        version_template = config.get("version_template", "{major}.{minor}.{patch}")
        tag_template = config.get("tag_template", "v{next_version}")
        title_template = config.get("title_template", "Release {next_version}")
        tag_regex = config.get("tag_regex", "v(?P<version>.+)")
        rc_template = config.get("rc_template", "-{pre_label}{pre_number}")
        bump_part = config.get("bump_part", "minor")
        include_labels = set(config.get("include_labels", []))
        for current_stage in ("pre", "final") if stage == "both" else (stage,):
            logger.info(f"Processing stage: {current_stage}")
            include_prerelease = current_stage == "pre"

            latest_release = find_latest_release(
                tag_regex, repo=repo_slug, include_prereleases=include_prerelease
            )
            if latest_release is None:
                logger.info("No previous release found, assuming initial release")
                initial_version = version_template.format(major=0, minor=0, patch=0)
                latest_release = {
                    "tagName": tag_template.format(next_version=initial_version),
                    "publishedAt": "1970-01-01T00:00:00Z",
                }
            else:
                logger.info(
                    f"Latest release: {latest_release['tagName']} at {latest_release['publishedAt']}"
                )

            prs = [
                pr
                for pr in all_prs
                if pr_has_any_label(pr, include_labels)
                and pr["closedAt"] > latest_release["publishedAt"]
            ]
            logger.info(
                f"Found {len(prs)} PRs since last release with labels {include_labels}"
            )

            changelog = compile_changelog(prs)
            logger.debug("Changelog:\n" + changelog)

            current_version = re.match(tag_regex, latest_release["tagName"]).group(
                "version"
            )
            logger.info(f"Current version: {current_version}")
            next_version = bump_version(
                current_version,
                "dev" if include_prerelease else bump_part,
                version_template=version_template,
                rc_template=rc_template,
                rc_bump_part=bump_part,
            )
            next_tag = tag_template.format(next_version=next_version)
            next_title = title_template.format(
                next_version=next_version,
                next_tag=next_tag,
                current_version=current_version,
                current_tag=latest_release["tagName"],
                current_date=latest_release["publishedAt"],
                stage=current_stage,
            )
            logger.info(f"Next version: {next_version}")
            logger.info(f"Next tag: {next_tag}")
            logger.info(f"Next title: {next_title}")

            create_or_update_draft_release(
                next_tag,
                next_title,
                changelog,
                repo=repo_slug,
                dry_run=dry_run,
                prerelease=include_prerelease,
            )
