#!/usr/bin/env python3

import yaml
import sys
import os
import shutil
import logging
from typing import Dict, Any

def merge_yaml(input_file: str, overlay_file: str) -> Dict[Any, Any]:
    """
    Merges a YAML overlay onto a base YAML structure.  Handles nested dictionaries and lists.

    Args:
        input_file: Path to the base YAML file.
        overlay_file: Path to the YAML file to overlay.

    Returns:
        dict: The merged YAML data, or an empty dict on error.
    """
    try:
        logging.info(f"Reading base file: {input_file}")
        with open(input_file, 'r') as infile:
            content = infile.read()
            logging.debug(f"Base file content:\n{content}")
            try:
                base_data = yaml.safe_load(content)
                logging.info(f"Base data type: {type(base_data)}")
            except yaml.YAMLError as e:
                logging.error(f"Error loading base YAML file '{input_file}': {e}")
                return {}

        logging.info(f"Reading overlay file: {overlay_file}")
        with open(overlay_file, 'r') as ovfile:
            content = ovfile.read()
            logging.debug(f"Overlay file content:\n{content}")
            try:
                overlay_data = yaml.safe_load(content)
                logging.info(f"Overlay data type: {type(overlay_data)}")
            except yaml.YAMLError as e:
                logging.error(f"Error loading overlay YAML file '{overlay_file}': {e}")
                return {}
    except FileNotFoundError as e:
        logging.error(f"Error: {e}")
        return {}

    if not isinstance(base_data, dict) or not isinstance(overlay_data, dict):
        logging.error(f"Error: Both input and overlay YAML files must represent dictionaries.")
        logging.error(f"Base data type: {type(base_data)}")
        logging.error(f"Overlay data type: {type(overlay_data)}")
        return {}

    def _merge(base: Dict[Any, Any], overlay: Dict[Any, Any]) -> Dict[Any, Any]:
        """
        Recursively merges two dictionaries in a generic way.
        For dictionaries: recursively merge
        For lists: replace with overlay value
        For all other types: replace with overlay value
        """
        merged = base.copy()
        for key, value in overlay.items():
            if key in merged and isinstance(merged[key], dict) and isinstance(value, dict):
                merged[key] = _merge(merged[key], value)
            else:
                # For lists and all other types, replace with overlay value
                merged[key] = value
        return merged

    merged_data = _merge(base_data, overlay_data)
    return merged_data

def process_yaml_files(input_file: str, overlay_file: str, output_file: str) -> bool:
    """
    Merges two YAML files, creates a backup, and writes the merged result to a new file.

    Args:
        input_file: Path to the base YAML file.
        overlay_file: Path to the YAML file to overlay.
        output_file: Path to the desired output YAML file.

    Returns:
        bool: True on success, False on error.
    """
    # Create a backup of the input file
    backup_file = input_file + ".bk"
    try:
        shutil.copy2(input_file, backup_file)  # copy2 preserves metadata
        logging.info(f"Created backup of input file: {backup_file}")
    except OSError as e:
        logging.error(f"Error creating backup of '{input_file}': {e}")
        return False

    # Perform the YAML merge
    merged_data = merge_yaml(input_file, overlay_file)
    if not merged_data:
        logging.error("YAML merge failed.")
        return False

    # Write the merged YAML to the output file
    try:
        with open(output_file, 'w') as outfile:
            yaml.dump(merged_data, outfile, indent=2, sort_keys=False)
        logging.info(f"Successfully wrote merged YAML to '{output_file}'")
        return True
    except IOError as e:
        logging.error(f"Error writing to output file '{output_file}': {e}")
        return False
    except yaml.YAMLError as e:
        logging.error(f"Error dumping YAML: {e}")
        return False

if __name__ == "__main__":
    import sys

    # Set up basic logging
    logging.basicConfig(level=logging.INFO, format='%(asctime)s - %(levelname)s - %(message)s')

    if len(sys.argv) != 4:
        print("Usage: python merge_yaml.py <input_file> <overlay_file> <output_file>")
        sys.exit(1)

    input_file = sys.argv[1]
    overlay_file = sys.argv[2]
    output_file = sys.argv[3]

    if process_yaml_files(input_file, overlay_file, output_file):
        logging.info("YAML merge process completed successfully.")
    else:
        logging.error("YAML merge process failed.")
        sys.exit(1)