<?php

namespace App\Http\Controllers\Admin;

use App\Http\Controllers\Controller;
use App\Models\JobApplication;
use App\Models\SiteSetting;
use Illuminate\Http\Request;
use Illuminate\Support\Facades\Artisan;
use Illuminate\Support\Facades\Log;
use Illuminate\Support\Facades\Mail;
use Illuminate\Support\Facades\Storage;

class SiteSettingController extends Controller
{
    public function index()
    {
        $settings = SiteSetting::all()->keyBy('key');
        $isMaintenanceMode = app()->isDownForMaintenance();
        $systemInfo = $this->getSystemInfo();
        $backupTables = $this->getDefaultTables();

        // KVKK: Silinecek başvurular listesi (sadece super-admin görebilir)
        $kvkkData = null;
        if (auth()->user()->hasRole('super-admin')) {
            $retentionDays = (int) SiteSetting::get('kvkk_retention_days', 90);
            $cutoff = now()->subDays($retentionDays);

            // Silinmeyi bekleyen başvurular (süre dolmuş, henüz purge edilmemiş)
            $pendingApplications = JobApplication::with('jobPosting')
                ->where('created_at', '<', $cutoff)
                ->whereNull('purged_at')
                ->orderBy('created_at')
                ->get()
                ->map(function ($app) use ($retentionDays) {
                    $deleteDate = $app->created_at->addDays($retentionDays);
                    $daysOverdue = now()->diffInDays($deleteDate, false); // negatif = gecikmiş
                    return [
                        'id'           => $app->id,
                        'full_name'    => $app->full_name,
                        'email'        => $app->email,
                        'job_title'    => $app->jobPosting->title ?? '—',
                        'applied_at'   => $app->created_at->format('d.m.Y'),
                        'delete_date'  => $deleteDate->format('d.m.Y'),
                        'days_overdue' => abs((int) $daysOverdue),
                    ];
                });

            // 30 gün içinde dolacak başvurular (henüz süre dolmamış ama yaklaşıyor)
            $expiringApplications = JobApplication::with('jobPosting')
                ->whereBetween('created_at', [
                    now()->subDays($retentionDays),
                    now()->subDays($retentionDays - 30),
                ])
                ->whereNull('purged_at')
                ->orderBy('created_at')
                ->get()
                ->map(function ($app) use ($retentionDays) {
                    $deleteDate = $app->created_at->addDays($retentionDays);
                    $daysLeft = now()->diffInDays($deleteDate, false);
                    return [
                        'id'          => $app->id,
                        'full_name'   => $app->full_name,
                        'email'       => $app->email,
                        'job_title'   => $app->jobPosting->title ?? '—',
                        'applied_at'  => $app->created_at->format('d.m.Y'),
                        'delete_date' => $deleteDate->format('d.m.Y'),
                        'days_left'   => max(0, (int) $daysLeft),
                    ];
                });

            // Tüm aktif başvurular (süresi dolmamış, purge edilmemiş) — bellek taşmasını önlemek için limit
            $allActiveApplications = JobApplication::with('jobPosting')
                ->where('created_at', '>=', $cutoff)
                ->whereNull('purged_at')
                ->orderBy('created_at')
                ->limit(500)
                ->get()
                ->map(function ($app) use ($retentionDays) {
                    $deleteDate = $app->created_at->addDays($retentionDays);
                    $daysLeft = now()->diffInDays($deleteDate, false);
                    return [
                        'id'          => $app->id,
                        'full_name'   => $app->full_name,
                        'email'       => $app->email,
                        'job_title'   => $app->jobPosting->title ?? '—',
                        'applied_at'  => $app->created_at->format('d.m.Y'),
                        'delete_date' => $deleteDate->format('d.m.Y'),
                        'days_left'   => max(0, (int) $daysLeft),
                    ];
                });

            $kvkkData = [
                'retention_days'        => $retentionDays,
                'pending_count'         => $pendingApplications->count(),
                'expiring_count'        => $expiringApplications->count(),
                'active_count'          => $allActiveApplications->count(),
                'purged_total'          => JobApplication::whereNotNull('purged_at')->count(),
                'pending_applications'  => $pendingApplications,
                'expiring_applications' => $expiringApplications,
                'active_applications'   => $allActiveApplications,
                'next_purge_date'       => now()->addDay()->setTime(2, 0)->format('d.m.Y 02:00'),
            ];
        }

        return view('admin.ayarlar.index', compact('settings', 'isMaintenanceMode', 'systemInfo', 'backupTables', 'kvkkData'));
    }

