import requests
import argparse
import zipfile
import json
import os
import hashlib


def compute_sha256(file_path):
    # Compute SHA256 hash for a given file
    sha256_hash = hashlib.sha256()
    try:
        with open(file_path, 'rb') as f:
            for byte_block in iter(lambda: f.read(4096), b""):
                sha256_hash.update(byte_block)
        return sha256_hash.hexdigest()
    except FileNotFoundError:
        print(f"File not found: {file_path}")
        return None


def compute_file_hash_with_extension(base_file_name, extensions):
    # Compute SHA256 hash for a file with various extensions
    for ext in extensions:
        full_path = f"{base_file_name}{ext}"
        calculated_hash = compute_sha256(full_path)
        if calculated_hash:
            print(f"SHA256 computed for {full_path}: {calculated_hash}")
            return full_path, calculated_hash
    return None, None


def compare_file_hash(expected_hash, base_file_name):
    # Compare the computed file hash with the expected hash
    print(f"Comparing SHA256 hashes for base file: {base_file_name}")
    extensions = ['.dll', '.so', '.dylib']
    full_path, calculated_hash = compute_file_hash_with_extension(base_file_name, extensions)

    if calculated_hash:
        if calculated_hash.lower() == expected_hash.lower():
            print(f"SHA256 checksum for {full_path} is valid.")
            return True
        else:
            print(f"Error: SHA256 checksum for {full_path} is invalid. Expected {expected_hash}, got {calculated_hash}")
    else:
        print(f"No valid file found with the correct SHA256 checksum for any expected extensions.")

    exit(1)


def write_summery_file_content(message):
    # Write error message to GitHub summary file
    try:
        summery_file = os.environ.get("GITHUB_STEP_SUMMARY")
        with open(summery_file, "a") as file:
            file.write(f":red_circle: Error: {message}")
    except Exception as e:
        print(f"An error occurred: {e}")


def unzip_file(zip_file, extract_to):
    # Extract the contents of a zip file
    with zipfile.ZipFile(zip_file, 'r') as zip_ref:
        zip_ref.extractall(extract_to)


def compare_checksums(required_checksum, incoming_checksum_file, file_name):
    # Compare the required checksum with the incoming checksum
    try:
        with open(incoming_checksum_file, 'r') as file:
            file_content = file.read().replace(" ", "").replace("\n", "")
    except FileNotFoundError:
        print(f"File '{file_path}' not found.")
        exit(1)

    if required_checksum == file_content:
        print(f"{file_name} File checksum match")
    else:
        write_summery_file_content(f"{file_name} File checksum does not match.")
        print(f"Error: {file_name} File checksum does not match.")
        exit(1)


def get_download_urls(nexus_url, params, username, password):
    # Retrieve download URLs from Nexus repository
    response_data = requests.get(nexus_url, params=params, auth=(username, password))
    download_urls = []
    if response_data.status_code == 200:
        continuationToken = "start"
        while continuationToken:
            continuationToken = response_data.json().get("continuationToken")
            params.update({"continuationToken": continuationToken})
            download_urls += [item.get("downloadUrl") for item in response_data.json().get("items", [])]
            response_data = requests.get(nexus_url, params=params, auth=(username, password))
            if response_data.status_code != 200:
                print(f"Error: {response.status_code} - {response.reason}")
                exit(1)
    else:
        write_summery_file_content(f"{response_data.status_code} - {response_data.reason}")
        print(f"Error: {response_data.status_code} - {response_data.reason}")
        exit(1)

    return download_urls


