<?php

namespace App\Console\Commands;

use Illuminate\Console\Command;
use Illuminate\Http\Client\Response;
use Illuminate\Support\Facades\Http;
use Illuminate\Support\Str;

class BackupUploadFileCommand extends Command
{
    protected $signature = 'backup:upload-file
        {file : Path file .backup yang akan dikirim}
        {--url= : Base URL server cloud backup}
        {--token= : Token API cloud backup}
        {--name= : Nama backup}
        {--chunk-size= : Ukuran chunk dalam byte}
        {--retries=5 : Jumlah percobaan ulang setiap chunk}
        {--retry-delay=10 : Jeda percobaan ulang dalam detik}
        {--timeout=300 : Timeout request upload dalam detik}';

    protected $description = 'Upload file backup ke duidev.com secara bertahap/chunked.';

    public function handle(): int
    {
        $file = (string) $this->argument('file');
        if (!is_file($file) || !is_readable($file)) {
            $this->error('File backup tidak ditemukan atau tidak bisa dibaca: ' . $file);
            return Command::FAILURE;
        }

        $baseUrl = rtrim((string) ($this->option('url') ?: config('cloud_backup.api_url')), '/');
        $token = (string) ($this->option('token') ?: config('cloud_backup.api_token'));
        $backupName = (string) ($this->option('name') ?: config('cloud_backup.backup_name', 'lismikro'));
        $chunkSize = (int) ($this->option('chunk-size') ?: config('cloud_backup.chunk_size', 5 * 1024 * 1024));
        $retries = max(1, (int) $this->option('retries'));
        $retryDelay = max(0, (int) $this->option('retry-delay'));
        $timeout = max(30, (int) $this->option('timeout'));

        if ($baseUrl === '' || $token === '') {
            $this->error('CLOUD_BACKUP_API_URL dan CLOUD_BACKUP_API_TOKEN wajib diisi.');
            return Command::FAILURE;
        }

        if ($chunkSize < 1024 * 1024) {
            $this->error('Chunk size minimal 1 MB.');
            return Command::FAILURE;
        }

        $size = filesize($file);
        $checksum = hash_file('sha256', $file);
        $totalChunks = (int) ceil($size / $chunkSize);
        $uploadId = now()->format('YmdHis') . '_' . Str::random(16);
        $endpoint = $baseUrl . '/api/backups/chunk';

        $this->info('Upload backup: ' . basename($file));
        $this->info('Upload ID: ' . $uploadId);
        $this->info('Total chunk: ' . $totalChunks);

        $handle = fopen($file, 'rb');
        if (!$handle) {
            $this->error('Gagal membuka file backup.');
            return Command::FAILURE;
        }

        try {
            for ($index = 0; $index < $totalChunks; $index++) {
                $chunk = fread($handle, $chunkSize);
                if ($chunk === false) {
                    $this->error('Gagal membaca chunk ke-' . $index);
                    return Command::FAILURE;
                }

                $response = $this->uploadChunk(
                    endpoint: $endpoint,
                    token: $token,
                    chunk: $chunk,
                    chunkIndex: $index,
                    payload: [
                        'backup_name' => $backupName,
                        'upload_id' => $uploadId,
                        'filename' => basename($file),
                        'chunk_index' => $index,
                        'total_chunks' => $totalChunks,
                        'total_size' => $size,
                        'checksum' => $checksum,
                    ],
                    retries: $retries,
                    retryDelay: $retryDelay,
                    timeout: $timeout
                );

                if (!$response || !$response->successful()) {
                    $this->error('Upload chunk ke-' . $index . ' gagal. HTTP ' . ($response?->status() ?: '-'));
                    $this->line($response?->body() ?: 'Tidak ada response dari server.');
                    return Command::FAILURE;
                }

                $this->line('Chunk ' . ($index + 1) . '/' . $totalChunks . ' terkirim.');
            }
        } finally {
            fclose($handle);
        }

        $this->info('Upload backup selesai.');

        return Command::SUCCESS;
    }

    private function uploadChunk(
        string $endpoint,
        string $token,
        string $chunk,
        int $chunkIndex,
        array $payload,
        int $retries,
        int $retryDelay,
        int $timeout
    ): ?Response {
        $response = null;

        for ($attempt = 1; $attempt <= $retries; $attempt++) {
            try {
                $response = Http::withToken($token)
                    ->timeout($timeout)
                    ->attach('chunk', $chunk, 'chunk_' . $chunkIndex . '.part')
                    ->post($endpoint, $payload);

                if ($response->successful()) {
                    return $response;
                }

                $this->warn('Chunk ' . ($chunkIndex + 1) . ' gagal percobaan ' . $attempt . '/' . $retries . '. HTTP ' . $response->status());
            } catch (\Throwable $e) {
                $this->warn('Chunk ' . ($chunkIndex + 1) . ' gagal percobaan ' . $attempt . '/' . $retries . ': ' . $e->getMessage());
            }

            if ($attempt < $retries && $retryDelay > 0) {
                sleep($retryDelay);
            }
        }

        return $response;
    }
}
