<?php
declare(strict_types=1);

namespace App\Controllers;

use App\Core\{Auth, Csrf, Database, Response};
use App\Models\AdminLog;
use App\Models\SecurityScanLog;
use App\Models\Setting;
use App\Services\Security\ZaloroxShield;
use PDO;
use Throwable;

class SecurityBotController extends BaseController
{
    public function scanLink(): Response
    {
        $input = $this->input();

        if (!Auth::check() && !$this->isTestMode()) {
            return Response::json(['ok' => false, 'message' => 'Bu tarama icin giris yapmalisin.'], 401);
        }

        if (!$this->isTestMode() && !$this->csrfIsValid($input)) {
            return Response::json(['ok' => false, 'message' => 'CSRF token mismatch.'], 419);
        }

        $url = $this->trim((string) ($input['url'] ?? ''), 500);
        if ($url === '') {
            return Response::json(['ok' => false, 'message' => 'URL zorunludur.'], 422);
        }

        try {
            $shield = new ZaloroxShield($this->config);
            $result = $shield->scanLink(
                $url,
                Auth::id(),
                $this->trim((string) ($input['target_type'] ?? 'external_link'), 80),
                isset($input['target_id']) ? (int) $input['target_id'] : null
            );

            return Response::json(['ok' => true] + $result);
        } catch (Throwable) {
            return Response::json(['ok' => false, 'message' => 'Guvenlik taramasi su anda tamamlanamadi.'], 500);
        }
    }

    public function logs(): Response
    {
        if ($response = $this->requireAdminJson()) {
            return $response;
        }

        try {
            return Response::json(['ok' => true, 'logs' => (new SecurityScanLog())->latest(100)]);
        } catch (Throwable) {
            return Response::json(['ok' => false, 'message' => 'Loglar okunamadi.'], 500);
        }
    }

    public function settings(): Response
    {
        $input = $this->input();
        if ($response = $this->requireAdminJson()) {
            return $response;
        }

        if (!$this->csrfIsValid($input)) {
            return Response::json(['ok' => false, 'message' => 'CSRF token mismatch.'], 419);
        }

        try {
            $settings = (new Setting())->saveShieldSettings($input);
            (new AdminLog())->write('security.settings.update', 'security', null, ['keys' => array_keys($settings)]);

            return Response::json(['ok' => true, 'settings' => $settings, 'message' => 'Ayarlar kaydedildi.']);
        } catch (Throwable) {
            return Response::json(['ok' => false, 'message' => 'Ayarlar kaydedilemedi.'], 500);
        }
    }

    public function createWhitelist(): Response
    {
        return $this->createDomain('domain_whitelist');
    }

    public function createBlacklist(): Response
    {
        return $this->createDomain('domain_blacklist');
    }

    public function deleteWhitelist(): Response
    {
        return $this->deleteRow('domain_whitelist');
    }

    public function deleteBlacklist(): Response
    {
        return $this->deleteRow('domain_blacklist');
    }

    public function createForbiddenWord(): Response
    {
        $input = $this->input();
        if ($response = $this->requireAdminJson()) {
            return $response;
        }

        if (!$this->csrfIsValid($input)) {
            return Response::json(['ok' => false, 'message' => 'CSRF token mismatch.'], 419);
        }

        $word = $this->trim(strip_tags((string) ($input['word'] ?? '')), 120);
        $severity = min(100, max(0, (int) ($input['severity'] ?? 60)));

        if ($word === '' || strlen($word) < 2) {
            return Response::json(['ok' => false, 'message' => 'Yasakli kelime gecersiz.'], 422);
        }

        try {
            $pdo = Database::connection($this->config);
            $exists = $pdo->prepare('SELECT id FROM forbidden_words WHERE LOWER(word) = LOWER(:word) LIMIT 1');
            $exists->execute(['word' => $word]);

            if ($exists->fetch()) {
                return Response::json(['ok' => true, 'duplicate' => true, 'message' => 'Bu kelime zaten listede.']);
            }

            $statement = $pdo->prepare('INSERT INTO forbidden_words (word, severity, created_at) VALUES (:word, :severity, NOW())');
            $statement->execute(['word' => $word, 'severity' => $severity]);
            (new AdminLog())->write('forbidden_words.create', 'forbidden_word', (int) $pdo->lastInsertId(), ['word' => $word, 'severity' => $severity]);

            return Response::json(['ok' => true, 'message' => 'Yasakli kelime eklendi.']);
        } catch (Throwable) {
            return Response::json(['ok' => false, 'message' => 'Yasakli kelime eklenemedi.'], 500);
        }
    }

    public function deleteForbiddenWord(): Response
    {
        return $this->deleteRow('forbidden_words');
    }

