import subprocess
import sys
import os
import platform
import hashlib
import click
import shutil
import toml
import hashlib
import urllib.request
from dataclasses import dataclass

PACKAGER_TARGET_DIR = "target/packager"

@dataclass
class PackageInfo:
    name: str
    version: str
    description: str
    publisher: str
    url: str

@click.group()
def cli():
    pass

@cli.command()
@click.option('--skipcargo', default=False, is_flag=True, help="Skip cargo release build")
def build(skipcargo):
    """Build espanso distribution"""
    # Check operating system
    TARGET_OS = "macos"
    if platform.system() == "Windows":
        TARGET_OS = "windows"
    elif platform.system() == "Linux":
        TARGET_OS = "linux"

    print("Detected OS:", TARGET_OS)

    print("Loading info from Cargo.toml")
    cargo_info = toml.load("Cargo.toml")
    package_info = PackageInfo(cargo_info["package"]["name"],
                               cargo_info["package"]["version"],
                               cargo_info["package"]["description"],
                               cargo_info["package"]["authors"][0],
                               cargo_info["package"]["homepage"])
    print(package_info)

    if not skipcargo:
        print("Building release version...")
        subprocess.run(["cargo", "build", "--release"])
    else:
        print("Skipping build")

    if TARGET_OS == "windows":
        build_windows(package_info)
    elif TARGET_OS == "macos":
        build_mac(package_info)


def build_windows(package_info):
    print("Starting packaging process for Windows...")

    # Check Inno Setup
    try:
        subprocess.run(["iscc"], stdout=subprocess.DEVNULL, stderr=subprocess.DEVNULL)
    except FileNotFoundError:
        raise Exception("Could not find Inno Setup compiler. Please install it from here: http://www.jrsoftware.org/isdl.php")

    print("Clearing target dirs")

    # Clearing previous build directory
    if os.path.isdir(PACKAGER_TARGET_DIR):
        print("Cleaning packager temp directory...")
        shutil.rmtree(PACKAGER_TARGET_DIR)

    TARGET_DIR = os.path.join(PACKAGER_TARGET_DIR, "win")
    os.makedirs(TARGET_DIR, exist_ok=True)

    print("Downloading Visual C++ redistributable")
    vc_redist_file = os.path.join(TARGET_DIR, "vc_redist.x64.exe")
    urllib.request.urlretrieve("https://aka.ms/vs/16/release/vc_redist.x64.exe", vc_redist_file)

    INSTALLER_NAME = f"espanso-win-installer"

    # Inno setup
    shutil.copy("packager/win/modpath.iss", os.path.join(TARGET_DIR, "modpath.iss"))

    print("Processing inno setup template")
    with open("packager/win/setupscript.iss", "r") as iss_script:
        content = iss_script.read()

        # Replace variables
        content = content.replace("{{{app_name}}}", package_info.name)
        content = content.replace("{{{app_version}}}", package_info.version)
        content = content.replace("{{{app_publisher}}}", package_info.publisher)
        content = content.replace("{{{app_url}}}", package_info.url)
        content = content.replace("{{{app_license}}}",  os.path.abspath("LICENSE"))
        content = content.replace("{{{app_icon}}}",  os.path.abspath("packager/win/icon.ico"))
        content = content.replace("{{{executable_path}}}",  os.path.abspath("target/release/espanso.exe"))
        content = content.replace("{{{output_dir}}}",  os.path.abspath(TARGET_DIR))
        content = content.replace("{{{output_name}}}",  INSTALLER_NAME)

        with open(os.path.join(TARGET_DIR, "setupscript.iss"), "w") as output_script:
            output_script.write(content)

    print("Compiling installer with Inno setup")
    subprocess.run(["iscc", os.path.abspath(os.path.join(TARGET_DIR, "setupscript.iss"))])

    print("Calculating the SHA256")
    sha256_hash = hashlib.sha256()
    with open(os.path.abspath(os.path.join(TARGET_DIR, INSTALLER_NAME+".exe")),"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)

        hash_file = os.path.abspath(os.path.join(TARGET_DIR, "espanso-win-installer-sha256.txt"))
        with open(hash_file, "w") as hf:
            hf.write(sha256_hash.hexdigest())


def build_mac(package_info):
    print("Starting packaging process for MacOS...")

    print("Clearing target dirs")

    # Clearing previous build directory
    if os.path.isdir(PACKAGER_TARGET_DIR):
        print("Cleaning packager temp directory...")
        shutil.rmtree(PACKAGER_TARGET_DIR)

    TARGET_DIR = os.path.join(PACKAGER_TARGET_DIR, "mac")
    os.makedirs(TARGET_DIR, exist_ok=True)

    print("Compressing release to archive...")
    target_name = f"espanso-mac.tar.gz"
    archive_target = os.path.abspath(os.path.join(TARGET_DIR, target_name))
    subprocess.run(["tar",
                    "-C", os.path.abspath("target/release"),
                    "-cvf",
                    archive_target,
                    "espanso",
                    ])
    print(f"Created archive: {archive_target}")

    print("Calculating the SHA256")
    sha256_hash = hashlib.sha256()
    with open(archive_target,"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)

        hash_file = os.path.abspath(os.path.join(TARGET_DIR, "espanso-mac-sha256.txt"))
        with open(hash_file, "w") as hf:
            hf.write(sha256_hash.hexdigest())

    print("Processing Homebrew formula template")
    with open("packager/mac/espanso.rb", "r") as formula_template:
        content = formula_template.read()

        # Replace variables
        content = content.replace("{{{app_desc}}}", package_info.description)
        content = content.replace("{{{app_url}}}", package_info.url)
        content = content.replace("{{{app_version}}}", package_info.version)

        # Calculate hash
        with open(archive_target, "rb") as f:
            bytes = f.read()
            readable_hash = hashlib.sha256(bytes).hexdigest()
            content = content.replace("{{{release_hash}}}", readable_hash)

        with open(os.path.join(TARGET_DIR, "espanso.rb"), "w") as output_script:
            output_script.write(content)

    print("Done!")

if __name__ == '__main__':
    print("[[ espanso packager ]]")

    # Check python version 3
    if sys.version_info[0] < 3:
        raise Exception("Must be using Python 3")

    cli()