    /**
     * KVKK: Tek başvuruyu kalıcı sil — sadece super-admin
     */
    public function kvkkDeleteSingle(Request $request, JobApplication $application)
    {
        if (!auth()->user()->hasRole('super-admin')) {
            abort(403, 'Bu işlem için yetkiniz yok.');
        }

        // CV dosyasını sil
        if ($application->cv_path && Storage::disk('local')->exists($application->cv_path)) {
            Storage::disk('local')->delete($application->cv_path);
        }

        // Kişisel verileri anonimleştir
        $application->update([
            'full_name'        => 'Silindi',
            'email'            => 'silindi_' . $application->id . '@kvkk.local',
            'phone'            => null,
            'cover_letter'     => null,
            'cv_path'          => null,
            'cv_original_name' => null,
            'linkedin_url'     => null,
            'internal_notes'   => null,
            'kvkk_consent_ip'  => null,
            'purged_at'        => now(),
        ]);

        Log::info('KVKK Tek Silme', [
            'by_user'        => auth()->id(),
            'application_id' => $application->id,
        ]);

        return back()->with('success', 'Başvurunun kişisel verileri KVKK gereği silindi.');
    }

    /**
     * KVKK: Tüm süresi dolmuş başvuruları toplu sil — sadece super-admin
     * chunk() ile bellek güvenli işlem
     */
    public function kvkkDeleteAll(Request $request)
    {
        if (!auth()->user()->hasRole('super-admin')) {
            abort(403, 'Bu işlem için yetkiniz yok.');
        }

        $retentionDays = (int) SiteSetting::get('kvkk_retention_days', 90);
        $cutoff = now()->subDays($retentionDays);

        $count = 0;

        JobApplication::where('created_at', '<', $cutoff)
            ->whereNull('purged_at')
            ->chunk(100, function ($applications) use (&$count) {
                foreach ($applications as $application) {
                    if ($application->cv_path && Storage::disk('local')->exists($application->cv_path)) {
                        Storage::disk('local')->delete($application->cv_path);
                    }

                    $application->update([
                        'full_name'        => 'Silindi',
                        'email'            => 'silindi_' . $application->id . '@kvkk.local',
                        'phone'            => null,
                        'cover_letter'     => null,
                        'cv_path'          => null,
                        'cv_original_name' => null,
                        'linkedin_url'     => null,
                        'internal_notes'   => null,
                        'kvkk_consent_ip'  => null,
                        'purged_at'        => now(),
                    ]);

                    $count++;
                }
            });

        if ($count === 0) {
            return back()->with('success', 'Silinecek eski başvuru bulunamadı.');
        }

        Log::info('KVKK Toplu Silme', [
            'by_user' => auth()->id(),
            'count'   => $count,
            'cutoff'  => $cutoff->toDateTimeString(),
        ]);

        return back()->with('success', "{$count} başvurunun kişisel verileri KVKK gereği temizlendi.");
    }

    /**
     * İzin verilen ayar key'leri — whitelist ile güvenli güncelleme
     */
    protected const ALLOWED_SETTING_KEYS = [
        // Genel
        'site_name', 'site_title', 'site_description', 'site_email', 'site_phone', 'site_address',
        'site_facebook', 'site_twitter', 'site_linkedin', 'site_instagram',
        'contact_email', 'notification_email',
        // KVKK
        'kvkk_text', 'kvkk_retention_days', 'kvkk_updated_at',
        // Banner
        'banner_enabled', 'banner_text', 'banner_color', 'banner_link',
        'banner_title', 'banner_subtitle', 'banner_button_text', 'banner_button_url', 'banner_bg_color',
        // WhatsApp
        'whatsapp_enabled', 'whatsapp_number', 'whatsapp_message',
        // VirusTotal
        'virustotal_enabled', 'virustotal_api_key',
        // Görüşme bildirimleri
        'interview_email_notifications',
        // CV tarama modu
        'cv_scan_mode',
    ];

