<?php

declare(strict_types=1);

namespace App\Core;

/**
 * Session
 *
 * Wraps PHP's native session handling with secure defaults applied
 * BEFORE session_start() is ever called, plus flash-message and
 * session-fixation protections that a raw $_SESSION never gives you.
 */
final class Session
{
    private static bool $started = false;

    public static function start(array $config): void
    {
        if (self::$started || session_status() === PHP_SESSION_ACTIVE) {
            self::$started = true;
            return;
        }

        session_name($config['cookie']['name']);

        session_set_cookie_params([
            'lifetime' => $config['lifetime_minutes'] * 60,
            'path'     => $config['cookie']['path'],
            'domain'   => $config['cookie']['domain'],
            'secure'   => $config['cookie']['secure'],
            'httponly' => $config['cookie']['httponly'],
            'samesite' => $config['cookie']['samesite'],
        ]);

        if ($config['driver'] === 'database') {
            session_set_save_handler(new DatabaseSessionHandler($config['lifetime_minutes']), true);
        } else {
            $dir = $config['files_path'];
            if (!is_dir($dir)) {
                @mkdir($dir, 0770, true);
            }
            session_save_path($dir);
        }

        // Hardening flags independent of session_set_cookie_params().
        ini_set('session.use_strict_mode', '1');
        ini_set('session.use_only_cookies', '1');
        ini_set('session.gc_maxlifetime', (string) ($config['lifetime_minutes'] * 60));

        session_start();
        self::$started = true;

        self::enforceIdleTimeout($config['lifetime_minutes']);
        self::rotateFlash();
    }

    private static function enforceIdleTimeout(int $lifetimeMinutes): void
    {
        $now = time();
        $last = $_SESSION['_last_activity'] ?? $now;

        if (($now - $last) > ($lifetimeMinutes * 60)) {
            self::destroy();
            session_start();
        }

        $_SESSION['_last_activity'] = $now;
    }

    /** Call immediately after a successful login to defeat session fixation. */
    public static function regenerate(): void
    {
        session_regenerate_id(true);
    }

    public static function destroy(): void
    {
        $_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();
    }

    public static function get(string $key, mixed $default = null): mixed
    {
        return $_SESSION[$key] ?? $default;
    }

    public static function set(string $key, mixed $value): void
    {
        $_SESSION[$key] = $value;
    }

    public static function has(string $key): bool
    {
        return isset($_SESSION[$key]);
    }

    public static function remove(string $key): void
    {
        unset($_SESSION[$key]);
    }

    /** Flash data survives exactly one subsequent request (e.g. "Lead created"). */
    public static function flash(string $key, mixed $value): void
    {
        $_SESSION['_flash_new'][$key] = $value;
    }

    public static function getFlash(string $key, mixed $default = null): mixed
    {
        return $_SESSION['_flash_old'][$key] ?? $default;
    }

    private static function rotateFlash(): void
    {
        $_SESSION['_flash_old'] = $_SESSION['_flash_new'] ?? [];
        $_SESSION['_flash_new'] = [];
    }
}
