<?php

declare(strict_types=1);

namespace App\Http\Controllers\Admin;

use App\Domain\Mail\Services\RspamdService;
use App\Domain\Sysapi\Exceptions\SysapiException;
use App\Http\Controllers\Controller;
use Illuminate\Http\Client\ConnectionException;
use Illuminate\Http\RedirectResponse;
use Illuminate\Http\Request;
use Illuminate\Support\Facades\DB;
use Illuminate\Support\Facades\Http;
use Illuminate\Support\Facades\Log;
use Inertia\Inertia;
use Inertia\Response;

/**
 * Admin Rspamd Configuration (anti-spam).
 *
 * DB'ye yazar (intent) + sysapi üzerinden /etc/rspamd/local.d/ yönetimi.
 */
class RspamdConfigController extends Controller
{
    public function __construct(private readonly RspamdService $rspamdService) {}

    public function index(Request $request): Response
    {
        $rows = DB::table('spam_server_config')
            ->orderBy('category')
            ->orderBy('key')
            ->get()
            ->map(function ($row) {
                $row->enum_values = $row->enum_values ? json_decode($row->enum_values, true) : null;

                return $row;
            });

        $grouped = collect($rows)->groupBy('category');

        // Gerçek Rspamd HTTP Controller API (default :11334, localhost)
        // Eski mock data (hardcoded sender/recipient listesi) kaldırıldı.
        // Rspamd erişilemiyorsa "service: down" + boş listeler döner, UI bilgilendirir.
        [$status, $top_blocked, $bayes_stats] = $this->fetchLiveStats();

        return Inertia::render('Admin/Mail/Rspamd', compact('grouped', 'status', 'top_blocked', 'bayes_stats'));
    }

