<?php
// includes/logger.php
declare(strict_types=1);

/**
 * Єдиний універсальний логер подій.
 * Зберігає message + технічні поля (action_code, url, method, user_agent, ip).
 * Вміє “сам лікувати” таблицю user_logs, якщо в БД стара схема.
 */

function ej__table_exists(mysqli $conn, string $table): bool {
    $t = $conn->real_escape_string($table);
    $res = $conn->query("SHOW TABLES LIKE '{$t}'");
    if (!$res) return false;
    $ok = (bool)$res->fetch_row();
    $res->free();
    return $ok;
}

function ej__column_info(mysqli $conn, string $table, string $column): ?array {
    $t = $conn->real_escape_string($table);
    $c = $conn->real_escape_string($column);
    $res = $conn->query("SHOW COLUMNS FROM `{$t}` LIKE '{$c}'");
    if (!$res) return null;
    $row = $res->fetch_assoc() ?: null;
    $res->free();
    return $row;
}

function ej__column_exists(mysqli $conn, string $table, string $column): bool {
    return ej__column_info($conn, $table, $column) !== null;
}

function ej__index_exists(mysqli $conn, string $table, string $indexName): bool {
    $t = $conn->real_escape_string($table);
    $idx = $conn->real_escape_string($indexName);
    $res = $conn->query("SHOW INDEX FROM `{$t}` WHERE Key_name = '{$idx}'");
    if (!$res) return false;
    $ok = (bool)$res->fetch_row();
    $res->free();
    return $ok;
}

function ej__try_query(mysqli $conn, string $sql): void {
    // Не валимо сторінки, але залишаємо слід у error_log якщо щось не так
    if (!$conn->query($sql)) {
        error_log("[ej_logger] SQL failed: {$sql} | err=" . $conn->error);
    }
}