    public function update(Request $request)
    {
        $request->validate([
            'settings' => 'nullable|array',
        ]);

        // Sadece whitelist'teki key'leri kaydet
        foreach ($request->input('settings', []) as $key => $value) {
            if (!in_array($key, self::ALLOWED_SETTING_KEYS, true)) {
                continue; // İzin verilmeyen key'leri atla
            }

            // kvkk_retention_days için ek server-side validasyon
            if ($key === 'kvkk_retention_days') {
                $days = (int) $value;
                if ($days < 30 || $days > 730) {
                    $days = 90; // Geçersiz değeri varsayılana düşür
                }
                $value = (string) $days;
            }

            $group = $this->resolveGroup($key);
            SiteSetting::set($key, $value ?? '', $group);
        }

        return back()->with('success', 'Ayarlar kaydedildi.');
    }

    public function backup(Request $request)
    {
        try {
            $mode   = $request->input('mode', 'full');   // full | schema | tables
            $format = $request->input('format', 'sql');  // sql | gz | json

            // Validation
            if (!in_array($mode, ['full', 'schema', 'tables'])) {
                return back()->with('error', 'Geçersiz yedek modu.');
            }
            if (!in_array($format, ['sql', 'gz', 'json'])) {
                return back()->with('error', 'Geçersiz dosya formatı.');
            }
            if ($mode === 'schema' && $format === 'json') {
                return back()->with('error', 'İskelet modu JSON formatını desteklemiyor.');
            }

            // Tablo listesi — whitelist + regex ile güvenli filtreleme
            $tables = [];
            if ($mode === 'tables') {
                $tables = $this->sanitizeTables((array) $request->input('tables', []));
                if (empty($tables)) {
                    return back()->with('error', 'Lütfen en az bir geçerli tablo seçin.');
                }
            }

            // Yedek dizini
            $backupDir = storage_path('app/backups');
            if (!is_dir($backupDir)) {
                mkdir($backupDir, 0755, true);
            }

            $dbName = config('database.connections.' . config('database.default') . '.database');
            $suffix = $mode === 'schema' ? '_iskelet' : ($mode === 'tables' ? '_tablolar' : '');
            $baseName = 'backup_' . $dbName . $suffix . '_' . now()->format('Y-m-d_H-i-s');

            // full ve schema modlarında tüm tablolar kullanılır
            if (in_array($mode, ['full', 'schema'])) {
                $tables = $this->getDefaultTables();
            }

            if ($format === 'json') {
                $filePath = $this->backupJson($tables, $backupDir, $baseName);
                return response()->download($filePath, $baseName . '.json', [
                    'Content-Type' => 'application/json',
                ])->deleteFileAfterSend(true);
            }

            if ($format === 'gz') {
                $filePath = $this->backupGz($mode, $tables, $backupDir, $baseName);
                return response()->download($filePath, $baseName . '.sql.gz', [
                    'Content-Type' => 'application/gzip',
                ])->deleteFileAfterSend(true);
            }

            // sql
            $filePath = $this->backupSql($mode, $tables, $backupDir, $baseName);
            return response()->download($filePath, $baseName . '.sql', [
                'Content-Type' => 'application/sql',
            ])->deleteFileAfterSend(true);

        } catch (\Exception $e) {
            Log::error('Veritabanı yedekleme hatası', [
                'by_user' => auth()->id(),
                'error'   => $e->getMessage(),
                'trace'   => $e->getTraceAsString(),
            ]);
            return back()->with('error', 'Yedek alınırken bir hata oluştu. Lütfen tekrar deneyin.');
        }
    }

    /**
     * Veritabanındaki gerçek tabloları SHOW TABLES ile dinamik olarak döndürür.
     * Sabit liste yerine her zaman güncel DB durumunu yansıtır.
     */
    protected function getDefaultTables(): array
    {
        $rows = \Illuminate\Support\Facades\DB::select('SHOW TABLES');
        return array_map(function ($row) {
            return array_values((array) $row)[0];
        }, $rows);
    }

