# (c) 2015-2023 Acellera Ltd http://www.acellera.com
# All Rights Reserved
# Distributed under HTMD Software License Agreement
# No redistribution in whole or part
#
import os
import zipfile
import logging


logger = logging.getLogger(__name__)


def compare_hashes_gcs(bucket, local_hash_loc, remote_hash_loc):
    """Compare hashes using Google Cloud Storage bucket directly."""
    download = True
    source_dir = os.path.dirname(remote_hash_loc)

    try:
        # Download remote hash from GCS
        blob = bucket.blob(remote_hash_loc)
        if not blob.exists():
            print(f"Remote hash file {remote_hash_loc} does not exist.")
            return False

        remote_hash = blob.download_as_text().strip()

        if not os.path.exists(local_hash_loc):
            print(f"No hashes found locally for {source_dir}. Downloading.")
            return True

        # Read local hash
        with open(local_hash_loc, "r") as f:
            local_hash = f.read().strip()

        if local_hash == remote_hash:
            download = False
            print(f"Hashes for {source_dir} matched. Skipping download.")
        else:
            print(f"Hashes for {source_dir} were different. Downloading.")

    except Exception as e:
        print(f"Failed to get current app hash with error {e}")
        # If there was some crash re-download anyway
        download = True

    return download


def _update_protocols(outdir, service_acc_json=None):
    from google.cloud import storage
    from google.oauth2 import service_account

    protocols_file = "acellera-protocols.zip"

    logger.info("Validating Google Cloud credentials")
    image_bucket_name = "69695b3c-2abc-41c8-a06f-03ac55c3fe9a"

    if service_acc_json:
        # Use service account credentials if provided
        cred = service_account.Credentials.from_service_account_file(service_acc_json)
        storage_client = storage.Client(project=cred._project_id, credentials=cred)
    else:
        # Use default credentials (gcloud CLI or Application Default Credentials)
        storage_client = storage.Client()

    image_bucket = storage_client.bucket(image_bucket_name)

    outdir = os.path.join(outdir, "acellera-protocols")
    os.makedirs(outdir, exist_ok=True)

    # Compare hashes
    download = compare_hashes_gcs(
        image_bucket,
        os.path.join(outdir, f"{protocols_file}.md5sum"),
        f"acellera-protocols/{protocols_file}.md5sum",
    )
    if not download:
        return

    print(f"Downloading {protocols_file} from google cloud...")
    blob = image_bucket.blob(f"acellera-protocols/{protocols_file}")
    if not blob.exists():
        print(f"{protocols_file} does not exist.")
        return
    blob.download_to_filename(os.path.join(outdir, protocols_file))
    print(f"Downloading {protocols_file}.md5sum from google cloud...")
    blob = image_bucket.blob(f"acellera-protocols/{protocols_file}.md5sum")
    if not blob.exists():
        print(f"{protocols_file}.md5sum does not exist.")
        return
    blob.download_to_filename(os.path.join(outdir, f"{protocols_file}.md5sum"))

    zipname = os.path.join(outdir, protocols_file)
    if os.path.exists(zipname):
        with zipfile.ZipFile(zipname, "r") as zip_ref:
            zip_ref.extractall(outdir)
        os.remove(zipname)


def update_apps(service_acc_json=None, pull_new=False, interactive=False):
    """Update PlayMolecule applications.

    This function downloads/updates the PlayMolecule applications. It can use either
    a service account JSON file or default gcloud CLI credentials.

    Parameters
    ----------
    service_acc_json : str, optional
        Path to the Google Cloud service account JSON file. If not provided,
        will attempt to use default credentials (gcloud CLI).
    pull_new : bool, optional
        If True, also pull docker images that don't exist locally. Default is False
        (only update existing).
    interactive : bool, optional
        If True, show a list of all docker apps and allow user to select which ones
        to update. Default is False (update all existing or all if pull_new is True).
    """
    from playmolecule._config import _get_config
    from playmolecule._backends._docker import _update_docker_apps_from_gcloud

    config = _get_config()
    if config.registries is None:
        raise ValueError("PM_REGISTRIES environment variable is not set")

    docker_registry = [
        registry for registry in config.registries if registry.startswith("docker")
    ]
    if len(docker_registry) > 0:
        # Update docker images
        _update_docker_apps_from_gcloud(pull_new=pull_new, interactive=interactive)

    local_registry = [
        registry for registry in config.registries if registry.startswith("local:")
    ]
    if len(local_registry) > 0:
        # Update protocols
        _update_protocols(local_registry[0].replace("local:", ""), service_acc_json)
