<?php

declare(strict_types=1);

namespace App\Http\Controllers\Customer;

use App\Domain\Account\Models\Account;
use App\Domain\Domain\Models\Domain;
use App\Domain\Security\ModSecurity\Exceptions\ModSecurityException;
use App\Domain\Security\ModSecurity\Services\ModSecurityProvisioner;
use App\Http\Controllers\Controller;
use App\Http\Controllers\Customer\Concerns\RebuildsHtaccess;
use Illuminate\Http\RedirectResponse;
use Illuminate\Http\Request;
use Illuminate\Support\Facades\DB;
use Inertia\Inertia;
use Inertia\Response;

/**
 * Customer ModSecurity per-site (cPanel ModSecurity Tools muadili).
 * Phase 1: DB + UI; Phase 1.5'ta sysapi('htaccess-modsec-write', ...) ile
 * .htaccess'e SecRuleEngine/SecRuleRemoveById direktifleri yazılır.
 */
class ModSecurityController extends Controller
{
    use RebuildsHtaccess;

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

        $domainRows = Domain::where('account_id', $account->id)
            ->orderBy('name')
            ->get();

        $settings = DB::table('modsecurity_site_settings')
            ->where('account_id', $account->id)
            ->get()
            ->keyBy('domain_id');

        $domains = $domainRows->map(function (Domain $d) use ($settings) {
            $row = $settings->get($d->id);

            return [
                'id' => $d->id,
                'name' => $d->name,
                'enabled' => $row ? (bool) $row->enabled : true,
                'exceptions' => $row ? json_decode($row->exceptions ?? '[]', true) ?? [] : [],
                'hit_counter_24h' => $row ? (int) $row->hit_counter_24h : 0,
                'last_hit_at' => $row ? $row->last_hit_at : null,
            ];
        })->values();

        // Recent hits — modsec_audit.log'tan müşterinin kendi domain'leri için filtrelenmiş.
        // Eski mock URIs/reasons/random IP üretimi kaldırıldı. Audit log erişilemiyorsa boş.
        $domainNames = $domainRows->pluck('name')->toArray();
        $recent_hits = $this->fetchRecentHitsForDomains($domainNames);

