<?php
declare(strict_types=1);

require_once __DIR__ . '/config.php';
require_once __DIR__ . '/functions.php';

function admin_auth_db(): ?mysqli
{
    global $conn;
    return $conn instanceof mysqli ? $conn : null;
}

function admin_client_ip(): string
{
    $keys = ['HTTP_CF_CONNECTING_IP', 'HTTP_X_FORWARDED_FOR', 'REMOTE_ADDR'];
    foreach ($keys as $key) {
        $raw = trim((string)($_SERVER[$key] ?? ''));
        if ($raw === '') {
            continue;
        }
        if ($key === 'HTTP_X_FORWARDED_FOR') {
            $raw = trim(explode(',', $raw)[0] ?? '');
        }
        if ($raw !== '') {
            return substr($raw, 0, 64);
        }
    }
    return '';
}

function admin_setting_value(string $key, string $default = ''): string
{
    $db = admin_auth_db();
    if (!$db) {
        return $default;
    }
    $stmt = $db->prepare('SELECT setting_value FROM settings WHERE setting_key = ? LIMIT 1');
    if (!$stmt) {
        return $default;
    }
    $stmt->bind_param('s', $key);
    $stmt->execute();
    $row = $stmt->get_result()->fetch_assoc() ?: null;
    $stmt->close();
    return $row ? (string)($row['setting_value'] ?? $default) : $default;
}

function admin_setting_enabled(string $key, bool $default = false): bool
{
    $value = strtolower(trim(admin_setting_value($key, $default ? '1' : '0')));
    return in_array($value, ['1', 'true', 'yes', 'on'], true);
}

function admin_session_timeout_seconds(): int
{
    $minutes = max(1, (int)admin_setting_value('admin_session_timeout_minutes', '120'));
    return $minutes * 60;
}

function admin_security_column_exists(mysqli $db, string $table, string $column): bool
{
    $database = (string)($db->query('SELECT DATABASE() AS db')->fetch_assoc()['db'] ?? '');
    if ($database === '') {
        return false;
    }
    $stmt = $db->prepare('SELECT 1 FROM INFORMATION_SCHEMA.COLUMNS WHERE TABLE_SCHEMA = ? AND TABLE_NAME = ? AND COLUMN_NAME = ? LIMIT 1');
    if (!$stmt) {
        return false;
    }
    $stmt->bind_param('sss', $database, $table, $column);
    $stmt->execute();
    $exists = (bool)$stmt->get_result()->fetch_row();
    $stmt->close();
    return $exists;
}

