#!/usr/bin/python3

"""
UpdateSWP.py by Mason Nelson
===============================================================================
Script to update SkywarnPlus to the latest version. This script will download
the latest version of SkywarnPlus from GitHub, and then merge the existing
config.yaml with the new config.yaml. This script will also create a backup of the
existing SkywarnPlus directory before updating.

Please note that this script might not work correctly if you have made
significant changes to the SkywarnPlus code or directory structure.
If you have made significant changes, it is recommended that you manually update SkywarnPlus.

This file is part of SkywarnPlus.
SkywarnPlus is free software: you can redistribute it and/or modify it under the terms of
the GNU General Public License as published by the Free Software Foundation, either version 3
of the License, or (at your option) any later version. SkywarnPlus is distributed in the hope
that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details.
You should have received a copy of the GNU General Public License along with SkywarnPlus. If not, see <https://www.gnu.org/licenses/>.
"""

import os
import os.path
import zipfile
import shutil
import requests
import datetime
from ruamel.yaml import YAML
import argparse

# Set up command line arguments
parser = argparse.ArgumentParser(description="Update SkywarnPlus")
parser.add_argument(
    "-f",
    "--force",
    help="Force update without confirmation prompt",
    action="store_true",
)
args = parser.parse_args()


# Logging function
def log(message):
    print("[UPDATE]:", message)


# Function to load a yaml file
def load_yaml_file(filename):
    yaml = YAML()
    with open(filename, "r") as f:
        return yaml.load(f)


# Function to save a yaml file
def save_yaml_file(filename, data):
    yaml = YAML()
    yaml.preserve_quotes = True
    with open(filename, "w") as f:
        yaml.dump(data, f)


# Function to merge two yaml files
def merge_yaml_files(old_file, new_file):
    # Load the old and new yaml files
    old_yaml_data = load_yaml_file(old_file)
    new_yaml_data = load_yaml_file(new_file)

    # Merge the new yaml file with values from the old file
    for key in new_yaml_data:
        if key in old_yaml_data:
            if isinstance(new_yaml_data[key], dict) and isinstance(
                old_yaml_data[key], dict
            ):
                new_yaml_data[key].update(old_yaml_data[key])
            else:
                new_yaml_data[key] = old_yaml_data[key]

    # Save the merged yaml data back to the new file
    save_yaml_file(new_file, new_yaml_data)


def remove_duplicate_comments(filename):
    # Keep track of the last comment block
    last_comment_block = []
    new_lines = []

    # Read the file line by line
    with open(filename, "r") as f:
        lines = f.readlines()

    current_comment_block = []
    for line in lines:
        stripped_line = line.strip()

        # If line is a comment or blank, it's part of the current block
        if stripped_line.startswith("#") or not stripped_line:
            current_comment_block.append(line)
        else:
            if current_comment_block:
                if current_comment_block != last_comment_block:
                    new_lines.extend(current_comment_block)
                last_comment_block = list(current_comment_block)
                current_comment_block = []
            new_lines.append(line)

    # Check after finishing file
    if current_comment_block and current_comment_block != last_comment_block:
        new_lines.extend(current_comment_block)

    # Write the new lines back to the file
    with open(filename, "w") as f:
        f.writelines(new_lines)
        

# Display the initial warning
def display_update_warning():
    warning_message = """
    ============================================================
    WARNING: Please read the following information carefully before updating.

    This utility is designed to update SkywarnPlus to the latest version by fetching it 
    directly from GitHub. Before updating:
    
    - A backup of the existing SkywarnPlus directory will be created to ensure safety.
    
    - The updater will attempt to merge your existing config.yaml with the new version's 
      config.yaml. ALWAYS double-check your config.yaml after updating. This script is not 
      perfect and may not merge your configuration correctly.
    
    - If you've made significant changes to the SkywarnPlus code, directory structure, or 
      configuration, this updater might not work correctly. In such cases, manual updating 
      is recommended.
    
    Remember, this script's primary goal is to help with the updating process. However, 
    given the complexities of merging and updating, always verify the results yourself to 
      ensure your system continues to operate as expected.

    Proceed with caution.
    ============================================================
    """
    print(warning_message)


# Check for root privileges
if os.geteuid() != 0:
    exit("ERROR: This script must be run as root.")

# Make sure the script is in the right directory
if not os.path.isfile("SkywarnPlus.py"):
    print(
        "ERROR: Cannot find SkywarnPlus.py. Make sure this script is in the SkywarnPlus directory."
    )
    exit()

# Display the warning message
if not args.force:
    display_update_warning()
    
    confirmation = input("\nDo you want to continue with the update? (yes/no) ")
    if confirmation.lower() != "yes":
        log("Update cancelled by user.")
        exit()

# Zip the current directory
root_dir = os.getcwd()
log("Current directory is {}".format(root_dir))

# Full path to the archive
zip_name = root_dir + "_backup_" + datetime.datetime.now().strftime("%Y%m%d_%H%M")
log("Creating backup at {}.zip...".format(zip_name))

# Create the zip archive
shutil.make_archive(zip_name, "zip", root_dir)

# Download the new zip from GitHub
url = (
    "https://github.com/Mason10198/SkywarnPlus/releases/latest/download/SkywarnPlus.zip"
)
log("Downloading SkywarnPlus from {}...".format(url))
response = requests.get(url)

with open("/tmp/SkywarnPlus.zip", "wb") as out_file:
    out_file.write(response.content)

# Delete /tmp/SkywarnPlus if it already exists
if os.path.isdir("/tmp/SkywarnPlus"):
    log("Removing old /tmp/SkywarnPlus directory...")
    shutil.rmtree("/tmp/SkywarnPlus")

# Unzip the downloaded file
log("Extracting SkywarnPlus.zip...")
with zipfile.ZipFile("/tmp/SkywarnPlus.zip", "r") as zip_ref:
    zip_ref.extractall("/tmp")

# Merge the old config with the new config
log("Merging old config with new config...")
merge_yaml_files("config.yaml", "/tmp/SkywarnPlus/config.yaml")

# Remove duplicate comments from config.yaml
remove_duplicate_comments("/tmp/SkywarnPlus/config.yaml")

# Replace old directory with updated files
log("Merging updated files into {}...".format(root_dir))
for root, dirs, files in os.walk("/tmp/SkywarnPlus"):
    for file in files:
        old_file_path = os.path.join(root, file)
        relative_path = os.path.relpath(old_file_path, "/tmp/SkywarnPlus")
        new_file_path = os.path.join(root_dir, relative_path)

        os.makedirs(os.path.dirname(new_file_path), exist_ok=True)
        shutil.copy2(old_file_path, new_file_path)

# Set all .py files as executable
log("Setting .py files as executable...")
for dirpath, dirs, files in os.walk(root_dir):
    for filename in files:
        if filename.endswith(".py"):
            os.chmod(os.path.join(dirpath, filename), 0o755)  # chmod +x

# Delete temporary files and folders
log("Deleting temporary files and folders...")
shutil.rmtree("/tmp/SkywarnPlus")
os.remove("/tmp/SkywarnPlus.zip")

# Delete old TmpDir if it still exists
if os.path.isdir("/tmp/SkywarnPlus"):
    log("Removing old /tmp/SkywarnPlus directory...")
    shutil.rmtree("/tmp/SkywarnPlus")

log("Update complete!")
