<?php

declare(strict_types=1);

namespace App\Http\Controllers\Admin;

use App\Domain\Domain\Models\Domain;
use App\Domain\Email\Services\DeliverabilityChecker;
use App\Domain\MailDns\Services\MailDnsAutoFixer;
use App\Http\Controllers\Controller;
use Illuminate\Http\JsonResponse;
use Illuminate\Http\RedirectResponse;
use Illuminate\Http\Request;
use Illuminate\Support\Facades\Cache;
use Illuminate\Support\Facades\DB;
use Inertia\Inertia;
use Inertia\Response;

/**
 * Admin Email Deliverability Dashboard — WHM "Email Deliverability" muadili (Phase 2).
 *
 * Customer-side EmailDeliverabilityController müşterinin kendi domain'leri için zaten var.
 * Bu admin tarafı cross-account dashboard: TÜM hesapların TÜM domain'lerini tek tabloda gösterir.
 *
 * Özellikler:
 *  - SPF/DKIM/DMARC/MX/PTR/RBL kontrol (DeliverabilityChecker — dns_get_record bazlı)
 *  - Per-domain auto-fix (MailDnsAutoFixer — eksik kayıtları panel DNS'ine ekle)
 *  - Bulk auto-fix (tüm broken domain'lere uygula)
 *  - Search + filter (low score / missing SPF / missing DKIM / RBL listed)
 *  - Server PTR + RBL durumu üst banner'da
 */
class EmailDeliverabilityController extends Controller
{
    public function __construct(
        private readonly DeliverabilityChecker $checker,
        private readonly MailDnsAutoFixer $autoFixer,
    ) {}

    public function index(Request $request): Response
    {
        // Tüm domain'leri çek (account ile join)
        $domains = DB::table('domains as d')
            ->join('accounts as a', 'a.id', '=', 'd.account_id')
            ->select(
                'd.id', 'd.name', 'd.account_id', 'd.type',
                'a.username', 'a.primary_domain', 'a.status as account_status',
            )
            ->whereNull('d.deleted_at')
            ->whereIn('d.type', ['main', 'addon'])  // subdomain'leri atla
            ->orderBy('a.username')
            ->orderBy('d.name')
            ->get();

        // Per-domain checks — basit format (detay sayfasında full sonuç)
        $rows = $domains->map(function ($d): array {
            $checks = $this->checker->check($d->name);
            return [
                'id' => $d->id,
                'name' => $d->name,
                'account_id' => $d->account_id,
                'username' => $d->username,
                'primary_domain' => $d->primary_domain,
                'account_status' => $d->account_status,
                'type' => $d->type,
                'spf' => $this->normaliseStatus($checks['spf']),
                'dkim' => $this->normaliseStatus($checks['dkim']),
                'dmarc' => $this->normaliseStatus($checks['dmarc']),
                'mx' => $this->normaliseStatus($checks['mx']),
                'overall_score' => $checks['overall_score'],
                'severity' => $this->severity($checks['overall_score']),
            ];
        });

        // Server-level checks (PTR + RBL) — bir kez yapılır, üst banner'da gösterilir
        $ptr = $this->checker->checkPtr();
        $rbl = $this->checker->checkRbl();

        // Filter
        $search = trim((string) $request->input('search', ''));
        if ($search !== '') {
            $rows = $rows->filter(fn ($r) =>
                str_contains(strtolower($r['name']), strtolower($search))
                || str_contains(strtolower($r['username'] ?? ''), strtolower($search))
            );
        }

        $statusFilter = (string) $request->input('status', 'all');
        if ($statusFilter !== 'all') {
            $rows = $rows->filter(function ($r) use ($statusFilter) {
                return match ($statusFilter) {
                    'critical' => $r['overall_score'] < 40,
                    'warning' => $r['overall_score'] >= 40 && $r['overall_score'] < 75,
                    'good' => $r['overall_score'] >= 75,
                    'missing_spf' => $r['spf'] === 'missing',
                    'missing_dkim' => $r['dkim'] === 'missing',
                    'missing_dmarc' => $r['dmarc'] === 'missing',
                    'missing_mx' => $r['mx'] === 'missing',
                    default => true,
                };
            });
        }

        $summary = [
            'total' => $domains->count(),
            'critical' => $rows->where('severity', 'critical')->count(),
            'warning' => $rows->where('severity', 'warning')->count(),
            'good' => $rows->where('severity', 'good')->count(),
            'missing_spf' => $rows->where('spf', 'missing')->count(),
            'missing_dkim' => $rows->where('dkim', 'missing')->count(),
            'missing_dmarc' => $rows->where('dmarc', 'missing')->count(),
            'missing_mx' => $rows->where('mx', 'missing')->count(),
            'avg_score' => $rows->count() > 0 ? (int) round($rows->avg('overall_score')) : 0,
        ];

        return Inertia::render('Admin/Mail/Deliverability', [
            'domains' => $rows->values(),
            'summary' => $summary,
            'server' => [
                'ipv4' => $ptr['server_ip'],
                'ptr' => $ptr,
                'rbl' => $rbl,
            ],
            'filters' => [
                'search' => $search,
                'status' => $statusFilter,
            ],
        ]);
    }

