<?php

declare(strict_types=1);

namespace App\Http\Controllers\Customer;

use App\Domain\Account\Models\Account;
use App\Domain\Snapshot\Models\AccountSnapshot;
use App\Domain\Snapshot\Services\SnapshotService;
use App\Http\Controllers\Controller;
use Illuminate\Http\RedirectResponse;
use Illuminate\Http\Request;
use Inertia\Inertia;
use Inertia\Response;
use RuntimeException;

/**
 * Customer self-snapshot — müşteri kendi hesabının yedek snapshot'ını alır.
 *
 * Sınırlamalar:
 * - Maks 5 snapshot/account
 * - 30 gün retention (otomatik silinir)
 * - Aylık 10 snapshot oluşturma limiti (abuse koruması)
 */
class SnapshotController extends Controller
{
    public function __construct(private readonly SnapshotService $service) {}

    public function index(Request $request): Response
    {
        /** @var Account $account */
        $account = $request->attributes->get('customer_account');

        $snapshots = AccountSnapshot::where('account_id', $account->id)
            ->whereNull('deleted_at')
            ->latest()
            ->get()
            ->map(fn ($s) => [
                'id' => $s->id,
                'snapshot_number' => $s->snapshot_number,
                'type' => $s->type,
                'status' => $s->status,
                'size_bytes' => $s->size_bytes,
                'size_human' => $this->humanBytes($s->size_bytes),
                'includes_files' => $s->includes_files,
                'includes_databases' => $s->includes_databases,
                'includes_email' => $s->includes_email,
                'notes' => $s->notes,
                'created_at' => $s->created_at->format('d.m.Y H:i'),
                'completed_at' => $s->completed_at?->format('d.m.Y H:i'),
                'duration_seconds' => $s->duration_seconds,
                'expires_at' => $s->expires_at?->format('d.m.Y'),
                'expires_in_days' => $s->expires_at?->diffInDays(now(), false),
                'can_restore' => $s->canRestore(),
            ]);

        $monthlyCount = AccountSnapshot::where('account_id', $account->id)
            ->where('created_at', '>=', now()->startOfMonth())
            ->count();

        return Inertia::render('Customer/Snapshots/Index', [
            'snapshots' => $snapshots,
            'limits' => [
                'max_active' => 5,
                'max_per_month' => 10,
                'retention_days' => 30,
                'active_count' => $snapshots->count(),
                'monthly_count' => $monthlyCount,
            ],
        ]);
    }

    public function store(Request $request): RedirectResponse
    {
        /** @var Account $account */
        $account = $request->attributes->get('customer_account');

        $validated = $request->validate([
            'includes_files' => ['boolean'],
            'includes_databases' => ['boolean'],
            'includes_email' => ['boolean'],
            'includes_dns' => ['boolean'],
            'notes' => ['nullable', 'string', 'max:500'],
        ]);

        // Rate limit checks
        $activeCount = AccountSnapshot::where('account_id', $account->id)->whereNull('deleted_at')->count();
        if ($activeCount >= 5) {
            return back()->with('error', 'Maksimum 5 aktif snapshot tutabilirsiniz. Eski birini silin.');
        }

        $monthlyCount = AccountSnapshot::where('account_id', $account->id)
            ->where('created_at', '>=', now()->startOfMonth())->count();
        if ($monthlyCount >= 10) {
            return back()->with('error', 'Bu ay maksimum 10 snapshot oluşturma hakkınızı kullandınız. Bir sonraki ay tekrar deneyin.');
        }

        try {
            $snapshot = $this->service->createSnapshot($account, $request->user(), 'manual_snapshot', [
                'files' => $validated['includes_files'] ?? true,
                'databases' => $validated['includes_databases'] ?? true,
                'email' => $validated['includes_email'] ?? true,
                'dns' => $validated['includes_dns'] ?? true,
                'config' => false,
                'ssl' => false,
                'cron' => true,
            ]);

            if (! empty($validated['notes'])) {
                $snapshot->update(['notes' => $validated['notes']]);
            }

            return back()->with('success', "Snapshot {$snapshot->snapshot_number} oluşturuldu.");
        } catch (RuntimeException $e) {
            return back()->with('error', $e->getMessage());
        }
    }

