<?php

declare(strict_types=1);

namespace App\Http\Controllers\Admin;

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

class SnapshotController extends Controller
{
    public function __construct(private readonly SnapshotService $service) {}

    public function index(Request $request): Response
    {
        $query = AccountSnapshot::with(['account:id,username,email', 'createdBy:id,name']);

        if ($s = $request->string('source')->value()) {
            $query->where('source', $s);
        }
        if ($search = $request->string('search')->value()) {
            $query->where(function ($q) use ($search) {
                $q->where('snapshot_number', 'like', "%{$search}%")
                    ->orWhere('source_username', 'like', "%{$search}%")
                    ->orWhereHas('account', fn ($a) => $a->where('username', 'like', "%{$search}%"));
            });
        }

        $snapshots = $query->latest()->paginate(20)->withQueryString()
            ->through(function ($s) {
                $replicas = collect($s->manifest['replicated_to'] ?? []);

                return [
                    'id' => $s->id,
                    'snapshot_number' => $s->snapshot_number,
                    'type' => $s->type,
                    'source' => $s->source,
                    'status' => $s->status,
                    'account_username' => $s->account?->username,
                    'account_email' => $s->account?->email,
                    'source_username' => $s->source_username,
                    'source_panel_version' => $s->source_panel_version,
                    'size_bytes' => $s->size_bytes,
                    'size_human' => $this->humanBytes($s->size_bytes),
                    'created_by' => $s->createdBy?->name,
                    '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'),
                    'replicated_to_count' => $replicas->count(),
                ];
            });

