import logging
import shutil
from pathlib import Path

from git import InvalidGitRepositoryError, Repo
from kedro.framework.hooks import hook_impl

from everycure.datasets.kedro.storage import GitStorageService
from everycure.datasets.kedro.config import GitOpsConfig
logger = logging.getLogger(__name__)


class GitStorageHook:
    """Kedro hook to clone or update a Git repository before running a pipeline."""

    @hook_impl
    def after_context_created(self, context):
        """Clone or update repo before the pipeline runs."""

        try:
            if "gitops" not in context.config_loader.config_patterns.keys():
                context.config_loader.config_patterns.update(
                    {"gitops": ["gitops*", "gitops*/**", "**/gitops*"]}
                )
            conf_gitops_yml = context.config_loader["gitops"]
        except MissingConfigException:
            self._logger.warning(
                "No 'gitops.yml' config file found in environment. Default configuration will be used. Use ``kedro gitops init`` command in CLI to customize the configuration."
            )
            # we create an empty dict to have the same behaviour when the argo.yml
            # is commented out. In this situation there is no MissingConfigException
            # but we got an empty dict
            conf_gitops_yml = {}

        conf_gitops_yml: GitOpsConfig = GitOpsConfig.model_validate(conf_gitops_yml)
        context.__setattr__("gitops", conf_gitops_yml)
        target_dir = Path(conf_gitops_yml.options.target_dir)

        if conf_gitops_yml.options.force and target_dir.exists():
            shutil.rmtree(target_dir)

        if not target_dir.exists():
            repo = Repo.clone_from(
                conf_gitops_yml.origin.repo_url, 
                conf_gitops_yml.options.target_dir, 
                branch=conf_gitops_yml.origin.branch
            )

        else:
            try:
                repo = Repo(target_dir)
                logger.info(f"📁 Existing repo found: {repo.working_dir}")

                if conf_gitops_yml.options.pull:
                    logger.info(f"⬇️ Pulling latest changes from {conf_gitops_yml.origin.branch}")
                    origin = repo.remotes.origin
                    origin.fetch()
                    origin.pull(conf_gitops_yml.origin.branch)
                    logger.info("✅ Repository updated.")
                else:
                    logger.info("🔸 Skipping pull, using existing contents.")

                # Ensure branch consistency
                repo.git.checkout(conf_gitops_yml.origin.branch)
            except InvalidGitRepositoryError:
                if conf_gitops_yml.options.force:
                    logger.info(
                        f"⚠️ {target_dir} exists but is not a Git repo. Re-cloning."
                    )
                    shutil.rmtree(target_dir)
                    Repo.clone_from(
                        conf_gitops_yml.origin.repo_url, 
                        conf_gitops_yml.options.target_dir, 
                        branch=conf_gitops_yml.origin.branch
                    )

        self.git_service = GitStorageService(
            root_path=str(target_dir),
            user=conf_gitops_yml.user.name,
            email=conf_gitops_yml.user.email,
        )

        logger.info(f"🔁 GitStorageService ready at {target_dir}")


git_storage_hook = GitStorageHook()