    public function destroy(Request $request, AccountSnapshot $snapshot): RedirectResponse
    {
        /** @var Account $account */
        $account = $request->attributes->get('customer_account');
        if ($snapshot->account_id !== $account->id) {
            abort(403);
        }

        $snapshot->delete();

        return back()->with('success', 'Snapshot silindi.');
    }

    public function restore(Request $request, AccountSnapshot $snapshot): RedirectResponse
    {
        /** @var Account $account */
        $account = $request->attributes->get('customer_account');
        if ($snapshot->account_id !== $account->id) {
            abort(403);
        }

        try {
            $this->service->restoreSnapshot($snapshot, $account, true);

            return back()->with('success', 'Restore başlatıldı. Site geçici olarak erişilmez olabilir.');
        } catch (RuntimeException $e) {
            return back()->with('error', $e->getMessage());
        }
    }

    public function download(Request $request, AccountSnapshot $snapshot)
    {
        /** @var Account $account */
        $account = $request->attributes->get('customer_account');
        if ($snapshot->account_id !== $account->id) {
            abort(403);
        }

        if (! $snapshot->isCompleted()) {
            return back()->with('error', 'Snapshot henüz tamamlanmadı.');
        }

        $filePath = $snapshot->file_path;
        if (! $filePath || ! is_string($filePath)) {
            return back()->with('error', 'Snapshot dosya yolu kayıtlı değil.');
        }

        // Path traversal koruması: snapshot yolları sadece /var/backups/onoxsoft/ veya
        // /var/lib/onox/snapshots/ altında olmalı. Bunun dışına çıkmaya çalışan satır
        // reddedilir (DB tampering / config drift için savunma).
        $allowedPrefixes = [
            '/var/backups/onoxsoft/',
            '/var/lib/onox/snapshots/',
        ];
        $resolved = realpath($filePath) ?: $filePath;
        $isAllowed = false;
        foreach ($allowedPrefixes as $prefix) {
            if (str_starts_with($resolved, $prefix)) {
                $isAllowed = true;
                break;
            }
        }
        if (! $isAllowed) {
            \Illuminate\Support\Facades\Log::warning('snapshot.download_path_rejected', [
                'snapshot_id' => $snapshot->id,
                'requested' => $filePath,
            ]);
            return back()->with('error', 'Geçersiz snapshot dosya yolu.');
        }

        if (! file_exists($filePath) || ! is_readable($filePath)) {
            return back()->with('error', 'Snapshot dosyası okunamıyor (panel kullanıcısının okuma izni yok mu?).');
        }

        // Audit
        try {
            \Illuminate\Support\Facades\DB::table('audit_log')->insert([
                'user_id' => $request->user()?->id,
                'action' => 'snapshot.downloaded',
                'target_type' => 'account_snapshot',
                'target_id' => $snapshot->id,
                'ip_address' => $request->ip(),
                'outcome' => 'success',
                'metadata' => json_encode([
                    'snapshot_number' => $snapshot->snapshot_number,
                    'size_bytes' => $snapshot->size_bytes,
                    'account_id' => $account->id,
                ], JSON_UNESCAPED_UNICODE),
                'created_at' => now(),
            ]);
        } catch (\Throwable) {
            // soft-fail
        }

        $downloadName = $snapshot->snapshot_number.'.tar.gz';

        return response()->download($filePath, $downloadName, [
            'Content-Type' => 'application/gzip',
            'X-Content-Type-Options' => 'nosniff',
        ]);
    }

    private function humanBytes(int $bytes): string
    {
        if ($bytes < 1024) {
            return $bytes.' B';
        }
        if ($bytes < 1024 ** 2) {
            return number_format($bytes / 1024, 1).' KB';
        }
        if ($bytes < 1024 ** 3) {
            return number_format($bytes / 1024 ** 2, 1).' MB';
        }

        return number_format($bytes / 1024 ** 3, 2).' GB';
    }
}