    /**
     * Kullanıcıdan gelen tablo listesini dinamik DB listesiyle filtreler.
     * SQL injection'a karşı: sadece [a-zA-Z0-9_] karakterlerine izin verilir
     * ve gerçek DB tabloları ile karşılaştırılır.
     */
    protected function sanitizeTables(array $userTables): array
    {
        $existing = $this->getDefaultTables();
        return array_values(array_filter($userTables, function ($t) use ($existing) {
            return preg_match('/^[a-zA-Z0-9_]+$/', $t) && in_array($t, $existing, true);
        }));
    }

    /**
     * PHP/PDO ile SQL dump üretir — mysqldump gerektirmez.
     * Güvenlik önlemleri:
     *   - Tablo adları whitelist + regex ile doğrulanır
     *   - Tablo adları backtick ile sarılır (SQL injection engeli)
     *   - Değerler PDO::quote() ile escape edilir
     *   - PDO cursor ile bellek güvenli okuma
     */
    protected function generateSqlDump(string $mode, array $tables, $handle): void
    {
        $pdo    = \Illuminate\Support\Facades\DB::connection()->getPdo();
        $dbName = config('database.connections.' . config('database.default') . '.database');

        fwrite($handle, "-- Markamama Kariyer DB Backup\n");
        fwrite($handle, "-- Generated: " . now()->toIso8601String() . "\n");
        fwrite($handle, "-- Database: " . $dbName . "\n\n");
        fwrite($handle, "SET FOREIGN_KEY_CHECKS=0;\n\n");

        foreach ($tables as $table) {
            // Tablo adı güvenlik kontrolü (double-check)
            if (!preg_match('/^[a-zA-Z0-9_]+$/', $table)) {
                continue;
            }

            $quotedTable = '`' . $table . '`';

            // DROP + CREATE TABLE
            fwrite($handle, "-- Table: {$table}\n");
            fwrite($handle, "DROP TABLE IF EXISTS {$quotedTable};\n");

            try {
                $createRow = \Illuminate\Support\Facades\DB::select("SHOW CREATE TABLE {$quotedTable}");
            } catch (\Exception $e) {
                fwrite($handle, "-- SKIPPED (table not found): {$table}\n\n");
                continue;
            }

            if (!empty($createRow)) {
                $createSql = $createRow[0]->{'Create Table'} ?? '';
                fwrite($handle, $createSql . ";\n\n");
            }

            // Veri (iskelet modunda atla)
            if ($mode === 'schema') {
                continue;
            }

            // PDO cursor ile bellek güvenli okuma
            $hasRows = false;
            try {
                $stmt = $pdo->query("SELECT * FROM `{$table}`");
                $stmt->setFetchMode(\PDO::FETCH_ASSOC);
                while ($rowArr = $stmt->fetch()) {
                    $cols = implode(', ', array_map(fn($c) => '`' . $c . '`', array_keys($rowArr)));
                    $vals = implode(', ', array_map(function ($v) use ($pdo) {
                        if ($v === null) return 'NULL';
                        return $pdo->quote((string) $v);
                    }, array_values($rowArr)));

                    fwrite($handle, "INSERT INTO `{$table}` ({$cols}) VALUES ({$vals});\n");
                    $hasRows = true;
                }
            } catch (\Exception $e) {
                fwrite($handle, "-- ERROR reading data from {$table}: " . $e->getMessage() . "\n");
            }

            if ($hasRows) {
                fwrite($handle, "\n");
            }
        }

        fwrite($handle, "SET FOREIGN_KEY_CHECKS=1;\n");
    }

    protected function backupSql(string $mode, array $tables, string $backupDir, string $baseName): string
    {
        $filePath = $backupDir . DIRECTORY_SEPARATOR . $baseName . '.sql';
        $handle   = fopen($filePath, 'wb');
        if (!$handle) {
            throw new \RuntimeException('SQL dosyası oluşturulamadı.');
        }

        try {
            $this->generateSqlDump($mode, $tables, $handle);
        } finally {
            fclose($handle);
        }

        if (!file_exists($filePath) || filesize($filePath) === 0) {
            throw new \RuntimeException('SQL dosyası oluşturulamadı veya boş.');
        }

        return $filePath;
    }