function admin_security_ensure_schema(): void
{
    static $done = false;
    if ($done) {
        return;
    }
    $done = true;
    $db = admin_auth_db();
    if (!$db) {
        return;
    }
    @ $db->query("CREATE TABLE IF NOT EXISTS admin_login_attempts (
        id INT UNSIGNED NOT NULL AUTO_INCREMENT PRIMARY KEY,
        username VARCHAR(120) NOT NULL,
        ip_address VARCHAR(64) NOT NULL DEFAULT '',
        attempts INT NOT NULL DEFAULT 0,
        locked_until DATETIME NULL,
        last_error VARCHAR(255) NULL,
        last_attempt_at DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP,
        created_at DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP,
        updated_at DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
        UNIQUE KEY uq_admin_login_attempts (username, ip_address)
    ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci");
    @ $db->query("CREATE TABLE IF NOT EXISTS admin_activity_logs (
        id INT UNSIGNED NOT NULL AUTO_INCREMENT PRIMARY KEY,
        admin_id INT UNSIGNED NULL,
        event_key VARCHAR(100) NOT NULL DEFAULT 'admin.action',
        action_label VARCHAR(190) NOT NULL DEFAULT '',
        target_type VARCHAR(80) NOT NULL DEFAULT '',
        target_id VARCHAR(80) NOT NULL DEFAULT '',
        description TEXT NULL,
        ip_address VARCHAR(64) NOT NULL DEFAULT '',
        payload_json LONGTEXT NULL,
        created_at DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP,
        KEY idx_admin_activity_admin (admin_id),
        KEY idx_admin_activity_event (event_key),
        KEY idx_admin_activity_created (created_at)
    ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci");
    if (!admin_security_column_exists($db, 'admins', 'last_login_ip')) {
        @ $db->query("ALTER TABLE admins ADD COLUMN last_login_ip VARCHAR(64) NULL AFTER last_login_at");
    }
    if (!admin_security_column_exists($db, 'admins', 'last_seen_ip')) {
        @ $db->query("ALTER TABLE admins ADD COLUMN last_seen_ip VARCHAR(64) NULL AFTER last_login_ip");
    }
}

function admin_log_activity(string $eventKey, string $actionLabel, string $description = '', string $targetType = '', int|string $targetId = 0, array $payload = [], bool $force = false): bool
{
    admin_security_ensure_schema();
    $db = admin_auth_db();
    if (!$db || !$force && !admin_setting_enabled('admin_log_changes_enabled', false)) {
        return false;
    }
    $adminId = current_admin_id() > 0 ? current_admin_id() : null;
    $ip = admin_setting_enabled('admin_track_ip_enabled', true) ? admin_client_ip() : '';
    $payloadJson = $payload ? json_encode($payload, JSON_UNESCAPED_UNICODE | JSON_UNESCAPED_SLASHES) : null;
    $targetIdText = $targetId === 0 ? '' : (string)$targetId;
    $stmt = $db->prepare('INSERT INTO admin_activity_logs (admin_id, event_key, action_label, target_type, target_id, description, ip_address, payload_json, created_at) VALUES (?, ?, ?, ?, ?, ?, ?, ?, NOW())');
    if (!$stmt) {
        return false;
    }
    $stmt->bind_param('isssssss', $adminId, $eventKey, $actionLabel, $targetType, $targetIdText, $description, $ip, $payloadJson);
    $ok = $stmt->execute();
    $stmt->close();
    return $ok;
}

function admin_update_last_seen(): void
{
    if (!is_admin_logged_in()) {
        return;
    }
    $_SESSION['admin_last_activity_at'] = time();
    if (!admin_setting_enabled('admin_track_ip_enabled', true)) {
        return;
    }
    $db = admin_auth_db();
    if (!$db) {
        return;
    }
    $adminId = current_admin_id();
    $ip = admin_client_ip();
    if ($adminId <= 0 || $ip === '') {
        return;
    }
    $stmt = $db->prepare('UPDATE admins SET last_seen_ip = ? WHERE id = ? LIMIT 1');
    if ($stmt) {
        $stmt->bind_param('si', $ip, $adminId);
        $stmt->execute();
        $stmt->close();
    }
}

function admin_login_lock_meta(string $username): array
{
    admin_security_ensure_schema();
    $db = admin_auth_db();
    $username = trim($username);
    if (!$db || $username === '') {
        return ['locked' => false, 'remaining_seconds' => 0, 'attempts' => 0, 'max_attempts' => max(1, (int)admin_setting_value('admin_max_login_attempts', '5'))];
    }
    $ip = admin_client_ip();
    $stmt = $db->prepare('SELECT attempts, locked_until FROM admin_login_attempts WHERE username = ? AND ip_address = ? LIMIT 1');
    if (!$stmt) {
        return ['locked' => false, 'remaining_seconds' => 0, 'attempts' => 0, 'max_attempts' => max(1, (int)admin_setting_value('admin_max_login_attempts', '5'))];
    }
    $stmt->bind_param('ss', $username, $ip);
    $stmt->execute();
    $row = $stmt->get_result()->fetch_assoc() ?: null;
    $stmt->close();
    $remaining = 0;
    if (!empty($row['locked_until'])) {
        $remaining = max(0, strtotime((string)$row['locked_until']) - time());
    }
    return [
        'locked' => $remaining > 0,
        'remaining_seconds' => $remaining,
        'attempts' => (int)($row['attempts'] ?? 0),
        'max_attempts' => max(1, (int)admin_setting_value('admin_max_login_attempts', '5')),
    ];
}

function admin_register_login_failure(string $username, string $reason = ''): void
{
    admin_security_ensure_schema();
    $db = admin_auth_db();
    $username = trim($username);
    if (!$db || $username === '') {
        return;
    }
    $ip = admin_client_ip();
    $maxAttempts = max(1, (int)admin_setting_value('admin_max_login_attempts', '5'));
    $lockMinutes = max(1, (int)admin_setting_value('admin_lockout_minutes', '15'));
    $stmt = $db->prepare('SELECT id, attempts FROM admin_login_attempts WHERE username = ? AND ip_address = ? LIMIT 1');
    if (!$stmt) {
        return;
    }
    $stmt->bind_param('ss', $username, $ip);
    $stmt->execute();
    $row = $stmt->get_result()->fetch_assoc() ?: null;
    $stmt->close();
    $attempts = ((int)($row['attempts'] ?? 0)) + 1;
    $lockedUntil = $attempts >= $maxAttempts ? date('Y-m-d H:i:s', time() + ($lockMinutes * 60)) : null;
    if ($row) {
        $update = $db->prepare('UPDATE admin_login_attempts SET attempts = ?, locked_until = ?, last_error = ?, last_attempt_at = NOW(), updated_at = NOW() WHERE id = ?');
        if ($update) {
            $id = (int)$row['id'];
            $update->bind_param('issi', $attempts, $lockedUntil, $reason, $id);
            $update->execute();
            $update->close();
        }
    } else {
        $insert = $db->prepare('INSERT INTO admin_login_attempts (username, ip_address, attempts, locked_until, last_error, last_attempt_at, created_at, updated_at) VALUES (?, ?, ?, ?, ?, NOW(), NOW(), NOW())');
        if ($insert) {
            $insert->bind_param('ssiss', $username, $ip, $attempts, $lockedUntil, $reason);
            $insert->execute();
            $insert->close();
        }
    }
    admin_log_activity('admin.login_failed', 'محاولة دخول فاشلة', 'فشل تسجيل دخول الأدمن: ' . $username, 'admin_auth', 0, ['username' => $username, 'reason' => $reason], true);
}

function admin_clear_login_failures(string $username): void
{
    admin_security_ensure_schema();
    $db = admin_auth_db();
    $username = trim($username);
    if (!$db || $username === '') {
        return;
    }
    $ip = admin_client_ip();
    $stmt = $db->prepare('DELETE FROM admin_login_attempts WHERE username = ? AND ip_address = ?');
    if ($stmt) {
        $stmt->bind_param('ss', $username, $ip);
        $stmt->execute();
        $stmt->close();
    }
}

function is_admin_logged_in(): bool
{
    if (!isset($_SESSION['admin_id']) || (int)$_SESSION['admin_id'] <= 0) {
        return false;
    }
    $lastActivity = (int)($_SESSION['admin_last_activity_at'] ?? 0);
    $timeout = admin_session_timeout_seconds();
    if ($lastActivity > 0 && (time() - $lastActivity) > $timeout) {
        logout_admin(false);
        return false;
    }
    return true;
}

function require_admin_login(): void
{
    if (!is_admin_logged_in()) {
        set_flash('error', 'يرجى تسجيل الدخول أولاً.');
        redirect_to(admin_url('admins/login.php'));
    }
    admin_update_last_seen();
}

function current_admin_id(): int
{
    return isset($_SESSION['admin_id']) ? (int)$_SESSION['admin_id'] : 0;
}

function current_admin_name(): string
{
    return $_SESSION['admin_name'] ?? '';
}

function current_admin_username(): string
{
    return $_SESSION['admin_username'] ?? '';
}

function current_admin_role(): string
{
    return $_SESSION['admin_role'] ?? '';
}


function admin_normalize_role(string $role): string
{
    $role = strtolower(trim($role));
    return in_array($role, ['super_admin', 'admin', 'staff'], true) ? $role : 'staff';
}

function admin_role_allowed(array $allowedRoles): bool
{
    $role = admin_normalize_role(current_admin_role());
    return in_array($role, $allowedRoles, true);
}

function admin_required_roles_for_path(string $path): array
{
    $path = strtolower(str_replace('\\', '/', $path));

    if (str_contains($path, '/app/admin/admins/') && !str_contains($path, '/app/admin/admins/login.php') && !str_contains($path, '/app/admin/admins/logout.php')) {
        return ['super_admin'];
    }

    if (str_contains($path, '/app/influencer/')) {
        return ['super_admin', 'admin'];
    }

    $adminOnlyModules = [
        '/app/admin/settings/',
        '/app/admin/themes/',
        '/app/admin/activity_logs/',
        '/app/admin/app_home/',
        '/app/admin/marketing_engine/',
        '/app/admin/notifications/',
        '/app/admin/loyalty/',
        '/app/admin/wallet/',
        '/app/admin/referrals/',
        '/app/admin/customer_tiers/',
        '/app/admin/influencers/',
        '/app/admin/branches/',
        '/app/admin/payment_methods/',
        '/app/admin/order_addons/',
        '/app/admin/threshold_promotions/',
        '/app/admin/promo_codes/',
        '/app/admin/offers/',
    ];

    foreach ($adminOnlyModules as $modulePath) {
        if (str_contains($path, $modulePath)) {
            return ['super_admin', 'admin'];
        }
    }

    return ['super_admin', 'admin', 'staff'];
}

function admin_user_can_access_path(string $path): bool
{
    return admin_role_allowed(admin_required_roles_for_path($path));
}

function admin_enforce_path_access(string $path): void
{
    if (admin_user_can_access_path($path)) {
        return;
    }
    admin_log_activity(
        'admin.access_denied',
        'منع وصول إداري',
        'تم منع وصول حساب إداري إلى صفحة غير مسموحة.',
        'admin_path',
        0,
        ['path' => $path, 'role' => current_admin_role()],
        true
    );
    set_flash('error', 'ليست لديك صلاحية كافية لفتح هذه الصفحة.');
    redirect_to(admin_url('index.php'));
}

function logout_admin(bool $logActivity = true): void
{
    if ($logActivity && current_admin_id() > 0) {
        admin_log_activity('admin.logout', 'تسجيل خروج الأدمن', 'تم تسجيل الخروج.', 'admin', current_admin_id(), ['username' => current_admin_username()], true);
    }
    unset($_SESSION['admin_id'], $_SESSION['admin_name'], $_SESSION['admin_username'], $_SESSION['admin_role'], $_SESSION['admin_last_activity_at']);
    session_regenerate_id(true);
}

if (!defined('NAY_ADMIN_AUTH_BOOTSTRAPPED')) {
    define('NAY_ADMIN_AUTH_BOOTSTRAPPED', true);
    admin_security_ensure_schema();

    if (PHP_SAPI !== 'cli') {
        $scriptName = str_replace('\\', '/', (string)($_SERVER['SCRIPT_NAME'] ?? ''));
        $authExempt = [
            '/app/admin/admins/login.php',
            '/app/admin/admins/logout.php',
        ];

        if ($scriptName !== '' && str_contains($scriptName, '/app/admin/') && !in_array($scriptName, $authExempt, true)) {
            require_admin_login();
            admin_enforce_path_access($scriptName);
        }
    }
}
