<?php

namespace App\Services;

use DateTimeImmutable;

class BackupMetricsService
{
    private string $bankDir = '/opt/camaraweb/backups/bancos';
    private string $filesDir = '/opt/camaraweb/backups/arquivos';
    private string $logsDir = '/opt/camaraweb/backups/logs';

    public function forDatabase(?string $database): array
    {
        $base = [
            'status' => 'nao_encontrado',
            'database' => $database,
            'file' => null,
            'path' => null,
            'size' => 0,
            'size_human' => '—',
            'date' => null,
            'date_human' => '—',
            'age_hours' => null,
            'recent' => false,
        ];

        if (!$database || !is_dir($this->bankDir)) {
            return $base;
        }

        // Nome de banco vindo do discovery, mas ainda restringimos
        // os caracteres permitidos antes de procurar no filesystem.
        if (!preg_match('/^[A-Za-z0-9_]+$/', $database)) {
            return $base;
        }

        $files = glob(
            $this->bankDir . '/' . $database . '_*.sql.gz'
        ) ?: [];

        $files = array_values(array_filter(
            $files,
            fn ($file) => is_file($file) && filesize($file) > 0
        ));

        if (!$files) {
            return $base;
        }

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

        $file = $files[0];
        $mtime = filemtime($file);
        $size = filesize($file);

        $ageHours = $mtime
            ? round((time() - $mtime) / 3600, 1)
            : null;

        // Como o backup é diário às 03:00, damos uma margem
        // operacional de 30 horas.
        $recent = $ageHours !== null && $ageHours <= 30;

        return [
            'status' => $recent ? 'ok' : 'atrasado',
            'database' => $database,
            'file' => basename($file),
            'path' => $file,
            'size' => $size,
            'size_human' => $this->humanBytes($size),
            'date' => $mtime,
            'date_human' => $mtime
                ? date('d/m/Y H:i', $mtime)
                : '—',
            'age_hours' => $ageHours,
            'recent' => $recent,
        ];
    }

    public function latestFilesBackup(): array
    {
        $base = [
            'status' => 'nao_encontrado',
            'file' => null,
            'path' => null,
            'size' => 0,
            'size_human' => '—',
            'date' => null,
            'date_human' => '—',
            'age_hours' => null,
            'recent' => false,
        ];

        if (!is_dir($this->filesDir)) {
            return $base;
        }

        $files = glob($this->filesDir . '/www_*.tar.gz') ?: [];

        $files = array_values(array_filter(
            $files,
            fn ($file) => is_file($file) && filesize($file) > 0
        ));

        if (!$files) {
            return $base;
        }

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

        $file = $files[0];
        $mtime = filemtime($file);
        $size = filesize($file);

        $ageHours = $mtime
            ? round((time() - $mtime) / 3600, 1)
            : null;

        $recent = $ageHours !== null && $ageHours <= 30;

        return [
            'status' => $recent ? 'ok' : 'atrasado',
            'file' => basename($file),
            'path' => $file,
            'size' => $size,
            'size_human' => $this->humanBytes($size),
            'date' => $mtime,
            'date_human' => $mtime
                ? date('d/m/Y H:i', $mtime)
                : '—',
            'age_hours' => $ageHours,
            'recent' => $recent,
        ];
    }

    public function latestRun(): array
    {
        $base = [
            'status' => 'desconhecido',
            'file' => null,
            'date_human' => '—',
            'duration_seconds' => null,
            'duration_human' => '—',
            'remote_confirmed' => false,
        ];

        if (!is_dir($this->logsDir)) {
            return $base;
        }

        $logs = glob($this->logsDir . '/backup_*.log') ?: [];

        if (!$logs) {
            return $base;
        }

        usort(
            $logs,
            fn ($a, $b) => filemtime($b) <=> filemtime($a)
        );

        $log = $logs[0];
        $content = @file_get_contents($log);

        if ($content === false) {
            return $base;
        }

        $success = str_contains(
            $content,
            'Backup finalizado com sucesso'
        );

        $remoteConfirmed = str_contains(
            $content,
            'Todos os backups foram confirmados no OneDrive'
        );

        $duration = null;

        if (preg_match(
            '/Backup finalizado com sucesso em\s+(\d+)\s+segundos/i',
            $content,
            $m
        )) {
            $duration = (int) $m[1];
        }

        return [
            'status' => $success ? 'ok' : 'falha',
            'file' => basename($log),
            'date_human' => date(
                'd/m/Y H:i',
                filemtime($log)
            ),
            'duration_seconds' => $duration,
            'duration_human' => $duration !== null
                ? $this->humanDuration($duration)
                : '—',
            'remote_confirmed' => $remoteConfirmed,
        ];
    }

    private function humanBytes(int|float $bytes): string
    {
        if ($bytes <= 0) {
            return '0 B';
        }

        $units = ['B', 'KB', 'MB', 'GB', 'TB'];
        $power = min(
            (int) floor(log($bytes, 1024)),
            count($units) - 1
        );

        return number_format(
            $bytes / (1024 ** $power),
            1,
            ',',
            '.'
        ) . ' ' . $units[$power];
    }

    private function humanDuration(int $seconds): string
    {
        $hours = intdiv($seconds, 3600);
        $minutes = intdiv($seconds % 3600, 60);
        $secs = $seconds % 60;

        $parts = [];

        if ($hours > 0) {
            $parts[] = $hours . 'h';
        }

        if ($minutes > 0 || $hours > 0) {
            $parts[] = $minutes . 'min';
        }

        $parts[] = $secs . 's';

        return implode(' ', $parts);
    }
}