    protected function backupGz(string $mode, array $tables, string $backupDir, string $baseName): string
    {
        $gzPath  = $backupDir . DIRECTORY_SEPARATOR . $baseName . '.sql.gz';
        $tmpPath = $backupDir . DIRECTORY_SEPARATOR . $baseName . '_tmp.sql';

        // Önce geçici SQL dosyasını oluştur
        $handle = fopen($tmpPath, 'wb');
        if (!$handle) {
            throw new \RuntimeException('Geçici SQL dosyası oluşturulamadı.');
        }

        try {
            $this->generateSqlDump($mode, $tables, $handle);
        } catch (\Exception $e) {
            fclose($handle);
            if (file_exists($tmpPath)) @unlink($tmpPath);
            throw $e;
        }
        fclose($handle);

        // Geçici dosyayı gz'ye sıkıştır
        $gz = gzopen($gzPath, 'wb9');
        if (!$gz) {
            if (file_exists($tmpPath)) @unlink($tmpPath);
            throw new \RuntimeException('Gzip dosyası oluşturulamadı.');
        }

        $src = fopen($tmpPath, 'rb');
        if (!$src) {
            gzclose($gz);
            if (file_exists($tmpPath)) @unlink($tmpPath);
            throw new \RuntimeException('Geçici SQL dosyası okunamadı.');
        }

        try {
            while (!feof($src)) {
                gzwrite($gz, fread($src, 65536));
            }
        } finally {
            fclose($src);
            gzclose($gz);
            if (file_exists($tmpPath)) @unlink($tmpPath);
        }

        return $gzPath;
    }

    protected function backupJson(array $tables, string $backupDir, string $baseName): string
    {
        $data = [];
        foreach ($tables as $table) {
            try {
                // chunk(500) ile büyük tablolarda bellek taşması önlenir
                $tableRows = [];
                \Illuminate\Support\Facades\DB::table($table)->orderBy(
                    \Illuminate\Support\Facades\DB::raw('1')
                )->chunk(500, function ($rows) use (&$tableRows) {
                    foreach ($rows as $row) {
                        $tableRows[] = (array) $row;
                    }
                });
                $data[$table] = $tableRows;
            } catch (\Exception $e) {
                $data[$table] = ['error' => $e->getMessage()];
            }
        }

        $filePath = $backupDir . DIRECTORY_SEPARATOR . $baseName . '.json';
        $json     = json_encode([
            'generated_at' => now()->toIso8601String(),
            'database'     => config('database.connections.' . config('database.default') . '.database'),
            'tables'       => $data,
        ], JSON_PRETTY_PRINT | JSON_UNESCAPED_UNICODE);

        if ($json === false) {
            throw new \RuntimeException('JSON encode başarısız: ' . json_last_error_msg());
        }

        $written = file_put_contents($filePath, $json);
        if ($written === false) {
            throw new \RuntimeException('JSON dosyası diske yazılamadı.');
        }

        return $filePath;
    }

    public function maintenanceToggle(Request $request)
    {
        try {
            if (app()->isDownForMaintenance()) {
                Artisan::call('up');
                return back()->with('success', 'Site bakım modundan çıkarıldı.');
            } else {
                // Rastgele, tahmin edilemez bypass secret üret ve DB'ye kaydet
                $secret = \Illuminate\Support\Str::random(32);
                SiteSetting::set('maintenance_bypass_secret', $secret, 'general');
                Artisan::call('down', [
                    '--secret' => $secret,
                ]);
                // Bypass URL flash mesajda gösterilmez — sadece DB'de saklanır
                return back()->with('success', 'Site bakım moduna alındı. Bypass bağlantısı için Ayarlar sayfasını yenileyin.');
            }
        } catch (\Exception $e) {
            return back()->with('error', 'Bakım modu değiştirilemedi: ' . $e->getMessage());
        }
    }