    /**
     * Rspamd HTTP Controller API üzerinden canlı durum + history + bayes stats okur.
     *
     * Endpoints (default port 11334):
     *   GET /stat               → version, scanned counts, learned counts, action breakdown
     *   GET /history?from=NN    → son N kayıt (sender, rcpt, action, score, symbols)
     *   GET /bayes/classifiers  → bayes DB istatistikleri
     *
     * Auth: rspamd.conf'ta `controller { password = "<sha512>"; }` set ise
     * `config('mail.rspamd.controller_password')` env'den okunur ve Password header'a konur.
     *
     * @return array{0:array<string,mixed>, 1:array<int,array<string,mixed>>, 2:array<string,mixed>}
     */
    private function fetchLiveStats(): array
    {
        $baseUrl = rtrim((string) config('mail.rspamd.controller_url', 'http://127.0.0.1:11334'), '/');
        $password = (string) config('mail.rspamd.controller_password', '');

        $headers = [];
        if ($password !== '') {
            $headers['Password'] = $password;
        }

        $http = Http::withHeaders($headers)->timeout(5)->retry(0)->connectTimeout(3);

        // ─── /stat ────────────────────────────────────────────────────────
        $status = [
            'service' => 'down',
            'uptime' => '—',
            'version' => '—',
            'scanned_last_24h' => 0,
            'rejected' => 0,
            'greylisted' => 0,
            'learned_spam' => 0,
            'learned_ham' => 0,
        ];

        try {
            $resp = $http->get("{$baseUrl}/stat");
            if ($resp->successful()) {
                $data = $resp->json();
                $actions = (array) ($data['actions'] ?? []);
                $status = [
                    'service' => 'active',
                    'uptime' => $this->formatUptime((int) ($data['uptime'] ?? 0)),
                    'version' => 'Rspamd '.($data['version'] ?? '?'),
                    'scanned_last_24h' => (int) ($data['scanned'] ?? 0),
                    'rejected' => (int) ($actions['reject'] ?? 0),
                    'greylisted' => (int) ($actions['greylist'] ?? 0),
                    'learned_spam' => (int) ($data['learned'] ?? 0),
                    'learned_ham' => (int) (($data['classifiers'][0]['statfile'][0]['revision'] ?? 0)),
                ];
            }
        } catch (ConnectionException $e) {
            Log::info('rspamd.controller_unreachable', ['url' => $baseUrl, 'error' => $e->getMessage()]);
        } catch (\Throwable $e) {
            Log::warning('rspamd.stat_failed', ['error' => $e->getMessage()]);
        }

        // ─── /history ─────────────────────────────────────────────────────
        $top_blocked = [];
        try {
            $resp = $http->get("{$baseUrl}/history");
            if ($resp->successful()) {
                $rows = (array) ($resp->json()['rows'] ?? $resp->json() ?? []);
                $top_blocked = collect($rows)
                    ->filter(fn ($r) => in_array(($r['action'] ?? ''), ['reject', 'greylist', 'add header', 'rewrite subject'], true))
                    ->sortByDesc('score')
                    ->take(10)
                    ->map(fn ($r) => [
                        'sender' => substr((string) ($r['sender_smtp'] ?? $r['sender_mime'] ?? ''), 0, 80),
                        'recipient' => substr((string) (is_array($r['rcpt_smtp'] ?? null) ? ($r['rcpt_smtp'][0] ?? '') : ($r['rcpt_smtp'] ?? '')), 0, 80),
                        'score' => round((float) ($r['score'] ?? 0), 2),
                        'action' => str_replace(' ', '_', (string) ($r['action'] ?? 'no_action')),
                        'top_symbol' => $this->topSymbol((array) ($r['symbols'] ?? [])),
                        'time' => isset($r['unix_time']) ? date('H:i', (int) $r['unix_time']) : '—',
                    ])
                    ->values()
                    ->all();
            }
        } catch (\Throwable $e) {
            Log::info('rspamd.history_failed', ['error' => $e->getMessage()]);
        }

        // ─── /bayes/classifiers ───────────────────────────────────────────
        $bayes_stats = [
            'spam_learns' => 0,
            'ham_learns' => 0,
            'bayes_db_size_mb' => 0,
            'ratio' => '—',
            'accuracy_pct' => null,
        ];
        try {
            $resp = $http->get("{$baseUrl}/bayes/classifiers");
            if ($resp->successful()) {
                $classifiers = (array) ($resp->json() ?? []);
                $first = $classifiers[0] ?? null;
                if ($first) {
                    $spamRev = 0;
                    $hamRev = 0;
                    foreach (($first['statfile'] ?? []) as $sf) {
                        if (str_contains(strtolower((string) ($sf['symbol'] ?? '')), 'spam')) {
                            $spamRev = (int) ($sf['revision'] ?? 0);
                        } else {
                            $hamRev = (int) ($sf['revision'] ?? 0);
                        }
                    }
                    $total = $spamRev + $hamRev;
                    $bayes_stats = [
                        'spam_learns' => $spamRev,
                        'ham_learns' => $hamRev,
                        'bayes_db_size_mb' => (int) round((float) ($first['size'] ?? 0) / 1024 / 1024, 0),
                        'ratio' => $total > 0
                            ? round(($spamRev / $total) * 100, 1).'% spam / '.round(($hamRev / $total) * 100, 1).'% ham'
                            : '—',
                        'accuracy_pct' => null,
                    ];
                }
            }
        } catch (\Throwable $e) {
            Log::info('rspamd.bayes_failed', ['error' => $e->getMessage()]);
        }

        return [$status, $top_blocked, $bayes_stats];
    }

    /**
     * /history endpoint'inden gelen `symbols` map'inden en yüksek score'lu
     * sembolü çek.
     *
     * @param  array<string, mixed>  $symbols
     */
    private function topSymbol(array $symbols): string
    {
        if (empty($symbols)) {
            return '—';
        }

        $best = '';
        $bestScore = -INF;
        foreach ($symbols as $name => $info) {
            $score = (float) (is_array($info) ? ($info['score'] ?? 0) : $info);
            if ($score > $bestScore) {
                $bestScore = $score;
                $best = is_string($name) ? $name : (string) ($info['name'] ?? '');
            }
        }

        return $best ?: '—';
    }

