import boto3
import botocore
import hashlib
import os
import shutil
import sys
import tarfile
from os.path import exists


def make_remote_key(artifact_name, branch_name):
    return f'servers/{branch_name}/{artifact_name}'


def s3_sha(key):
    s3_client = boto3.client('s3')

    try:
        response = s3_client.head_object(
            Bucket='eagle0',
            Key=key,
        )
    except botocore.exceptions.ClientError as e:
        if e.response['Error']['Code'] == '404':
            sys.exit(f"Key {key} not found.")
        else:
            raise

    return response['Metadata']['sha']


def s3_download(remote_key, local_path):
    s3_client = boto3.client('s3')

    s3_client.download_file(Bucket='eagle0', Key=remote_key, Filename=local_path)
    return


def sha256sum(path):
    sha256_hash = hashlib.sha256()
    with open(path, "rb") as f:
        # Read and update hash string value in blocks of 4K
        for byte_block in iter(lambda: f.read(4096), b""):
            sha256_hash.update(byte_block)
        return sha256_hash.hexdigest()

def artifact_dir(base_dir, branch_name):
    return os.path.join(base_dir, 'artifacts', branch_name)


def binary_dir(base_dir, branch_name):
    return os.path.join(base_dir, 'binary', branch_name)


def extract(artifact_name, base_dir, branch_name):
    art_dir = artifact_dir(base_dir, branch_name)
    artifact_path = os.path.join(art_dir, artifact_name)

    extracted_dir = binary_dir(base_dir, branch_name)
    print(f"extracting to {extracted_dir}")
    if exists(extracted_dir):
        print('removing existing file tree')
        shutil.rmtree(extracted_dir)

    os.makedirs(extracted_dir)

    with tarfile.open(artifact_path) as file:
        file.extractall(extracted_dir)

    return


def update(artifact_name, base_dir, branch_name):
    remote_key=make_remote_key(artifact_name=artifact_name, branch_name=branch_name)
    remote_sha = s3_sha(remote_key)
    print(f"Remote sha: {remote_sha}")

    dir_path = artifact_dir(base_dir, branch_name)
    artifact_path = os.path.join(dir_path, artifact_name)
    hash_path = os.path.join(dir_path, artifact_name + '_hash')

    if exists(hash_path) and exists(artifact_path):
        with open(hash_path, 'r') as sha_file:
            local_sha = sha_file.read()

        print(f"Local sha:  {local_sha}")

        if local_sha == remote_sha:
            print("sha matches! No download needed")
            needs_download = False
        else:
            print("shas do not match, needs download")
            needs_download = True
    else:
        print("No file present, downloading")
        needs_download = True

    if needs_download:
        if not exists(dir_path):
            os.makedirs(dir_path)
        s3_download(remote_key, artifact_path)
        new_sha = sha256sum(artifact_path)

        if new_sha != remote_sha:
            sys.exit('Downloaded file did not match remote sha')

        extract(artifact_name=artifact_name, base_dir=base_dir, branch_name=branch_name)

        with open(hash_path, 'w') as sha_file:
            sha_file.write(new_sha)

