<?php
// file init.php
// Inisialisasi sesi dan fungsi keamanan umum.

// --- Session hardening ----------------------------------------------------------------
// Pastikan sesi menggunakan cookie saja dan (jika HTTPS) cookie secure.
ini_set('session.use_strict_mode', 1);
ini_set('session.use_only_cookies', 1);
ini_set('session.cookie_httponly', 1);

$secureCookie = (!empty($_SERVER['HTTPS']) && $_SERVER['HTTPS'] !== 'off')
    || (!empty($_SERVER['HTTP_X_FORWARDED_PROTO']) && $_SERVER['HTTP_X_FORWARDED_PROTO'] === 'https');
if ($secureCookie) {
    ini_set('session.cookie_secure', 1);
}

session_name('tqm_session');
if (session_status() !== PHP_SESSION_ACTIVE) {
    session_start();
}

// Session timeout (in detik)
$sessionTimeout = 1800; // 30 menit
if (isset($_SESSION['last_activity']) && (time() - $_SESSION['last_activity']) > $sessionTimeout) {
    // Jika sudah terlalu lama, matikan session lalu mulai ulang.
    $_SESSION = [];
    if (ini_get('session.use_cookies')) {
        $params = session_get_cookie_params();
        setcookie(session_name(), '', time() - 42000,
            $params['path'], $params['domain'],
            $params['secure'], $params['httponly']
        );
    }
    session_destroy();
    session_start();
}
$_SESSION['last_activity'] = time();

// --- CSRF token ------------------------------------------------------------------------
function csrf_token(): string
{
    if (empty($_SESSION['csrf_token'])) {
        $_SESSION['csrf_token'] = bin2hex(random_bytes(24));
    }
    return $_SESSION['csrf_token'];
}

function verify_csrf_token(?string $token): bool
{
    if (empty($token) || empty($_SESSION['csrf_token'])) {
        return false;
    }
    return hash_equals($_SESSION['csrf_token'], $token);
}

// --- Rate limiting login --------------------------------------------------------------
function login_is_locked(): bool
{
    $maxAttempts = 5;
    $lockoutSeconds = 300; // 5 menit

    if (empty($_SESSION['login_attempts'])) {
        return false;
    }

    $attempts = (int)$_SESSION['login_attempts'];
    $last = (int)($_SESSION['login_last_time'] ?? 0);

    if ($attempts >= $maxAttempts && (time() - $last) < $lockoutSeconds) {
        return true;
    }

    return false;
}

function login_register_attempt(): void
{
    $now = time();
    $last = (int)($_SESSION['login_last_time'] ?? 0);

    if ($last === 0 || ($now - $last) > 300) {
        $_SESSION['login_attempts'] = 1;
    } else {
        $_SESSION['login_attempts'] = (int)($_SESSION['login_attempts'] ?? 0) + 1;
    }

    $_SESSION['login_last_time'] = $now;
}

function login_reset_attempts(): void
{
    unset($_SESSION['login_attempts'], $_SESSION['login_last_time']);
}

// --- Helpers ---------------------------------------------------------------------------
function h(string $value): string
{
    return htmlspecialchars($value, ENT_QUOTES, 'UTF-8');
}

function require_login(): void
{
    if (empty($_SESSION['user_id'])) {
        header('Location: login.php');
        exit;
    }
}

function require_admin(): void
{
    if (empty($_SESSION['user_id']) || ($_SESSION['role'] ?? '') !== 'admin') {
        header('Location: login.php');
        exit;
    }
}

function uploads_dir(): string
{
    $dir = __DIR__ . '/uploads';
    if (!is_dir($dir)) {
        mkdir($dir, 0755, true);
    }
    return $dir;
}

