<?php
namespace EasyChat\Library\Security;

use EasyChat\Library\CentralGatewayClient;
use EasyChat\Model\Setting;








final class AuthRateLimiter {
    private const WINDOW_SECONDS = 900;
    private const MAX_FAILURES = 5;
    private const BLOCK_SECONDS = 900;

    private $db;
    private $prefix;
    private $secret;

    public function __construct($registry) {
        $this->db = $registry->get('db');
        $this->prefix = easychat_db_prefix();
        $this->secret = CentralGatewayClient::ensureInstallSecret(new Setting($registry));
    }

     
    public function status(string $action, int $manager_id, string $ip): array {
        $key = $this->key($action, $manager_id, $ip);
        $query = $this->db->query(
            "SELECT `blocked_until`
             FROM `{$this->prefix}auth_rate_limits`
             WHERE `subject_hash` = '" . $this->db->escape($key) . "'
             LIMIT 1"
        );
        if (!$query || !$query->num_rows || empty($query->row['blocked_until'])) {
            return ['allowed' => true, 'retry_after' => 0];
        }

        $retry_after = max(0, strtotime((string)$query->row['blocked_until']) - time());
        return ['allowed' => $retry_after === 0, 'retry_after' => $retry_after];
    }

    public function recordFailure(string $action, int $manager_id, string $ip): void {
        $key = $this->key($action, $manager_id, $ip);
        $window = self::WINDOW_SECONDS;
        $max = self::MAX_FAILURES;
        $block = self::BLOCK_SECONDS;

        $this->db->query(
            "INSERT INTO `{$this->prefix}auth_rate_limits`
                (`subject_hash`, `action`, `attempts`, `window_started_at`, `blocked_until`, `date_modified`)
             VALUES
                ('" . $this->db->escape($key) . "',
                 '" . $this->db->escape($this->normalizeAction($action)) . "',
                 1, NOW(), NULL, NOW())
             ON DUPLICATE KEY UPDATE
                `blocked_until` = CASE
                    WHEN `window_started_at` < DATE_SUB(NOW(), INTERVAL {$window} SECOND) THEN NULL
                    WHEN `attempts` + 1 >= {$max} THEN DATE_ADD(NOW(), INTERVAL {$block} SECOND)
                    ELSE `blocked_until`
                END,
                `attempts` = IF(`window_started_at` < DATE_SUB(NOW(), INTERVAL {$window} SECOND), 1, `attempts` + 1),
                `window_started_at` = IF(`window_started_at` < DATE_SUB(NOW(), INTERVAL {$window} SECOND), NOW(), `window_started_at`),
                `date_modified` = NOW()"
        );
    }

    public function clear(string $action, int $manager_id, string $ip): void {
        $key = $this->key($action, $manager_id, $ip);
        $this->db->query(
            "DELETE FROM `{$this->prefix}auth_rate_limits`
             WHERE `subject_hash` = '" . $this->db->escape($key) . "'
             LIMIT 1"
        );
    }

    private function key(string $action, int $manager_id, string $ip): string {
        return hash_hmac(
            'sha256',
            $this->normalizeAction($action) . '|' . max(0, $manager_id) . '|' . trim($ip),
            $this->secret
        );
    }

    private function normalizeAction(string $action): string {
        $action = strtolower(preg_replace('/[^a-z0-9_-]/i', '', $action));
        return substr($action ?: 'auth', 0, 32);
    }
}