    public function show(Request $request, int $domainId): Response
    {
        $domain = Domain::with('account:id,username,primary_domain')
            ->findOrFail($domainId);

        $checks = $this->checker->check($domain->name);
        $ptr = $this->checker->checkPtr();
        $rbl = $this->checker->checkRbl();

        $mailHost = (string) (config('onoxsoft.mail.smtp_hostname')
            ?? config('onoxsoft.hostname')
            ?? 'mail.'.$domain->name);

        return Inertia::render('Admin/Mail/DeliverabilityShow', [
            'domain' => [
                'id' => $domain->id,
                'name' => $domain->name,
                'username' => $domain->account?->username,
            ],
            'checks' => $checks,
            'server' => [
                'ptr' => $ptr,
                'rbl' => $rbl,
            ],
            'recommended' => [
                'mail_host' => $mailHost,
                'spf' => 'v=spf1 a mx ip4:' . ($ptr['server_ip'] ?? '0.0.0.0') . ' ~all',
                'dmarc' => 'v=DMARC1; p=quarantine; rua=mailto:postmaster@' . $domain->name . '; pct=100',
            ],
        ]);
    }

    /**
     * Cache temizle + tekrar kontrol et (admin "Yenile" tıklayınca).
     */
    public function refresh(Request $request): RedirectResponse
    {
        $domain = (string) $request->input('domain', '');
        if ($domain !== '') {
            Cache::forget("deliverability:{$domain}");
        } else {
            // Tüm domain'lerin cache'ini sil
            $allDomains = DB::table('domains')->whereNull('deleted_at')->pluck('name');
            foreach ($allDomains as $d) {
                Cache::forget("deliverability:{$d}");
            }
        }

        return back()->with('success', $domain ? "{$domain} cache yenilendi." : 'Tüm cache yenilendi.');
    }

    /**
     * Bir domain için eksik SPF/DKIM/DMARC/MX kayıtlarını panel DNS zone'una otomatik ekle.
     */
    public function autoFix(Request $request, int $domainId): RedirectResponse
    {
        $domain = Domain::findOrFail($domainId);

        $mailHost = (string) (config('onoxsoft.mail.smtp_hostname')
            ?? config('onoxsoft.hostname')
            ?? 'mail.'.$domain->name);

        $result = $this->autoFixer->fixAll($domain->name, $mailHost);

        // Cache'i invalidate et — yeniden check'te yeni kayıtlar görünsün
        Cache::forget("deliverability:{$domain->name}");

        // Audit
        try {
            if (\Illuminate\Support\Facades\Schema::hasTable('audit_log')) {
                DB::table('audit_log')->insert([
                    'user_id' => $request->user()?->id,
                    'action' => 'mail.deliverability_auto_fix',
                    'target_type' => 'domain',
                    'target_id' => $domain->id,
                    'ip_address' => $request->ip(),
                    'outcome' => $result['ok'] ? 'success' : 'partial',
                    'metadata' => json_encode([
                        'domain' => $domain->name,
                        'fixed' => $result['fixed'],
                        'skipped' => $result['skipped'],
                        'errors' => $result['errors'],
                    ], JSON_UNESCAPED_UNICODE),
                    'created_at' => now(),
                ]);
            }
        } catch (\Throwable) {}

        if (! $result['ok']) {
            return back()->with('warning',
                "{$domain->name}: " . count($result['fixed']) . ' eklendi, ' .
                count($result['errors']) . ' hata — ' . implode('; ', $result['errors'])
            );
        }

        $fixedList = implode(', ', $result['fixed']) ?: 'hiçbir şey';
        return back()->with('success',
            "{$domain->name}: {$fixedList} eklendi. DNS propagation 5-30 dk."
        );
    }

    /**
     * Toplu auto-fix — tüm "missing SPF/DKIM/DMARC" domain'lere uygula.
     */
    public function bulkAutoFix(Request $request): RedirectResponse
    {
        $confirm = $request->input('confirm_string', '');
        if ($confirm !== 'TUMUNE UYGULA') {
            return back()->with('error', 'Onay metni "TUMUNE UYGULA" olmalı.');
        }

        $mailHost = (string) (config('onoxsoft.mail.smtp_hostname')
            ?? config('onoxsoft.hostname')
            ?? 'mail.example.com');

        $domains = DB::table('domains')
            ->whereNull('deleted_at')
            ->whereIn('type', ['main', 'addon'])
            ->get(['id', 'name']);

        $applied = 0;
        $totalFixed = 0;
        $failed = 0;
        $errors = [];

        foreach ($domains as $d) {
            $checks = $this->checker->check($d->name);
            // Sadece eksik kayıtlardan en az biri varsa fix'le
            if ($checks['spf']['found'] && $checks['dkim']['found'] && $checks['dmarc']['found'] && $checks['mx']['found']) {
                continue;
            }

            try {
                $result = $this->autoFixer->fixAll($d->name, $mailHost);
                $applied++;
                $totalFixed += count($result['fixed']);
                if (! $result['ok']) {
                    $errors = array_merge($errors, array_map(fn ($e) => "{$d->name}: $e", $result['errors']));
                }
                Cache::forget("deliverability:{$d->name}");
            } catch (\Throwable $e) {
                $failed++;
                $errors[] = "{$d->name}: ".$e->getMessage();
            }
        }

        $msg = "{$applied} domain işlendi, toplam {$totalFixed} kayıt eklendi.";
        if ($failed > 0 || ! empty($errors)) {
            $msg .= " {$failed} hata: ".implode('; ', array_slice($errors, 0, 3));
            if (count($errors) > 3) $msg .= ' (+'.(count($errors) - 3).' more)';
        }

        return back()->with(empty($errors) ? 'success' : 'warning', $msg);
    }

    private function normaliseStatus(array $check): string
    {
        if (! ($check['found'] ?? false)) return 'missing';
        return ($check['valid'] ?? false) ? 'ok' : 'invalid';
    }

    private function severity(int $score): string
    {
        if ($score < 40) return 'critical';
        if ($score < 75) return 'warning';
        return 'good';
    }
}