    public function testEmail(Request $request)
    {
        try {
            $user = auth()->user();
            Mail::raw(
                'Bu, Markamama Kariyer yönetim panelinden gönderilen bir test e-postasıdır. ' .
                'E-posta sisteminiz düzgün çalışıyor. Gönderim zamanı: ' . now()->format('d.m.Y H:i:s'),
                function ($message) use ($user) {
                    $message->to($user->email, $user->name)
                            ->subject('[Markamama Kariyer] Test E-postası');
                }
            );
            return back()->with('success', $user->email . ' adresine test e-postası gönderildi.');
        } catch (\Exception $e) {
            return back()->with('error', 'E-posta gönderilemedi: ' . $e->getMessage());
        }
    }

    public function systemInfo()
    {
        return response()->json($this->getSystemInfo());
    }

    protected function getSystemInfo(): array
    {
        $diskTotal = disk_total_space(base_path());
        $diskFree  = disk_free_space(base_path());
        $diskUsed  = $diskTotal - $diskFree;

        return [
            'php_version'     => PHP_VERSION,
            'laravel_version' => app()->version(),
            'environment'     => app()->environment(),
            'debug_mode'      => config('app.debug') ? 'Açık' : 'Kapalı',
            'disk_total'      => $this->formatBytes($diskTotal),
            'disk_free'       => $this->formatBytes($diskFree),
            'disk_used'       => $this->formatBytes($diskUsed),
            'disk_percent'    => $diskTotal > 0 ? round(($diskUsed / $diskTotal) * 100, 1) : 0,
            'queue_driver'    => config('queue.default'),
            'cache_driver'    => config('cache.default'),
            'mail_driver'     => config('mail.default'),
            'timezone'        => config('app.timezone'),
        ];
    }

    protected function formatBytes(int $bytes): string
    {
        if ($bytes >= 1073741824) return round($bytes / 1073741824, 2) . ' GB';
        if ($bytes >= 1048576)   return round($bytes / 1048576, 2) . ' MB';
        if ($bytes >= 1024)      return round($bytes / 1024, 2) . ' KB';
        return $bytes . ' B';
    }

    /**
     * İzin verilen Google Calendar ayar key'leri — whitelist
     */
    protected const ALLOWED_GOOGLE_KEYS = [
        'google_calendar_enabled',
        'google_calendar_client_id',
        'google_calendar_client_secret',
        'google_calendar_id',
    ];

    public function googleSave(Request $request)
    {
        $request->validate([
            'google_calendar_enabled'       => 'nullable|string|max:10',
            'google_calendar_client_id'     => 'nullable|string|max:500',
            'google_calendar_client_secret' => 'nullable|string|max:500',
            'google_calendar_id'            => 'nullable|string|max:500',
        ]);

        foreach (self::ALLOWED_GOOGLE_KEYS as $key) {
            SiteSetting::set($key, $request->input($key, ''), 'google_calendar');
        }

        if ($request->expectsJson()) {
            return response()->json(['ok' => true, 'message' => 'Google Calendar ayarları kaydedildi.']);
        }

        return back()->with('success', 'Google Calendar ayarları kaydedildi.');
    }

