<?php

namespace App\Console\Commands;

use Carbon\Carbon;
use Exception;
use Illuminate\Support\Collection;
use Illuminate\Support\Facades\Config;
use Illuminate\Support\Facades\Storage;
use Mtc\MercuryDataModels\Tenant;
use Spatie\Backup\Events\BackupHasFailed;
use App\Backup\BackupJobFactory;

class BackupCommand extends \Spatie\Backup\Commands\BackupCommand
{
    public function handle(): int
    {
        $this->info('Start time: ' . Carbon::now()->format('Y-m-d'));
        $this->removeOldBackupRecords();
        $this->addTenancyDatabaseConnections();

        consoleOutput()->comment('Starting backup...');

        $disableNotifications = $this->option('disable-notifications');

        if ($this->option('timeout') && is_numeric($this->option('timeout'))) {
            set_time_limit((int)$this->option('timeout'));
        }

        try {
            $this->guardAgainstInvalidOptions();

            $backupJob = BackupJobFactory::createFromArray(config('backup'));

            if ($this->option('only-db')) {
                $backupJob->dontBackupFilesystem();
            }
            if ($this->option('db-name')) {
                $backupJob->onlyDbName($this->option('db-name'));
            }

            if ($this->option('only-files')) {
                $backupJob->dontBackupDatabases();
            }

            if ($this->option('only-to-disk')) {
                $backupJob->onlyBackupTo($this->option('only-to-disk'));
            }

            if ($this->option('filename')) {
                $backupJob->setFilename($this->option('filename'));
            }

            if ($disableNotifications) {
                $backupJob->disableNotifications();
            }

            if (!$this->getSubscribedSignals()) {
                $backupJob->disableSignals();
            }

            $backupJob->run();

            consoleOutput()->comment('Backup completed!');
        } catch (Exception $exception) {
            consoleOutput()->error("Backup failed because: {$exception->getMessage()}.");

            report($exception);

            if (!$disableNotifications) {
                event(new BackupHasFailed($exception));
            }

            return 1;
        }
        return 0;
    }

    /**
     * Remove expired backup records
     *
     * @return void
     */
    private function removeOldBackupRecords()
    {
        $this->info("Checking outdated backups");
        collect(Storage::disk('file-storage')->files(config('backup.backup.name')))
            ->tap(fn(Collection $files) => $this->info($files->count() . ' backup files total'))
            ->filter(fn($path) => Carbon::now()->subMonth()
                ->greaterThan(Carbon::createFromTimestamp(Storage::disk('file-storage')->lastModified($path))))
            ->tap(fn(Collection $files) => $this->info($files->count() . ' backup files to remove'))
            ->tap(fn(Collection $files) => Storage::disk('file-storage')->delete($files->toArray()));
    }

    /**
     * Adjust DB connections to have tenancy databases as connections
     *
     * @return void
     */
    private function addTenancyDatabaseConnections()
    {
        $tenants = Tenant::all();
        $mysql = config('database.connections.mysql');

        // mysql database
        $mysql['database'] = 'mysql';
        Config::set("database.connections.master", $mysql);

        // Tenants
        $tenants->each(function (Tenant $tenant) use ($mysql) {
            $mysql['database'] = $tenant->tenancy_db_name;
            Config::set("database.connections.$tenant->id", $mysql);
        });

        $toBackup = $tenants->pluck('id')->prepend('mysql')->prepend('master')->toArray();
        Config::set('backup.backup.source.databases', $toBackup);
    }
}
