<?php

declare(strict_types=1);

namespace App\Services;

use GuzzleHttp\Client;
use Illuminate\Database\Capsule\Manager as DB;

class BackupService
{
    private string $storagePath;
    private int $retentionHourly;
    private int $retentionDaily;
    private int $retentionWeekly;

    public function __construct()
    {
        $this->storagePath     = rtrim($_ENV['STORAGE_PATH'] ?? (dirname(__DIR__, 4) . '/storage'), '/');
        $this->retentionHourly = (int)($_ENV['BACKUP_RETENTION_HOURLY'] ?? 5);
        $this->retentionDaily  = (int)($_ENV['BACKUP_RETENTION_DAILY'] ?? 7);
        $this->retentionWeekly = (int)($_ENV['BACKUP_RETENTION_WEEKLY'] ?? 4);
    }

    public function creerBackup(): string
    {
        $dir      = $this->storagePath . '/backups';
        if (!is_dir($dir)) mkdir($dir, 0755, true);

        $filename = 'mykollufab_' . date('Y-m-d_H-i') . '.sql.gz';
        $filepath = $dir . '/' . $filename;

        $host = escapeshellarg($_ENV['DB_HOST'] ?? 'localhost');
        $user = escapeshellarg($_ENV['DB_USER'] ?? '');
        $pass = escapeshellarg($_ENV['DB_PASS'] ?? '');
        $db   = escapeshellarg($_ENV['DB_NAME'] ?? 'mykollufab');
        $out  = escapeshellarg($filepath);

        $cmd = "mysqldump -h {$host} -u {$user} -p{$pass} {$db} | gzip > {$out} 2>&1";
        exec($cmd, $output, $returnCode);

        if ($returnCode !== 0 || !file_exists($filepath)) {
            throw new \Exception('mysqldump échoué: ' . implode("\n", $output));
        }

        $this->purgerAnciens($dir);

        return $filepath;
    }

    private function purgerAnciens(string $dir): void
    {
        $files = glob($dir . '/mykollufab_*.sql.gz');
        if (!$files) return;

        usort($files, fn($a, $b) => filemtime($b) - filemtime($a));

        // Garder les N plus récents
        $toKeep = $this->retentionHourly;
        foreach (array_slice($files, $toKeep) as $old) {
            @unlink($old);
        }
    }

    public function uploadDropbox(string $filepath): bool
    {
        $config = DB::table('dropbox_config')->first();
        if (!$config || !$config->access_token) {
            throw new \Exception('Token Dropbox non configuré');
        }

        $token  = $this->decryptToken($config->access_token);
        $folder = rtrim($config->dossier ?? '/MKO-Backups/', '/');
        $dest   = $folder . '/' . basename($filepath);

        $http = new Client(['timeout' => 120]);

        try {
            $http->post('https://content.dropboxapi.com/2/files/upload', [
                'headers' => [
                    'Authorization'   => 'Bearer ' . $token,
                    'Dropbox-API-Arg' => json_encode([
                        'path'       => $dest,
                        'mode'       => 'overwrite',
                        'autorename' => false,
                    ]),
                    'Content-Type'    => 'application/octet-stream',
                ],
                'body' => fopen($filepath, 'r'),
            ]);

            DB::table('dropbox_config')->where('id', $config->id)->update([
                'dernier_backup'  => date('Y-m-d H:i:s'),
                'dernier_statut'  => 'ok',
                'dernier_message' => basename($filepath) . ' — ' . $this->formatSize(filesize($filepath)),
            ]);

            return true;
        } catch (\Exception $e) {
            DB::table('dropbox_config')->where('id', $config->id)->update([
                'dernier_statut'  => 'erreur',
                'dernier_message' => $e->getMessage(),
            ]);
            throw $e;
        }
    }

    public function testerConnexionDropbox(): bool
    {
        $config = DB::table('dropbox_config')->first();
        if (!$config || !$config->access_token) {
            throw new \Exception('Token Dropbox non configuré');
        }

        $token  = $this->decryptToken($config->access_token);
        $folder = rtrim($config->dossier ?? '/MKO-Backups/', '/');

        $http = new Client(['timeout' => 30]);
        $http->post('https://content.dropboxapi.com/2/files/upload', [
            'headers' => [
                'Authorization'   => 'Bearer ' . $token,
                'Dropbox-API-Arg' => json_encode([
                    'path'       => $folder . '/mko_test.txt',
                    'mode'       => 'overwrite',
                    'autorename' => false,
                ]),
                'Content-Type'    => 'application/octet-stream',
            ],
            'body' => 'MKO test',
        ]);

        return true;
    }

    public function sauvegarderTokenDropbox(string $token, string $dossier): void
    {
        $key       = hex2bin($_ENV['ENCRYPTION_KEY'] ?? str_repeat('0', 64));
        $iv        = openssl_random_pseudo_bytes(16);
        $encrypted = base64_encode($iv . openssl_encrypt($token, 'AES-256-CBC', $key, 0, $iv));

        DB::table('dropbox_config')->updateOrInsert(
            ['id' => 1],
            ['access_token' => $encrypted, 'dossier' => $dossier]
        );
    }

    private function decryptToken(string $encrypted): string
    {
        $key  = hex2bin($_ENV['ENCRYPTION_KEY'] ?? str_repeat('0', 64));
        $data = base64_decode($encrypted);
        $iv   = substr($data, 0, 16);
        return openssl_decrypt(substr($data, 16), 'AES-256-CBC', $key, 0, $iv);
    }

    private function formatSize(int $bytes): string
    {
        if ($bytes >= 1048576) return round($bytes / 1048576, 1) . ' MB';
        if ($bytes >= 1024) return round($bytes / 1024, 1) . ' KB';
        return $bytes . ' B';
    }

    public function getDropboxConfig(): array
    {
        $config = DB::table('dropbox_config')->first();
        if (!$config) return ['token_configuré' => false];

        return [
            'token_configuré' => !empty($config->access_token),
            'dossier'         => $config->dossier ?? '/MKO-Backups/',
            'dernier_backup'  => $config->dernier_backup,
            'dernier_statut'  => $config->dernier_statut,
            'dernier_message' => $config->dernier_message,
        ];
    }
}