    public function googleTest()
    {
        try {
            if (!SiteSetting::get('google_calendar_access_token')) {
                return response()->json([
                    'ok'      => false,
                    'message' => 'Henüz Google hesabı bağlanmamış. Önce "Google ile Bağlan" butonuna tıklayın.',
                ]);
            }

            $token      = SiteSetting::get('google_calendar_access_token');
            $calendarId = SiteSetting::get('google_calendar_id', 'primary');

            $response = \Illuminate\Support\Facades\Http::withToken($token)
                ->get("https://www.googleapis.com/calendar/v3/calendars/{$calendarId}");

            if ($response->successful()) {
                $cal = $response->json();
                return response()->json([
                    'ok'      => true,
                    'message' => 'Bağlantı başarılı! Takvim: ' . ($cal['summary'] ?? $calendarId),
                ]);
            }

            // Token süresi dolmuş olabilir — refresh dene
            if ($response->status() === 401) {
                // Refresh token ile yenile
                $clientId     = SiteSetting::get('google_calendar_client_id');
                $clientSecret = SiteSetting::get('google_calendar_client_secret');
                $refreshToken = SiteSetting::get('google_calendar_refresh_token');

                if ($refreshToken) {
                    $refreshResp = \Illuminate\Support\Facades\Http::post('https://oauth2.googleapis.com/token', [
                        'client_id'     => $clientId,
                        'client_secret' => $clientSecret,
                        'refresh_token' => $refreshToken,
                        'grant_type'    => 'refresh_token',
                    ]);

                    if ($refreshResp->successful()) {
                        $newToken = $refreshResp->json('access_token');
                        SiteSetting::set('google_calendar_access_token', $newToken, 'google_calendar');
                        SiteSetting::set('google_calendar_token_expires_at', (string)(time() + ($refreshResp->json('expires_in') ?? 3600)), 'google_calendar');

                        return response()->json([
                            'ok'      => true,
                            'message' => 'Token yenilendi ve bağlantı başarılı!',
                        ]);
                    }
                }

                return response()->json([
                    'ok'      => false,
                    'message' => 'Token süresi dolmuş ve yenilenemedi. Lütfen tekrar Google ile bağlanın.',
                ]);
            }

            // Diğer API hataları — sadece error.message göster, body gösterme
            $errorMessage = $response->json('error.message');
            return response()->json([
                'ok'      => false,
                'message' => 'API hatası: ' . ($errorMessage ?? 'Bilinmeyen hata (HTTP ' . $response->status() . ')'),
            ]);

        } catch (\Exception $e) {
            Log::error('Google Calendar test hatası', [
                'by_user' => auth()->id(),
                'error'   => $e->getMessage(),
            ]);
            return response()->json([
                'ok'      => false,
                'message' => 'Bağlantı testi sırasında bir hata oluştu.',
            ]);
        }
    }

    public function googleDisconnect()
    {
        $keys = [
            'google_calendar_access_token',
            'google_calendar_refresh_token',
            'google_calendar_token_expires_at',
        ];

        foreach ($keys as $key) {
            SiteSetting::set($key, '', 'google_calendar');
        }

        return back()->with('success', 'Google Calendar bağlantısı kesildi.');
    }

    public function brandingUpdate(Request $request)
    {
        $request->validate([
            'site_logo'    => 'nullable|file|mimes:png,jpg,jpeg,svg,webp|max:2048',
            'site_favicon' => 'nullable|file|mimes:ico,png,jpg,jpeg|max:1024',
        ]);

        // Logo
        if ($request->boolean('delete_site_logo')) {
            $old = SiteSetting::get('site_logo_path');
            if ($old) Storage::disk('public')->delete($old);
            SiteSetting::set('site_logo_path', '', 'general');
        } elseif ($request->hasFile('site_logo')) {
            $old  = SiteSetting::get('site_logo_path');
            $path = $request->file('site_logo')->store('branding', 'public');
            if ($path === false) {
                return back()->with('error', 'Logo kaydedilemedi, lütfen tekrar deneyin.');
            }
            if ($old) Storage::disk('public')->delete($old);
            SiteSetting::set('site_logo_path', $path, 'general');
        }

        // Favicon
        if ($request->boolean('delete_site_favicon')) {
            $old = SiteSetting::get('site_favicon_path');
            if ($old) Storage::disk('public')->delete($old);
            SiteSetting::set('site_favicon_path', '', 'general');
        } elseif ($request->hasFile('site_favicon')) {
            $old  = SiteSetting::get('site_favicon_path');
            $path = $request->file('site_favicon')->store('branding', 'public');
            if ($path === false) {
                return back()->with('error', 'Favicon kaydedilemedi, lütfen tekrar deneyin.');
            }
            if ($old) Storage::disk('public')->delete($old);
            SiteSetting::set('site_favicon_path', $path, 'general');
        }

        return back()->with('success', 'Logo ve favicon güncellendi.');
    }

    protected function resolveGroup(string $key): string
    {
        if (str_starts_with($key, 'virustotal_'))     return 'virustotal';
        if (str_starts_with($key, 'google_calendar_')) return 'google_calendar';
        if (str_starts_with($key, 'whatsapp_'))       return 'whatsapp';
        if (str_starts_with($key, 'banner_'))         return 'banner';
        if (str_starts_with($key, 'kvkk_'))           return 'kvkk';
        return 'general';
    }
}