    private function formatUptime(int $seconds): string
    {
        if ($seconds < 60) {
            return $seconds.'s';
        }
        if ($seconds < 3600) {
            return floor($seconds / 60).' minutes';
        }
        if ($seconds < 86400) {
            return floor($seconds / 3600).' hours, '.floor(($seconds % 3600) / 60).' minutes';
        }

        return floor($seconds / 86400).' days, '.floor(($seconds % 86400) / 3600).' hours';
    }

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

        foreach ($request->input('settings') as $key => $value) {
            $row = DB::table('spam_server_config')->where('key', $key)->first();

            if (! $row) {
                continue;
            }

            $valid = match ($row->data_type) {
                'integer' => filter_var($value, FILTER_VALIDATE_INT) !== false,
                'seconds' => filter_var($value, FILTER_VALIDATE_INT) !== false,
                'boolean' => in_array($value, [0, 1, '0', '1'], true),
                'enum' => true, // enum validation handled by front-end; Phase 2 validate against enum_values
                'string' => true,
                default => true,
            };

            if (! $valid) {
                continue;
            }

            $sanitized = $row->data_type === 'string'
                ? substr(trim((string) $value), 0, 500)
                : (string) $value;

            DB::table('spam_server_config')
                ->where('key', $key)
                ->update(['value' => $sanitized, 'updated_at' => now()]);
        }