function ej_ensure_user_logs_table(mysqli $conn): void {
    // 1) Створити “нову” правильну таблицю (якщо її немає)
    if (!ej__table_exists($conn, 'user_logs')) {
        $sql = "
            CREATE TABLE IF NOT EXISTS `user_logs` (
                `id` BIGINT UNSIGNED NOT NULL AUTO_INCREMENT,
                `user_id` INT UNSIGNED NOT NULL,
                `role` VARCHAR(32) NOT NULL,
                `action_code` VARCHAR(64) NOT NULL,
                `message` VARCHAR(500) NOT NULL,
                `url` VARCHAR(255) NULL DEFAULT NULL,
                `method` VARCHAR(10) NULL DEFAULT NULL,
                `ip` VARBINARY(16) NULL DEFAULT NULL,
                `user_agent` VARCHAR(255) NULL DEFAULT NULL,
                `created_at` DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP,
                PRIMARY KEY (`id`),
                KEY `idx_created_at` (`created_at`),
                KEY `idx_user_time` (`user_id`, `created_at`),
                KEY `idx_action_time` (`action_code`, `created_at`)
            ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;
        ";
        ej__try_query($conn, $sql);
        return;
    }

    // 2) Якщо таблиця є — міграція зі старих схем

    // 2.1) Якщо було поле `action`, а `action_code` немає — перейменувати
    if (!ej__column_exists($conn, 'user_logs', 'action_code') && ej__column_exists($conn, 'user_logs', 'action')) {
        ej__try_query($conn, "ALTER TABLE `user_logs` CHANGE `action` `action_code` VARCHAR(64) NOT NULL");
    }

    // 2.2) Якщо нема action_code взагалі — додати
    if (!ej__column_exists($conn, 'user_logs', 'action_code')) {
        ej__try_query($conn, "ALTER TABLE `user_logs` ADD COLUMN `action_code` VARCHAR(64) NOT NULL AFTER `role`");
    }

    // 2.3) role: якщо ENUM або інший тип — зробити VARCHAR(32), щоб не валилось на нових ролях
    $roleInfo = ej__column_info($conn, 'user_logs', 'role');
    if ($roleInfo && isset($roleInfo['Type'])) {
        $type = strtolower((string)$roleInfo['Type']);
        if (str_starts_with($type, 'enum(') || str_starts_with($type, 'set(')) {
            ej__try_query($conn, "ALTER TABLE `user_logs` MODIFY `role` VARCHAR(32) NOT NULL");
        }
    } else {
        // якщо role відсутній (екзотика) — додамо
        if (!ej__column_exists($conn, 'user_logs', 'role')) {
            ej__try_query($conn, "ALTER TABLE `user_logs` ADD COLUMN `role` VARCHAR(32) NOT NULL AFTER `user_id`");
        }
    }

    // 2.4) Інші колонки — додати якщо відсутні
    if (!ej__column_exists($conn, 'user_logs', 'message')) {
        ej__try_query($conn, "ALTER TABLE `user_logs` ADD COLUMN `message` VARCHAR(500) NOT NULL AFTER `action_code`");
    }
    if (!ej__column_exists($conn, 'user_logs', 'url')) {
        ej__try_query($conn, "ALTER TABLE `user_logs` ADD COLUMN `url` VARCHAR(255) NULL DEFAULT NULL AFTER `message`");
    }
    if (!ej__column_exists($conn, 'user_logs', 'method')) {
        ej__try_query($conn, "ALTER TABLE `user_logs` ADD COLUMN `method` VARCHAR(10) NULL DEFAULT NULL AFTER `url`");
    }
    if (!ej__column_exists($conn, 'user_logs', 'ip')) {
        ej__try_query($conn, "ALTER TABLE `user_logs` ADD COLUMN `ip` VARBINARY(16) NULL DEFAULT NULL AFTER `method`");
    }
    if (!ej__column_exists($conn, 'user_logs', 'user_agent')) {
        ej__try_query($conn, "ALTER TABLE `user_logs` ADD COLUMN `user_agent` VARCHAR(255) NULL DEFAULT NULL AFTER `ip`");
    }
    if (!ej__column_exists($conn, 'user_logs', 'created_at')) {
        ej__try_query($conn, "ALTER TABLE `user_logs` ADD COLUMN `created_at` DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP AFTER `user_agent`");
    }

    // 2.5) Індекси — якщо відсутні
    if (!ej__index_exists($conn, 'user_logs', 'idx_created_at')) {
        ej__try_query($conn, "ALTER TABLE `user_logs` ADD KEY `idx_created_at` (`created_at`)");
    }
    if (!ej__index_exists($conn, 'user_logs', 'idx_user_time')) {
        ej__try_query($conn, "ALTER TABLE `user_logs` ADD KEY `idx_user_time` (`user_id`, `created_at`)");
    }
    if (!ej__index_exists($conn, 'user_logs', 'idx_action_time')) {
        ej__try_query($conn, "ALTER TABLE `user_logs` ADD KEY `idx_action_time` (`action_code`, `created_at`)");
    }
}

function ej_client_ip(): string {
    $candidates = [
        $_SERVER['HTTP_X_FORWARDED_FOR'] ?? '',
        $_SERVER['HTTP_CLIENT_IP'] ?? '',
        $_SERVER['REMOTE_ADDR'] ?? '',
    ];
    foreach ($candidates as $raw) {
        if (!$raw) continue;
        foreach (explode(',', $raw) as $p) {
            $ip = trim($p);
            if (filter_var($ip, FILTER_VALIDATE_IP, FILTER_FLAG_IPV4 | FILTER_FLAG_IPV6)) {
                return $ip;
            }
        }
    }
    return '0.0.0.0';
}

function ej_request_url(): string {
    $uri = (string)($_SERVER['REQUEST_URI'] ?? '');
    if ($uri === '') return '';
    return mb_substr($uri, 0, 255);
}

function ej_log_action(mysqli $conn, int $user_id, string $role, string $action_code, string $message): void {
    ej_ensure_user_logs_table($conn);

    // Безпечні ліміти під поля таблиці
    $role = mb_substr((string)$role, 0, 32);
    $action_code = mb_substr((string)$action_code, 0, 64);
    $message = mb_substr((string)$message, 0, 500);

    $ua = mb_substr((string)($_SERVER['HTTP_USER_AGENT'] ?? ''), 0, 255);
    $ip = ej_client_ip();
    $url = ej_request_url();
    $method = mb_substr((string)($_SERVER['REQUEST_METHOD'] ?? ''), 0, 10);

    // На всякий випадок: якщо в таблиці раптом все ще не action_code (дуже старий варіант)
    $actionCol = 'action_code';
    if (!ej__column_exists($conn, 'user_logs', 'action_code') && ej__column_exists($conn, 'user_logs', 'action')) {
        $actionCol = 'action';
    }

    $sql = "INSERT INTO `user_logs`
            (`user_id`,`role`,`{$actionCol}` ,`message`,`url`,`method`,`ip`,`user_agent`,`created_at`)
            VALUES (?,?,?,?,?,?,INET6_ATON(?),?,NOW())";

    $stmt = $conn->prepare($sql);
    if (!$stmt) {
        error_log("[ej_logger] prepare failed: " . $conn->error . " | sql=" . $sql);
        return;
    }

    $stmt->bind_param(
        "isssssss",
        $user_id,
        $role,
        $action_code,
        $message,
        $url,
        $method,
        $ip,
        $ua
    );

    if (!$stmt->execute()) {
        error_log("[ej_logger] execute failed: " . $stmt->error);
    }

    $stmt->close();
}
