"""
Module: rollback.py
Description: Provides functionality for reverting failed changes within the Genesis system.
Handles rollback operations, logging, and error management.
"""

import logging
import os
import shutil
import time
from typing import List, Optional

# Configure logging
LOG_DIR = "/mnt/e/genesis-system/logs"  # Define the log directory
if not os.path.exists(LOG_DIR):
    os.makedirs(LOG_DIR)

LOG_FILE = os.path.join(LOG_DIR, "rollback.log")
logging.basicConfig(filename=LOG_FILE, level=logging.INFO,
                    format='%(asctime)s - %(levelname)s - %(message)s')

class RollbackError(Exception):
    """
    Custom exception class for rollback-related errors.
    """
    pass


def create_backup(source_path: str, backup_path: str) -> bool:
    """
    Creates a backup of a file or directory.

    Args:
        source_path: The path to the file or directory to back up.
        backup_path: The path where the backup will be created.

    Returns:
        True if the backup was created successfully, False otherwise.

    Raises:
        RollbackError: If there are issues creating the backup.
    """
    try:
        if os.path.exists(source_path):
            if os.path.isdir(source_path):
                shutil.copytree(source_path, backup_path)
            else:
                shutil.copy2(source_path, backup_path)  # Preserves metadata
            logging.info(f"Backup created: {source_path} -> {backup_path}")
            return True
        else:
            logging.warning(f"Source path does not exist: {source_path}")
            return False
    except Exception as e:
        error_message = f"Error creating backup of {source_path} to {backup_path}: {e}"
        logging.error(error_message)
        raise RollbackError(error_message)


def restore_backup(backup_path: str, restore_path: str) -> bool:
    """
    Restores a file or directory from a backup.  If restore_path exists, it will be overwritten.

    Args:
        backup_path: The path to the backup file or directory.
        restore_path: The path where the backup will be restored.

    Returns:
        True if the restore was successful, False otherwise.

    Raises:
        RollbackError: If there are issues restoring the backup.
    """
    try:
        if os.path.exists(backup_path):
            # Handle existing files/directories at restore_path
            if os.path.exists(restore_path):
                if os.path.isdir(restore_path):
                    shutil.rmtree(restore_path)
                else:
                    os.remove(restore_path)

            if os.path.isdir(backup_path):
                shutil.copytree(backup_path, restore_path)
            else:
                shutil.copy2(backup_path, restore_path)  # Preserves metadata
            logging.info(f"Backup restored: {backup_path} -> {restore_path}")
            return True
        else:
            logging.error(f"Backup path does not exist: {backup_path}")
            raise RollbackError(f"Backup path does not exist: {backup_path}")
    except Exception as e:
        error_message = f"Error restoring backup of {backup_path} to {restore_path}: {e}"
        logging.error(error_message)
        raise RollbackError(error_message)


def perform_rollback(backup_paths: List[tuple[str, str]]) -> bool:
    """
    Performs a rollback operation, restoring files or directories from backups.

    Args:
        backup_paths: A list of tuples, where each tuple contains the backup path and restore path.
                      e.g., [("/path/to/backup1", "/path/to/restore1"), ("/path/to/backup2", "/path/to/restore2")]

    Returns:
        True if the rollback was successful, False otherwise.

    Raises:
        RollbackError: If any of the restore operations fail.
    """
    try:
        for backup_path, restore_path in backup_paths:
            restore_backup(backup_path, restore_path)
        logging.info("Rollback completed successfully.")
        return True
    except RollbackError as e:
        logging.error(f"Rollback failed: {e}")
        return False
    except Exception as e:
        error_message = f"Unexpected error during rollback: {e}"
        logging.error(error_message)
        return False


def delete_backup(backup_path: str) -> bool:
    """
    Deletes a backup file or directory.

    Args:
        backup_path: The path to the backup to delete.

    Returns:
        True if the deletion was successful, False otherwise.
    """
    try:
        if os.path.exists(backup_path):
            if os.path.isdir(backup_path):
                shutil.rmtree(backup_path)
            else:
                os.remove(backup_path)
            logging.info(f"Backup deleted: {backup_path}")
            return True
        else:
            logging.warning(f"Backup path does not exist: {backup_path}")
            return False
    except Exception as e:
        logging.error(f"Error deleting backup {backup_path}: {e}")
        return False

if __name__ == '__main__':
    # Example usage
    EXAMPLE_DIR = "/mnt/e/genesis-system/example_dir"
    EXAMPLE_FILE = "/mnt/e/genesis-system/example_file.txt"

    if not os.path.exists(EXAMPLE_DIR):
        os.makedirs(EXAMPLE_DIR)
    if not os.path.exists(EXAMPLE_FILE):
        with open(EXAMPLE_FILE, "w") as f:
            f.write("This is an example file.")


    backup_dir = "/mnt/e/genesis-system/backup_dir"
    backup_file = "/mnt/e/genesis-system/backup_file.txt"

    try:
        # Create backups
        create_backup(EXAMPLE_DIR, backup_dir)
        create_backup(EXAMPLE_FILE, backup_file)

        # Simulate a failed operation (e.g., corrupt the original file)
        with open(EXAMPLE_FILE, "w") as f:
            f.write("Corrupted data!")

        # Perform rollback
        rollback_paths = [(backup_dir, EXAMPLE_DIR), (backup_file, EXAMPLE_FILE)]
        if perform_rollback(rollback_paths):
            print("Rollback successful!")
        else:
            print("Rollback failed!")

        # Clean up backups (optional)
        delete_backup(backup_dir)
        delete_backup(backup_file)


    except RollbackError as e:
        print(f"An error occurred: {e}")
    except Exception as e:
        print(f"An unexpected error occurred: {e}")