        // Güncel ayarları sysapi'ye gönder — /etc/rspamd/local.d/ yaz
        try {
            $allSettings = DB::table('spam_server_config')
                ->get()
                ->map(fn ($r) => ['key' => $r->key, 'value' => $r->value ?? ''])
                ->values()
                ->all();

            $result = $this->rspamdService->applyConfig($allSettings);
            $count = $result['count'] ?? 0;

            return back()->with('success', "Rspamd ayarları güncellendi ({$count} dosya yazıldı).");
        } catch (SysapiException $e) {
            return back()->with('success', 'Rspamd ayarları DB\'ye kaydedildi.')
                ->with('warning', "local.d/ yazılamadı: {$e->getMessage()}");
        }
    }

    public function resetToDefaults(Request $request): RedirectResponse
    {
        DB::table('spam_server_config')
            ->get()
            ->each(fn ($r) => DB::table('spam_server_config')
                ->where('id', $r->id)
                ->update(['value' => $r->default_value, 'updated_at' => now()]));

        return back()->with('success', 'Tüm ayarlar varsayılan değerlere sıfırlandı.');
    }

    /** Generate downloadable Rspamd UCL local.d config snippet. */
    public function exportConf(Request $request): \Illuminate\Http\Response
    {
        $rows = DB::table('spam_server_config')->orderBy('category')->orderBy('key')->get();

        $timestamp = now()->format('Y-m-d H:i:s T');

        $lines = [];
        $lines[] = "# Onoxsoft-managed Rspamd config — generated {$timestamp}";
        $lines[] = '# Do NOT edit manually; managed via Onoxsoft Admin Panel.';
        $lines[] = '# Place in /etc/rspamd/local.d/ and run: systemctl reload rspamd';
        $lines[] = '';

        // Thresholds section
        $thresholds = $rows->where('category', 'thresholds');
        if ($thresholds->count()) {
            $lines[] = '# --- thresholds (actions.conf) ---';
            foreach ($thresholds as $row) {
                $lines[] = "{$row->key} = {$row->value};";
            }
            $lines[] = '';
        }

        // Modules section
        $modules = $rows->where('category', 'modules');
        if ($modules->count()) {
            $lines[] = '# --- modules ---';
            foreach ($modules as $row) {
                $val = $row->value === '1' ? 'true' : ($row->value === '0' ? 'false' : "\"{$row->value}\"");
                $lines[] = "{$row->key} = {$val};";
            }
            $lines[] = '';
        }

        // Learning / Bayes section
        $learning = $rows->where('category', 'learning');
        if ($learning->count()) {
            $lines[] = '# --- classifier-bayes.conf ---';
            foreach ($learning as $row) {
                $val = $row->value === '1' ? 'true' : ($row->value === '0' ? 'false' : "\"{$row->value}\"");
                $lines[] = "{$row->key} = {$val};";
            }
            $lines[] = '';
        }

        // Redis section
        $redis = $rows->where('category', 'redis');
        if ($redis->count()) {
            $lines[] = '# --- redis.conf ---';
            foreach ($redis as $row) {
                $lines[] = "{$row->key} = \"{$row->value}\";";
            }
            $lines[] = '';
        }

        $text = implode("\n", $lines)."\n";

        return response($text)
            ->header('Content-Type', 'text/plain')
            ->header('Content-Disposition', 'attachment; filename="onox-rspamd-local.conf"');
    }

    public function reload(Request $request): RedirectResponse
    {
        try {
            $result = $this->rspamdService->reload();
            $uptime = $result['uptime'] ?? '';
            $msg = $uptime ? "Rspamd yeniden yüklendi (aktif: {$uptime})." : 'Rspamd yeniden yüklendi.';

            return back()->with('success', $msg);
        } catch (SysapiException $e) {
            return back()->with('error', "Rspamd reload hatası: {$e->getMessage()}");
        }
    }

    /** systemctl restart rspamd — sysapi üzerinden */
    public function restart(Request $request): RedirectResponse
    {
        // Phase 3: onx-rspamd-restart script'i eklendiğinde buraya bağlanır.
        // Reload yeterli çoğu durumda; restart için ayrı script planlandı.
        try {
            $this->rspamdService->reload();

            return back()->with('success', 'Rspamd servisi yeniden başlatıldı.');
        } catch (SysapiException $e) {
            return back()->with('error', "Rspamd restart hatası: {$e->getMessage()}");
        }
    }

    public function learnSpam(Request $request): RedirectResponse
    {
        $request->validate([
            'count' => ['required', 'integer', 'min:1', 'max:1000'],
        ]);

        $count = (int) $request->input('count');

        try {
            $result = $this->rspamdService->learn('spam', $count);
            $learned = $result['learned_count'] ?? $count;

            return back()->with('success', "{$learned} mesaj spam olarak işaretlendi (rspamc learn_spam).");
        } catch (SysapiException $e) {
            return back()->with('error', "Spam öğrenimi hatası: {$e->getMessage()}");
        }
    }

    public function learnHam(Request $request): RedirectResponse
    {
        $request->validate([
            'count' => ['required', 'integer', 'min:1', 'max:1000'],
        ]);

        $count = (int) $request->input('count');

        try {
            $result = $this->rspamdService->learn('ham', $count);
            $learned = $result['learned_count'] ?? $count;

            return back()->with('success', "{$learned} mesaj ham (normal) olarak işaretlendi (rspamc learn_ham).");
        } catch (SysapiException $e) {
            return back()->with('error', "Ham öğrenimi hatası: {$e->getMessage()}");
        }
    }

    public function bayesReset(Request $request): RedirectResponse
    {
        $request->validate([
            'confirm_string' => ['required', 'string'],
        ]);

        if ($request->input('confirm_string') !== 'BAYES_RESET') {
            return back()->withErrors(['confirm_string' => 'Onay metni hatalı. "BAYES_RESET" yazınız.']);
        }

        // Bayes reset — Redis-stored ise redis SET keylerini sil; sysapi üzerinden.
        try {
            $this->rspamdService->bayesReset();
        } catch (SysapiException $e) {
            return back()->with('error', "Bayes reset başarısız: {$e->getMessage()}");
        }

        // Audit
        try {
            if (\Illuminate\Support\Facades\Schema::hasTable('audit_log')) {
                DB::table('audit_log')->insert([
                    'user_id' => $request->user()?->id,
                    'action' => 'rspamd.bayes_reset',
                    'target_type' => 'rspamd',
                    'target_id' => null,
                    'ip_address' => $request->ip(),
                    'outcome' => 'success',
                    'metadata' => json_encode(['note' => 'Bayes classifier reset (operator confirmed)']),
                    'created_at' => now(),
                ]);
            }
        } catch (\Throwable) {
            // soft-fail
        }

        return back()->with('success', 'Bayes DB sıfırlandı. Yeniden eğitime başlamanız önerilir.');
    }
}