    private function createDomain(string $table): Response
    {
        $input = $this->input();
        if ($response = $this->requireAdminJson()) {
            return $response;
        }

        if (!$this->csrfIsValid($input)) {
            return Response::json(['ok' => false, 'message' => 'CSRF token mismatch.'], 419);
        }

        $table = $this->safeListTable($table);
        $domain = $this->normalizeDomain((string) ($input['domain'] ?? ''));
        $description = $this->trim(strip_tags((string) ($input['description'] ?? '')), 255);

        if ($domain === null) {
            return Response::json(['ok' => false, 'message' => 'Domain gecersiz.'], 422);
        }

        try {
            $pdo = Database::connection($this->config);
            $exists = $pdo->prepare("SELECT id FROM {$table} WHERE domain = :domain LIMIT 1");
            $exists->execute(['domain' => $domain]);

            if ($exists->fetch()) {
                return Response::json(['ok' => true, 'duplicate' => true, 'message' => 'Domain zaten listede.']);
            }

            $statement = $pdo->prepare("INSERT INTO {$table} (domain, description, created_at) VALUES (:domain, :description, NOW())");
            $statement->execute(['domain' => $domain, 'description' => $description ?: null]);
            (new AdminLog())->write($table . '.create', 'domain', null, ['domain' => $domain, 'list' => $table]);

            return Response::json(['ok' => true, 'message' => 'Domain eklendi.']);
        } catch (Throwable) {
            return Response::json(['ok' => false, 'message' => 'Domain eklenemedi.'], 500);
        }
    }

    private function deleteRow(string $table): Response
    {
        $input = $this->input();
        if ($response = $this->requireAdminJson()) {
            return $response;
        }

        if (!$this->csrfIsValid($input)) {
            return Response::json(['ok' => false, 'message' => 'CSRF token mismatch.'], 419);
        }

        $id = (int) ($input['id'] ?? 0);
        if ($id < 1) {
            return Response::json(['ok' => false, 'message' => 'ID gecersiz.'], 422);
        }

        try {
            $table = $this->safeListTable($table);
            $statement = Database::connection($this->config)->prepare("DELETE FROM {$table} WHERE id = :id LIMIT 1");
            $statement->bindValue('id', $id, PDO::PARAM_INT);
            $statement->execute();
            (new AdminLog())->write($table . '.delete', 'security_list', $id, ['table' => $table]);

            return Response::json(['ok' => true, 'message' => 'Kayit silindi.']);
        } catch (Throwable) {
            return Response::json(['ok' => false, 'message' => 'Kayit silinemedi.'], 500);
        }
    }

    private function requireAdminJson(): ?Response
    {
        if (!Auth::check() || !Auth::role('admin')) {
            return Response::json(['ok' => false, 'message' => 'Bu islem icin admin yetkisi gerekir.'], 403);
        }

        return null;
    }

    private function csrfIsValid(array $input): bool
    {
        $token = array_key_exists('_csrf', $input) ? (string) $input['_csrf'] : null;

        return Csrf::verify($token);
    }

    private function input(): array
    {
        $input = $_POST;
        $raw = file_get_contents('php://input');

        if (is_string($raw) && $raw !== '') {
            $decoded = json_decode($raw, true);
            if (is_array($decoded)) {
                $input = array_merge($input, $decoded);
            }
        }

        return $input;
    }

    private function normalizeDomain(string $value): ?string
    {
        $value = strtolower(trim($value));
        if ($value === '') {
            return null;
        }

        if (filter_var($value, FILTER_VALIDATE_URL)) {
            $value = (string) parse_url($value, PHP_URL_HOST);
        } else {
            $value = preg_replace('~^https?://~', '', $value) ?: $value;
            $value = explode('/', $value)[0] ?? $value;
        }

        $value = trim($value, " \t\n\r\0\x0B.");

        return preg_match('/^(?:[a-z0-9](?:[a-z0-9-]{0,61}[a-z0-9])?\.)+[a-z0-9-]{2,}$/', $value) === 1
            ? $value
            : null;
    }

    private function trim(string $value, int $limit): string
    {
        $value = trim(preg_replace('/\s+/', ' ', $value) ?: '');

        return function_exists('mb_substr') ? mb_substr($value, 0, $limit) : substr($value, 0, $limit);
    }

    private function safeListTable(string $table): string
    {
        return match ($table) {
            'domain_whitelist' => 'domain_whitelist',
            'domain_blacklist' => 'domain_blacklist',
            'forbidden_words' => 'forbidden_words',
            default => throw new \InvalidArgumentException('Invalid table.'),
        };
    }

    private function isTestMode(): bool
    {
        return (bool) ($this->config['SECURITY_BOT_TEST_MODE'] ?? false)
            || (bool) ($this->config['FEATURE_TEST_MODE'] ?? false)
            || (bool) ($this->config['PAYMENT_TEST_MODE'] ?? false)
            || (string) ($this->config['APP_ENV'] ?? 'production') === 'local';
    }
}