function upload_image(array $file): array
{
    if (empty($file['name']) || $file['error'] === UPLOAD_ERR_NO_FILE) {
        return ['ok' => true, 'path' => null];
    }
    if ($file['error'] !== UPLOAD_ERR_OK) {
        $errCodes = [
            UPLOAD_ERR_INI_SIZE => 'Ukuran file melebihi upload_max_filesize di server.',
            UPLOAD_ERR_FORM_SIZE => 'Ukuran file melebihi batas form.',
            UPLOAD_ERR_PARTIAL => 'File hanya terupload sebagian.',
            UPLOAD_ERR_NO_FILE => 'Tidak ada file yang diupload.',
            UPLOAD_ERR_NO_TMP_DIR => 'Folder tmp hilang.',
            UPLOAD_ERR_CANT_WRITE => 'Gagal menulis ke disk.',
            UPLOAD_ERR_EXTENSION => 'Upload dihentikan ekstensi PHP.',
        ];
        $errMsg = $errCodes[$file['error']] ?? 'Terjadi kesalahan upload gambar (kode '.$file['error'].')';
        return ['ok' => false, 'error' => $errMsg];
    }

    // Validasi mime dan benar-benar gambar
    $imageInfo = @getimagesize($file['tmp_name']);
    if ($imageInfo === false) {
        return ['ok' => false, 'error' => 'File bukan gambar valid.'];
    }
    $mime = $imageInfo['mime'] ?? '';
    $allowedMime = ['image/jpeg', 'image/png', 'image/gif', 'image/webp'];
    if (!in_array($mime, $allowedMime, true)) {
        $msg = 'Format gambar tidak didukung. Hanya JPG, PNG, GIF, WebP yang didukung.';
        if (stripos($mime, 'heic') !== false || stripos($file['name'], '.heic') !== false) {
            $msg .= ' Gambar HEIC/HEIF dari iPhone belum didukung. Silakan konversi ke JPG/PNG.';
        } else {
            $msg .= ' (Tipe file: ' . h($mime) . ')';
        }
        return ['ok' => false, 'error' => $msg];
    }

    $maxSize = 20 * 1024 * 1024; // 20MB
    if ($file['size'] > $maxSize) {
        return ['ok' => false, 'error' => 'Ukuran file melebihi 20MB. Silakan kompres manual.'];
    }

    // Kompresi otomatis ke max 200kb
    $maxFinalSize = 200 * 1024; // 200kb
    $ext = pathinfo($file['name'], PATHINFO_EXTENSION);
    $ext = $ext ? strtolower($ext) : 'jpg';
    $safeName = time() . '_' . bin2hex(random_bytes(8)) . '.' . $ext;
    $target = uploads_dir() . '/' . $safeName;

    // Gunakan GD untuk resize/kompres
    $srcType = $imageInfo[2];
    $srcImg = false;
    switch ($srcType) {
        case IMAGETYPE_JPEG:
            $srcImg = @imagecreatefromjpeg($file['tmp_name']);
            break;
        case IMAGETYPE_PNG:
            $srcImg = @imagecreatefrompng($file['tmp_name']);
            break;
        case IMAGETYPE_GIF:
            $srcImg = @imagecreatefromgif($file['tmp_name']);
            break;
        case IMAGETYPE_WEBP:
            if (function_exists('imagecreatefromwebp')) {
                $srcImg = @imagecreatefromwebp($file['tmp_name']);
            }
            break;
    }
    if (!$srcImg) {
        return ['ok' => false, 'error' => 'Gagal membaca gambar.'];
    }

    $width = imagesx($srcImg);
    $height = imagesy($srcImg);
    // Resize jika terlalu besar (maks 2000px)
    $maxDim = 2000;
    if ($width > $maxDim || $height > $maxDim) {
        if ($width > $height) {
            $newWidth = $maxDim;
            $newHeight = intval($height * $maxDim / $width);
        } else {
            $newHeight = $maxDim;
            $newWidth = intval($width * $maxDim / $height);
        }
        $resized = imagecreatetruecolor($newWidth, $newHeight);
        imagecopyresampled($resized, $srcImg, 0, 0, 0, 0, $newWidth, $newHeight, $width, $height);
        imagedestroy($srcImg);
        $srcImg = $resized;
        $width = $newWidth;
        $height = $newHeight;
    }

    // Simpan dengan kompresi, ulangi jika >200kb
    $ok = false;
    $tryQuality = [85, 70, 60, 50, 40, 30];
    foreach ($tryQuality as $q) {
        ob_start();
        if ($srcType === IMAGETYPE_JPEG || $srcType === IMAGETYPE_WEBP) {
            if ($srcType === IMAGETYPE_JPEG) {
                imagejpeg($srcImg, null, $q);
            } else {
                imagewebp($srcImg, null, $q);
            }
        } elseif ($srcType === IMAGETYPE_PNG) {
            imagepng($srcImg, null, 9 - min(9, intval($q/10)));
        } elseif ($srcType === IMAGETYPE_GIF) {
            imagegif($srcImg);
        }
        $imgData = ob_get_clean();
        if ($imgData && strlen($imgData) <= $maxFinalSize) {
            file_put_contents($target, $imgData);
            $ok = true;
            break;
        }
        $lastImgData = $imgData;
    }
    if (!$ok && !empty($lastImgData)) {
        file_put_contents($target, $lastImgData); // simpan versi terkecil
        $ok = true;
    }
    imagedestroy($srcImg);
    if (!$ok) {
        return ['ok' => false, 'error' => 'Gagal kompres gambar.'];
    }
    return ['ok' => true, 'path' => 'uploads/' . $safeName];
}

function redirect(string $url): void
{
    header('Location: ' . $url);
    exit;
}
