<?php

namespace App\Jobs;

use App\Repositories\BackupRepository;
use App\Services\DatabaseBackupDumper;
use Illuminate\Bus\Queueable;
use Illuminate\Contracts\Queue\ShouldQueue;
use Illuminate\Foundation\Bus\Dispatchable;
use Illuminate\Queue\InteractsWithQueue;
use Illuminate\Queue\SerializesModels;
use Illuminate\Support\Facades\Config;
use Symfony\Component\Process\Process;

class BackupDatabaseJob implements ShouldQueue
{
    use Dispatchable, InteractsWithQueue, Queueable, SerializesModels;

    protected int $backupId;

    public function __construct(int $backupId)
    {
        $this->backupId = $backupId;
    }

    public function handle(BackupRepository $repo, DatabaseBackupDumper $nativeDumper)
    {
        $backup = $repo->find($this->backupId);
        if (! $backup) {
            return;
        }

        $repo->update($backup->id, ['status' => 'running']);

        // Build mysqldump command from DB config
        $connection = Config::get('database.connections.'.Config::get('database.default'));

        $filename = $backup->filename;
        $path = storage_path('app/backups/'.$filename);

        try {
            if (! is_dir(dirname($path))) {
                mkdir(dirname($path), 0755, true);
            }

            if (str_contains($connection['driver'] ?? '', 'mysql')) {
                $user = $connection['username'] ?? '';
                $pass = $connection['password'] ?? '';
                $host = $connection['host'] ?? '127.0.0.1';
                $port = $connection['port'] ?? 3306;
                $database = $connection['database'] ?? '';

                $handle = fopen($path, 'wb');
                if ($handle === false) {
                    throw new \RuntimeException('Unable to create the backup file.');
                }

                $process = new Process([
                    config('database.mysqldump_path', 'mysqldump'),
                    '--host='.$host,
                    '--port='.(string) $port,
                    '--user='.$user,
                    '--single-transaction',
                    '--quick',
                    '--routines',
                    '--triggers',
                    $database,
                ], null, $pass !== '' ? ['MYSQL_PWD' => $pass] : null);
                $process->setTimeout(600);
                $process->run(function (string $type, string $buffer) use ($handle) {
                    if ($type === Process::OUT) {
                        fwrite($handle, $buffer);
                    }
                });
                fclose($handle);

                if (! $process->isSuccessful()) {
                    @unlink($path);
                    $nativeDumper->dump($path);
                }
            } else {
                throw new \RuntimeException('Database backup is only configured for MySQL/MariaDB.');
            }

            $size = filesize($path) ?: null;
            $repo->update($backup->id, ['status' => 'completed', 'size' => $size]);
        } catch (\Throwable $e) {
            $repo->update($backup->id, ['status' => 'failed', 'notes' => $e->getMessage()]);
        }
    }
}
