<?php
/**
 * Antara — path resolution helpers for the auth/storage layer.
 * All sensitive files live under <ANTARA_ROOT>/storage/{secure,config,logs}.
 */

if (!defined('ANTARA_ROOT')) {
    define('ANTARA_ROOT', dirname(__DIR__, 2));
}

// Shared path guards (GRID_ROOT matches project root for this app).
if (!defined('GRID_ROOT')) {
    define('GRID_ROOT', ANTARA_ROOT);
}
require_once ANTARA_ROOT . '/includes/path_security.php';

final class AntaraPaths
{
    public static function root(): string { return ANTARA_ROOT; }
    public static function storage(): string { return ANTARA_ROOT . DIRECTORY_SEPARATOR . 'storage'; }
    public static function secure(): string { return self::storage() . DIRECTORY_SEPARATOR . 'secure'; }
    public static function config(): string { return self::storage() . DIRECTORY_SEPARATOR . 'config'; }
    public static function logs(): string   { return self::storage() . DIRECTORY_SEPARATOR . 'logs'; }

    public static function usersFile(): string         { return self::secure() . DIRECTORY_SEPARATOR . 'users.enc'; }
    public static function credsFile(): string         { return self::secure() . DIRECTORY_SEPARATOR . 'webauthn_credentials.enc'; }
    public static function rateFile(): string          { return self::secure() . DIRECTORY_SEPARATOR . 'rate.enc'; }
    public static function lockFile(string $name): string { return self::secure() . DIRECTORY_SEPARATOR . $name . '.lock'; }
    public static function configFile(): string        { return self::config() . DIRECTORY_SEPARATOR . 'auth_config.enc'; }
    /** First-run installer completed; if present, /install/ is disabled. */
    public static function installLockFile(): string   { return self::config() . DIRECTORY_SEPARATOR . 'install.lock'; }
    /** Operator-created sentinel to allow /install/recover.php after install.lock exists. */
    public static function recoveryAllowFile(): string { return self::config() . DIRECTORY_SEPARATOR . 'recovery.allow'; }
    /** Basename for optional external master.key dir (opaque id file in storage/config). */
    public static function externalSecretsBasenameFile(): string { return self::config() . DIRECTORY_SEPARATOR . 'external_secrets_basename.txt'; }
    public static function logFile(): string           { return self::logs() . DIRECTORY_SEPARATOR . 'security.log'; }

    public static function ensureDirs(): void
    {
        foreach ([self::storage(), self::secure(), self::config(), self::logs()] as $d) {
            if (!is_dir($d)) {
                @mkdir($d, 0700, true);
            }
        }
    }

    /**
     * Ensures a path is inside an allowed root. Defends against directory
     * traversal even if the caller passes user-controlled fragments.
     */
    public static function isInside(string $path, string $root): bool
    {
        $rp = realpath($path);
        $rr = realpath($root);
        if ($rp === false || $rr === false) {
            return false;
        }
        $rp = str_replace('\\', '/', $rp);
        $rr = rtrim(str_replace('\\', '/', $rr), '/');
        if (function_exists('grid_path_is_same_or_under')) {
            return grid_path_is_same_or_under($rp, $rr);
        }
        if (PHP_OS_FAMILY === 'Windows') {
            return stripos($rp, $rr . '/') === 0 || strcasecmp($rp, $rr) === 0;
        }
        return strpos($rp, $rr . '/') === 0 || $rp === $rr;
    }

    /**
     * True if $absolutePath targets a file whose directory resolves under
     * {@see storage()} (used by AntaraCrypto::writeAtomic as traversal guard).
     */
    public static function isStorageManagedFile(string $absolutePath): bool
    {
        if ($absolutePath === '' || strpos($absolutePath, '..') !== false) {
            return false;
        }
        $normalized = str_replace(['\\', '/'], DIRECTORY_SEPARATOR, $absolutePath);
        $storageReal = realpath(self::storage());
        if ($storageReal === false) {
            return false;
        }
        $dir = dirname($normalized);
        $dirReal = realpath($dir);
        if ($dirReal === false) {
            return false;
        }
        $base = basename($normalized);
        if ($base === '' || $base === '.' || $base === '..') {
            return false;
        }
        $full = $dirReal . DIRECTORY_SEPARATOR . $base;
        $fullNorm = str_replace('\\', '/', $full);
        $rootNorm = str_replace('\\', '/', $storageReal);
        if (function_exists('grid_path_is_same_or_under')) {
            return grid_path_is_same_or_under($fullNorm, $rootNorm);
        }
        if (PHP_OS_FAMILY === 'Windows') {
            $p = $rootNorm . '/';
            return stripos($fullNorm, $p) === 0 || strcasecmp($fullNorm, $rootNorm) === 0;
        }
        return strpos($fullNorm, $rootNorm . '/') === 0 || $fullNorm === $rootNorm;
    }
}

AntaraPaths::ensureDirs();