        return Inertia::render('Customer/Security/ModSecurity', compact('domains', 'recent_hits'));
    }

    /**
     * Müşterinin kendi domain'leri için modsec_audit.log'tan son 24 saatlik
     * tetiklenen kuralları çek. Admin tarafındaki modsec-audit-search sysapi
     * action'ını kullanır, sonuçları sadece izin verilen domain'lere kırpar.
     *
     * @param  array<int, string>  $allowedDomains
     * @return array<int, array<string, mixed>>
     */
    private function fetchRecentHitsForDomains(array $allowedDomains): array
    {
        if (empty($allowedDomains)) {
            return [];
        }

        try {
            /** @var \App\Domain\Sysapi\Contracts\SysapiBridge $sysapi */
            $sysapi = app(\App\Domain\Sysapi\Contracts\SysapiBridge::class);
            $result = $sysapi->run('modsec-audit-search', [
                'limit' => 100,
                'since_hours' => 24,
            ], timeoutSeconds: 10);

            if ($result->failed()) {
                return [];
            }

            $events = $result->data['events'] ?? [];

            // Ownership filter — müşterinin kendi domain'lerine ait olayları al
            $allowedLower = array_map('strtolower', $allowedDomains);

            $hits = collect($events)
                ->filter(fn ($e) => isset($e['domain_name'])
                    && in_array(strtolower((string) $e['domain_name']), $allowedLower, true))
                ->map(fn ($e) => [
                    'rule_id' => (string) ($e['rule_id'] ?? '000000'),
                    'ip' => (string) ($e['source_ip'] ?? ''),
                    'uri' => (string) ($e['uri'] ?? '/'),
                    'reason' => (string) ($e['reason'] ?? ($e['rule_tag'] ?? 'unknown')),
                    'domain' => (string) ($e['domain_name'] ?? ''),
                    'hit_at' => (string) ($e['event_at'] ?? now()->toIso8601String()),
                ])
                ->take(20)
                ->values()
                ->all();

            return $hits;
        } catch (\Throwable $e) {
            \Illuminate\Support\Facades\Log::info('customer.modsec.audit_failed', ['error' => $e->getMessage()]);
            return [];
        }
    }

    public function toggleDomain(Request $request, int $domainId, ModSecurityProvisioner $modsec): RedirectResponse
    {
        /** @var Account $account */
        $account = $request->attributes->get('customer_account');

        $domain = Domain::where('id', $domainId)
            ->where('account_id', $account->id)
            ->firstOrFail();

        $current = DB::table('modsecurity_site_settings')
            ->where('account_id', $account->id)
            ->where('domain_id', $domain->id)
            ->first();

        $newEnabled = ! ($current->enabled ?? true);

        DB::table('modsecurity_site_settings')->updateOrInsert(
            ['account_id' => $account->id, 'domain_id' => $domain->id],
            [
                'enabled' => $newEnabled,
                'updated_at' => now(),
                'created_at' => now(),
            ]
        );

        $this->rebuildHtaccessForDomain($domain);

        try {
            $modsec->rebuildForDomain($domain);
        } catch (ModSecurityException $e) {
            return back()->withErrors(['modsecurity' => $e->getMessage()]);
        }

        $label = $newEnabled ? 'acildi' : 'kapatildi';

        return back()->with('success', "ModSecurity {$label}: {$domain->name}");
    }

    public function addException(Request $request, int $domainId, ModSecurityProvisioner $modsec): RedirectResponse
    {
        /** @var Account $account */
        $account = $request->attributes->get('customer_account');

        $validated = $request->validate([
            'rule_id' => ['required', 'regex:/^\d{6}$/'],
            'reason' => ['nullable', 'string', 'max:200'],
        ]);

        $domain = Domain::where('id', $domainId)
            ->where('account_id', $account->id)
            ->firstOrFail();

        $row = DB::table('modsecurity_site_settings')
            ->where('account_id', $account->id)
            ->where('domain_id', $domain->id)
            ->first();

        $exceptions = $row ? (json_decode($row->exceptions ?? '[]', true) ?? []) : [];
        $exceptions[] = [
            'rule_id' => $validated['rule_id'],
            'reason' => $validated['reason'] ?? '',
            'added_at' => now()->toIso8601String(),
        ];

        DB::table('modsecurity_site_settings')->updateOrInsert(
            ['account_id' => $account->id, 'domain_id' => $domain->id],
            [
                'exceptions' => json_encode($exceptions),
                'updated_at' => now(),
                'created_at' => now(),
            ]
        );

        $this->rebuildHtaccessForDomain($domain);

        try {
            $modsec->rebuildForDomain($domain);
        } catch (ModSecurityException $e) {
            return back()->withErrors(['modsecurity' => $e->getMessage()]);
        }

        $ruleId = $validated['rule_id'];

        return back()->with('success', "Kural {$ruleId} bu site icin devre disi.");
    }

    public function removeException(Request $request, int $domainId, string $ruleId, ModSecurityProvisioner $modsec): RedirectResponse
    {
        /** @var Account $account */
        $account = $request->attributes->get('customer_account');

        $domain = Domain::where('id', $domainId)
            ->where('account_id', $account->id)
            ->firstOrFail();

        $row = DB::table('modsecurity_site_settings')
            ->where('account_id', $account->id)
            ->where('domain_id', $domain->id)
            ->first();

        $exceptions = $row ? (json_decode($row->exceptions ?? '[]', true) ?? []) : [];
        $exceptions = array_values(array_filter($exceptions, fn ($e) => $e['rule_id'] !== $ruleId));

        DB::table('modsecurity_site_settings')->updateOrInsert(
            ['account_id' => $account->id, 'domain_id' => $domain->id],
            [
                'exceptions' => json_encode($exceptions),
                'updated_at' => now(),
                'created_at' => now(),
            ]
        );

        $this->rebuildHtaccessForDomain($domain);

        try {
            $modsec->rebuildForDomain($domain);
        } catch (ModSecurityException $e) {
            return back()->withErrors(['modsecurity' => $e->getMessage()]);
        }

        return back()->with('success', "Kural {$ruleId} yeniden aktif.");
    }
}
