import os
import tarfile
from pathlib import Path

from src.compressor import CompressionLevel, Compressor
from src.encryptor import Encryptor


class BackupManager:
    """
    A class to manage the process of creating backups by compressing files and performing backup restoration.

    Attributes:
        source (Path): The directory to back up.
        backup_file (Path): The destination file where the backup will be stored.
        compressor (Compressor): The compressor instance used for file compression.
        encryption_manager (Encryptor): The encryption manager used for file encryption (if specified).
    """

    def __init__(self, source, destination, level=CompressionLevel.normal, parallel=False, encryption_key_path=None):
        """
        Initializes the BackupManager with the specified parameters.

        Args:
            source (str or Path): The directory to back up.
            destination (str or Path): The destination file for the backup.
            level (CompressionLevel): The compression level (default is normal).
            parallel (bool): Whether to use multithreaded compression (default is False).
            encryption_key_path (str or None): Path to the encryption key file (default is None).
        """
        self.source = Path(source)
        self.backup_file = Path(destination)

        os.makedirs(self.source, exist_ok=True)
        if not self.backup_file.exists():
            self.backup_file.open('w').close()

        self.encryption_key_path = encryption_key_path
        self.encryption_manager = Encryptor(encryption_key_path)
        self.compressor = Compressor(level=level, parallel=parallel)

    def create_backup(self):
        """
        Creates a backup by compressing the files in the source directory and storing them in a tar.zst archive.
        Optionally encrypts the archive file.
        """
        # Check if the source directory is empty
        if not any(self.source.iterdir()):
            raise FileNotFoundError("Error: The source directory is empty. Backup aborted.")

        # Temporary tar file
        temp_tar_file = self.backup_file.with_suffix(".tar")

        with tarfile.open(temp_tar_file, "w") as tar:
            tar.add(self.source)

        # Compress the tar file with Zstandard
        self.compressor.compress_file(temp_tar_file, self.backup_file)

        # Encrypt the compressed backup file
        self.encryption_manager.encrypt_file(self.backup_file)

        # Remove temporary tar file
        os.remove(temp_tar_file)

    def restore_backup(self, restore_dir):
        """
        Restores the backup by extracting the files from the archive and recreating the original structure.
        Decrypts the backup file if it is encrypted.

        Args:
            restore_dir (str or Path): The directory where files should be restored.
        """
        restore_dir = Path(restore_dir)
        os.makedirs(restore_dir, exist_ok=True)

        # Decrypt the backup file
        self.encryption_manager.decrypt_file(self.backup_file)

        # Decompress the backup file
        temp_tar_file = restore_dir / "temp.tar"
        self.compressor.decompress_file(self.backup_file, temp_tar_file)

        # Extract the tar file
        with tarfile.open(temp_tar_file, 'r') as tar:
            tar.extractall(path=restore_dir)

        # Remove temporary tar file
        os.remove(temp_tar_file)
