<?php

namespace App\Helpers;

use App\Lib\Database;
use Nette\Utils\{Json, Validators, Arrays};

class BackupUtility
{
    private Database $db;
    private $dbHost = $_ENV['DB_HOST'];
    private $dbUser = $_ENV['DB_USER'];
    private $dbPass = $_ENV['DB_PASS'];
    private $dbName = $_ENV['DB_NAME'];
    private $backupPath = \APPROOT . '/Backup/';
    private $restorePath = \APPROOT . '/Backup/';
    private int $maxBackups = 10; // Maximum number of backups to keep
    private int $backupRetentionDays = 60; // number of days to keep backups;

    public function __construct(Database $db = null)
    {
        $this->db = $db ?? Database::connectDB();
    }

    private function backupName(string $naming = 'date'): string
    {
        if ($naming === 'date') {
            return 'database-backup-' . date('Y-m-d-H-i-s') . '.sql';
        }
    }

    public function setBackupPath(string $path): self
    {
        $this->backupPath = $path;
        return $this;
    }

    public function setRestorePath($restorePath): self
    {
        $this->restorePath = $restorePath;
        return $this;
    }

    public function backupDatabase(bool $gzip = true)
    {
        $backupFile = $this->backupPath . '/' . $this->dbName . '_' . date("Y-m-d_H-i-s") . '.sql';
        $command = "mysqldump --opt -h" . $this->dbHost . " -u" . $this->dbUser . " -p" . $this->dbPass . " " . $this->dbName . " > " . $backupFile;

        system($command);

        if ($gzip) {
            $gzipBackupFile = $backupFile . '.gz';
            $command = "gzip -c " . $backupFile . " > " . $gzipBackupFile;
            system($command);
        }
    }

    // Restore the database from the backup file
    public function restoreDatabase()
    {
        $command = "mysql -h" . $this->dbHost . " -u" . $this->dbUser . " -p" . $this->dbPass . " " . $this->dbName . " < " . $this->restorePath;
        system($command);
    }


    public function backupFiles($directory)
    {
        $zip = new \ZipArchive();
        $zipFilename = $this->backupPath . '/files_' . date("Y-m-d_H-i-s") . '.zip';

        if ($zip->open($zipFilename, \ZipArchive::CREATE) !== TRUE) {
            exit("Cannot open <$zipFilename>\n");
        }

        $files = new \RecursiveIteratorIterator(new \RecursiveDirectoryIterator($directory), \RecursiveIteratorIterator::LEAVES_ONLY);

        foreach ($files as $name => $file) {
            if (!$file->isDir()) {
                $filePath = $file->getRealPath();
                $relativePath = substr($filePath, strlen($directory) + 1);

                $zip->addFile($filePath, $relativePath);
            }
        }

        $zip->close();
    }


    // Create a full backup of the database and the directory
    public function fullBackup($directory, $gzip = true)
    {
        $this->backupDatabase($gzip);
        $this->backupFiles($directory);

        $zip = new \ZipArchive();
        $zipFilename = $this->backupPath . '/fullbackup_' . date("Y-m-d_H-i-s") . '.zip';

        if ($zip->open($zipFilename, \ZipArchive::CREATE) !== TRUE) {
            exit("Cannot open <$zipFilename>\n");
        }

        $files = new \RecursiveIteratorIterator(new \RecursiveDirectoryIterator($this->backupPath), \RecursiveIteratorIterator::LEAVES_ONLY);

        foreach ($files as $name => $file) {
            if (!$file->isDir()) {
                $filePath = $file->getRealPath();
                $relativePath = substr($filePath, strlen($this->backupPath) + 1);

                $zip->addFile($filePath, $relativePath);
            }
        }

        $zip->close();

        // Remove the oldest backup if the number of backups exceeds $maxBackups
        $backupFiles = glob($this->backupPath . '/fullbackup_*.zip');
        if (count($backupFiles) > $this->maxBackups) {
            usort($backupFiles, function ($a, $b) {
                return filemtime($a) - filemtime($b);
            });
            unlink($backupFiles[0]);
        }
    }

    // Remove old backups
    // $days = number of days to keep
    public function removeOldBackups($days = $this->backupRetentionDays)
    {
        $files = glob($this->backupPath . '/*');
        $now   = time();

        foreach ($files as $file) {
            if (is_file($file)) {
                if ($now - filemtime($file) >= 60 * 60 * 24 * $days) {
                    unlink($file);
                }
            }
        }
    }
}