        return Inertia::render('Admin/Snapshots/Index', [
            'snapshots' => $snapshots,
            'filters' => [
                'source' => $request->string('source')->value(),
                'search' => $request->string('search')->value(),
            ],
            'stats' => [
                'total' => AccountSnapshot::count(),
                'imports' => AccountSnapshot::whereIn('type', ['cpanel_import', 'plesk_import'])->count(),
                'pending' => AccountSnapshot::whereIn('status', ['pending', 'creating', 'importing'])->count(),
                'failed' => AccountSnapshot::where('status', 'failed')->where('created_at', '>=', now()->subWeek())->count(),
                'total_size' => AccountSnapshot::sum('size_bytes'),
            ],
        ]);
    }

    public function create(Request $request): Response
    {
        $accounts = Account::orderBy('username')->get(['id', 'username', 'email', 'primary_domain']);

        return Inertia::render('Admin/Snapshots/Create', [
            'accounts' => $accounts,
            'preselected_account_id' => $request->integer('account_id'),
        ]);
    }

    public function store(Request $request): RedirectResponse
    {
        $validated = $request->validate([
            'account_id' => ['required', 'integer', 'exists:accounts,id'],
            'type' => ['required', 'in:manual_snapshot,full_export,pre_terminate'],
            'includes_files' => ['boolean'],
            'includes_databases' => ['boolean'],
            'includes_dns' => ['boolean'],
            'includes_email' => ['boolean'],
            'includes_config' => ['boolean'],
            'includes_ssl' => ['boolean'],
            'includes_cron' => ['boolean'],
            'notes' => ['nullable', 'string', 'max:1000'],
        ]);

        $account = Account::findOrFail($validated['account_id']);

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

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

            return redirect()->route('admin.snapshots.show', $snapshot->id)
                ->with('success', "Snapshot {$snapshot->snapshot_number} oluşturuldu.");
        } catch (RuntimeException $e) {
            return back()->with('error', $e->getMessage())->withInput();
        }
    }

    public function importForm(Request $request): Response
    {
        $accounts = Account::orderBy('username')->get(['id', 'username', 'email', 'primary_domain']);

        return Inertia::render('Admin/Snapshots/Import', [
            'accounts' => $accounts,
        ]);
    }

    public function import(Request $request): RedirectResponse
    {
        $validated = $request->validate([
            'archive' => ['required', 'file', 'mimes:gz,tar,zip', 'max:5242880'], // 5 GB
            'source_panel' => ['required', 'in:cpanel,plesk,directadmin'],
            'source_version' => ['nullable', 'string', 'max:50'],
            'source_hostname' => ['nullable', 'string', 'max:191'],
            'source_username' => ['nullable', 'string', 'max:100'],
            'primary_domain' => ['nullable', 'string', 'max:253'],
            'notes' => ['nullable', 'string', 'max:1000'],
        ]);

        try {
            $snapshot = $this->service->importSnapshot(
                $request->file('archive'),
                $request->user(),
                $validated['source_panel'],
                [
                    'version' => $validated['source_version'] ?? null,
                    'hostname' => $validated['source_hostname'] ?? null,
                    'username' => $validated['source_username'] ?? null,
                    'primary_domain' => $validated['primary_domain'] ?? null,
                    'notes' => $validated['notes'] ?? null,
                ],
            );

            return redirect()->route('admin.snapshots.show', $snapshot->id)
                ->with('success', "İmport tamamlandı: {$snapshot->snapshot_number}. Şimdi bir hesaba restore edebilirsin.");
        } catch (RuntimeException $e) {
            return back()->with('error', $e->getMessage())->withInput();
        }
    }

    public function show(AccountSnapshot $snapshot): Response
    {
        $snapshot->load(['account:id,username,email,primary_domain', 'createdBy:id,name,email']);

        return Inertia::render('Admin/Snapshots/Show', [
            'snapshot' => [
                'id' => $snapshot->id,
                'snapshot_number' => $snapshot->snapshot_number,
                'type' => $snapshot->type,
                'source' => $snapshot->source,
                'status' => $snapshot->status,
                'file_path' => $snapshot->file_path,
                'size_bytes' => $snapshot->size_bytes,
                'size_human' => $this->humanBytes($snapshot->size_bytes),
                'checksum_sha256' => $snapshot->checksum_sha256,
                'includes_files' => $snapshot->includes_files,
                'includes_databases' => $snapshot->includes_databases,
                'includes_dns' => $snapshot->includes_dns,
                'includes_email' => $snapshot->includes_email,
                'includes_config' => $snapshot->includes_config,
                'includes_ssl' => $snapshot->includes_ssl,
                'includes_cron' => $snapshot->includes_cron,
                'manifest' => $snapshot->manifest,
                'source_panel_version' => $snapshot->source_panel_version,
                'source_hostname' => $snapshot->source_hostname,
                'source_username' => $snapshot->source_username,
                'notes' => $snapshot->notes,
                'last_error' => $snapshot->last_error,
                'expires_at' => $snapshot->expires_at?->format('d.m.Y'),
                'started_at' => $snapshot->started_at?->format('d.m.Y H:i'),
                'completed_at' => $snapshot->completed_at?->format('d.m.Y H:i'),
                'duration_seconds' => $snapshot->duration_seconds,
                'account' => $snapshot->account ? [
                    'id' => $snapshot->account->id,
                    'username' => $snapshot->account->username,
                    'email' => $snapshot->account->email,
                    'primary_domain' => $snapshot->account->primary_domain,
                ] : null,
                'created_by' => $snapshot->createdBy ? ['name' => $snapshot->createdBy->name, 'email' => $snapshot->createdBy->email] : null,
                'is_import' => $snapshot->isImport(),
                'can_restore' => $snapshot->canRestore(),
            ],
            'accounts' => Account::orderBy('username')->get(['id', 'username', 'email', 'primary_domain']),
            'cluster_nodes' => ClusterNode::whereNull('deleted_at')
                ->where('status', 'active')
                ->orderBy('id')
                ->get(['id', 'name', 'hostname', 'role'])
                ->map(fn ($n) => [
                    'id' => $n->id,
                    'name' => $n->name,
                    'hostname' => $n->hostname,
                    'role' => $n->role,
                ]),
            'replication_status' => $this->service->replicationStatus($snapshot),
        ]);
    }

    public function restore(Request $request, AccountSnapshot $snapshot): RedirectResponse
    {
        $validated = $request->validate([
            'target_account_id' => ['nullable', 'integer', 'exists:accounts,id'],
            'overwrite' => ['boolean'],
        ]);

        $target = $validated['target_account_id'] ? Account::find($validated['target_account_id']) : null;

        try {
            $this->service->restoreSnapshot($snapshot, $target, $validated['overwrite'] ?? false);

            return back()->with('success', "Snapshot {$snapshot->snapshot_number} restore işlemi başlatıldı.");
        } catch (RuntimeException $e) {
            return back()->with('error', $e->getMessage());
        }
    }

    public function download(\Illuminate\Http\Request $request, AccountSnapshot $snapshot)
    {
        if (! $snapshot->canRestore()) {
            abort(404);
        }

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

        // Path traversal koruması — Customer/SnapshotController ile aynı whitelist
        $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.admin_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.');
        }

        try {
            \Illuminate\Support\Facades\DB::table('audit_log')->insert([
                'user_id' => $request->user()?->id,
                'action' => 'snapshot.admin_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,
                ], JSON_UNESCAPED_UNICODE),
                'created_at' => now(),
            ]);
        } catch (\Throwable) {
            // soft-fail
        }

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

    public function destroy(AccountSnapshot $snapshot): RedirectResponse
    {
        $snapshot->delete();

        return redirect()->route('admin.snapshots.index')->with('success', 'Snapshot silindi.');
    }

    /**
     * Snapshot'ı seçilen cluster node'larına replikate et.
     *
     * Her node için cluster_jobs içine 'snapshot-receive' action yazılır;
     * slave-agent rsync over SSH ile tar.gz dosyasını çeker.
     */
    public function replicate(Request $request, AccountSnapshot $snapshot): RedirectResponse
    {
        $validated = $request->validate([
            'node_ids' => ['required', 'array', 'min:1'],
            'node_ids.*' => ['integer', 'exists:cluster_nodes,id'],
        ]);

        try {
            $jobs = $this->service->replicate($snapshot, $validated['node_ids'], $request->user());
        } catch (RuntimeException $e) {
            return back()->with('error', $e->getMessage());
        }

        $count = count($jobs);

        return back()->with(
            'success',
            "Snapshot {$snapshot->snapshot_number} {$count} node'a replikasyon için kuyruğa alındı."
        );
    }

    /**
     * Bir snapshot için replikasyon durumu (JSON).
     */
    public function replicationStatus(AccountSnapshot $snapshot): JsonResponse
    {
        return response()->json($this->service->replicationStatus($snapshot));
    }

    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';
    }
}
