# Copyright 2023-2025 Marcel Bollmann <marcel@bollmann.me>
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
#     http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License 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.

"""Functions implementing the automatic cloning of the Anthology repository."""

from __future__ import annotations

from git.repo import Repo
from git import RemoteProgress
from pathlib import Path
from rich.progress import Progress
from typing import Optional, TYPE_CHECKING

if TYPE_CHECKING:
    from _typeshed import StrPath

from .logging import get_logger

log = get_logger()


def clone_or_pull_from_repo(
    repo_url: str, local_path: StrPath, verbose: Optional[bool]
) -> None:
    """Clones a Git repository, or pulls from remote if it already exists.

    Arguments:
        repo_url: The URL of a Git repo.
        local_path: The local path containing the repo.  If `{local_path}/.git` exists, we assume it already contains the repo and will attempt to pull from 'origin'; otherwise, we will attempt to clone the repo into `local_path`.
        verbose: If True, will show a progress display.
    """
    path = Path(local_path)
    progress = RichRemoteProgress() if verbose else None
    log.debug(f"Using local repository folder: {path}")
    if (path / ".git").exists():
        repo = Repo(path)
        if repo.remote().url != repo_url:
            log.error(
                (
                    "Repository folder exists, but doesn't match the given URL:\n",
                    f"   {repo.remote().url} != {repo_url}",
                )
            )
        log.info(f"Fetching updates from: {repo_url}")
        repo.remote().pull(force=True, progress=progress)
    else:
        log.info(f"Cloning repository: {repo_url}")
        repo = Repo.clone_from(
            repo_url, path, progress=progress, single_branch=True, depth=1  # type: ignore[arg-type]
        )
        # ^-- It seems that Repo.clone_from() has an incorrect type signature
        # for its progress argument...
    if progress is not None:
        progress.progress.update(progress.task, completed=210.0)
        progress.progress.stop()


# This is a bit hacky, mainly to provide a "smoother" user experience, but
# could probably be simplified.
class RichRemoteProgress(RemoteProgress):
    def __init__(self) -> None:
        super().__init__()
        self.progress = Progress()
        self.task = self.progress.add_task(
            "Fetching Anthology data...", start=False, visible=False, total=210.0
        )

    def update(
        self,
        op_code: int,
        cur_count: str | float,
        max_count: Optional[str | float] = None,
        message: str = "",
    ) -> None:
        operation = op_code & self.OP_MASK

        # Satisfying the type checker, even though this _should_ always be float ...
        if isinstance(cur_count, str):
            try:
                cur_count = float(cur_count)
            except ValueError:
                cur_count = 0.0
        if isinstance(max_count, str):
            try:
                max_count = float(max_count)
            except ValueError:
                max_count = 100.0
        elif max_count is None:
            max_count = 100.0

        if operation == self.COUNTING:
            if op_code & self.BEGIN:
                self.progress.start()
                self.progress.start_task(self.task)
                self.progress.update(self.task, visible=True)
            if op_code & self.END:
                value = 9.0
                self.progress.update(self.task, completed=value)
        elif operation == self.COMPRESSING:
            value = 9.0 + 100.0 * (cur_count / max_count)
            self.progress.update(self.task, completed=value)
        elif operation == self.RECEIVING:
            value = 109.0 + 100.0 * (cur_count / max_count)
            self.progress.update(self.task, completed=value)
        elif (op_code & self.END) and (operation == self.RESOLVING):
            self.progress.update(self.task, completed=210.0)
            self.progress.stop()