def download_files(download_urls, username, password, search_pattern, download_path, hash, is_zip_file, skip_checksum):
    # Download files from the list of URLs and verify checksums
    print(download_urls)
    if not download_urls:
        print("No files found to download.")
    for url in download_urls:
        if search_pattern:
            parts = url.split(search_pattern)
            file_download_dir = parts[1]
        else:
            file_download_dir = "agent-sign/firefly"[1]
        file_name = file_download_dir.split('/')[-1]
        directory_parts = file_download_dir.split('/')
        directory_to_create = '/'.join(directory_parts[:len(directory_parts) - 1])
        os.makedirs(f'{download_path}{directory_to_create}', exist_ok=True)
        download_request = requests.get(url, auth=(username, password))
        if download_request.status_code == 200:
            if is_zip_file:
                with open(f'{download_path}{file_download_dir}', 'wb') as f:
                    f.write(download_request.content)
                unzip_file(f'{download_path}{file_download_dir}', f'{download_path}/{directory_to_create}')
                os.remove(f'{download_path}{file_download_dir}')
            else:
                with open(f'{os.getcwd()}/{download_path}{directory_to_create}/{file_name}', 'wb') as f:
                    f.write(download_request.content)
            if not skip_checksum:
                compare_checksums(hash, f'{download_path}/{directory_to_create}/checksum_SHA256.txt',
                                  directory_to_create)
                compare_file_hash(hash, f'{download_path}/{directory_to_create}/firefly')
            print(f'{download_path}{directory_to_create} File downloaded and checked successfully')
        else:
            write_summery_file_content(f"Failed to download file: {download_request.status_code}")
            print(f"Failed to download file: {download_request.status_code}")
            exit(1)


if __name__ == "__main__":
    # Parse command-line arguments
    parser = argparse.ArgumentParser(description="Get input parameters")
    parser.add_argument('--nexus_url', type=str, nargs='?',
                        help='Nexus repository DNS name (Default: nexus.perimeter81.com)',
                        default='nexus.perimeter81.com')
    parser.add_argument('--nexus_root_dir', type=str, nargs='?', help='Nexus repository root path (Default: firefly)',
                        default='firefly')
    parser.add_argument('--nexus_repo_name', type=str, nargs='?', help='Nexus repository name (Default: agent-sign)',
                        default='agent-sign')
    parser.add_argument('--download_directory', type=str, help='Destination directory path', required=True)
    parser.add_argument('--json_file', type=str, help='Firefly version json file path', required=True)
    parser.add_argument('--is_zip_file', type=str, nargs='?', help='Is the file a zip file')
    parser.add_argument('--skip_checksum', type=str, nargs='?', help='If to skip the checksum')

    args = parser.parse_args()

    username = os.getenv("NEXUS_USERNAME").encode("utf-8")
    password = os.getenv("NEXUS_PASSWORD").encode("utf-8")
    nexus_search_url = f"https://{args.nexus_url}/service/rest/v1/search/assets"
    download_path = args.download_directory
    json_file_path = args.json_file.replace("\\", "/")

    if args.skip_checksum == 'false' or not args.skip_checksum:
        skip_checksum = False
    else:
        skip_checksum = True
    if not username or not password:
        write_summery_file_content("NEXUS_PASSWORD or NEXUS_USERNAME environment variables are required")
        raise ValueError("NEXUS_PASSWORD or NEXUS_USERNAME environment variables are required")

    with open(json_file_path, "r") as json_file:
        json_data = json.load(json_file)

    versionPath = json_data["versionPath"]

    for platform in json_data["platforms"]:
        os_name = platform["os"]
        name = platform["name"]
        hash = platform["hash"]
        try:
            is_zip_file = platform["isZipFile"]
            if is_zip_file == "false" or not is_zip_file:
                is_zip_file = False
        except:
            is_zip_file = True
        group = f"/{args.nexus_root_dir}{'/' + versionPath if versionPath else ''}{'/' + os_name if os_name else ''}" + (
            f"/{name}" if name else "")
        params = {
            "repository": args.nexus_repo_name,
            "format": "raw",
            "group": group
        }
        download_urls = get_download_urls(nexus_search_url, params, username, password)

        download_files(download_urls, username, password, versionPath, download_path, hash, is_zip_file, skip_checksum)

