<?php

declare(strict_types=1);

use App\Core\Env;

return [
    'login_rate_limit' => [
        'max_attempts'   => (int) Env::get('RATE_LIMIT_LOGIN_MAX_ATTEMPTS', 5),
        'decay_minutes'  => (int) Env::get('RATE_LIMIT_LOGIN_DECAY_MINUTES', 15),
    ],

    // General-purpose API/AJAX rate limiting used by RateLimitMiddleware
    // for any route tagged with a limiter name (see routes/web.php).
    'rate_limits' => [
        'default' => ['max_attempts' => 60,  'decay_minutes' => 1],
        'strict'  => ['max_attempts' => 10,  'decay_minutes' => 1],
        'upload'  => ['max_attempts' => 20,  'decay_minutes' => 5],
    ],

    'password' => [
        'min_length'      => (int) Env::get('PASSWORD_MIN_LENGTH', 10),
        'require_upper'   => true,
        'require_lower'   => true,
        'require_number'  => true,
        'require_symbol'  => true,
        // PASSWORD_ARGON2ID falls back to PASSWORD_DEFAULT (bcrypt) if
        // the argon2 extension isn't compiled into the PHP build.
        'hash_algo'       => defined('PASSWORD_ARGON2ID') ? PASSWORD_ARGON2ID : PASSWORD_DEFAULT,
        'hash_options'    => defined('PASSWORD_ARGON2ID')
            ? ['memory_cost' => 65536, 'time_cost' => 4, 'threads' => 2]
            : ['cost' => 12],

        // How many previous passwords (by hash) a user cannot reuse.
        'history_limit' => (int) Env::get('PASSWORD_HISTORY_LIMIT', 5),

        // Password Expiration: null disables expiry entirely. When set,
        // AuthService flags the session as needing a forced change once
        // password_changed_at is older than this many days.
        'expiry_days' => Env::get('PASSWORD_EXPIRY_DAYS', 90) !== null
            ? (int) Env::get('PASSWORD_EXPIRY_DAYS', 90)
            : null,
        'expiry_warning_days' => (int) Env::get('PASSWORD_EXPIRY_WARNING_DAYS', 14),
    ],

    // Account Lock: after this many consecutive failed logins for one
    // account, it is locked for lockout_minutes regardless of what the
    // IP-based login_rate_limit above says — this protects one specific
    // account even if an attacker rotates IPs.
    'account_lockout' => [
        'max_failed_attempts' => (int) Env::get('ACCOUNT_LOCKOUT_MAX_ATTEMPTS', 5),
        'lockout_minutes'     => (int) Env::get('ACCOUNT_LOCKOUT_MINUTES', 30),
    ],

    // Multi-session detection: how many concurrent sessions a user may
    // hold before SessionService flags it (does not force logout by
    // default — see SessionService::activeSessionsFor()).
    'sessions' => [
        'max_concurrent' => (int) Env::get('MAX_CONCURRENT_SESSIONS', 5),
    ],

    // Two-Factor Authentication — foundation is fully built
    // (TwoFactorService: TOTP secret issuance, verification, recovery
    // codes) but not force-enabled for any role by default.
    'two_factor' => [
        'issuer' => Env::get('APP_NAME', 'A&B Hub'),
        'digits' => 6,
        'period_seconds' => 30,
        'recovery_codes_count' => 8,
        'required_for_roles' => [], // e.g. ['operations_admin'] to force it later
    ],

    'csrf' => [
        'token_lifetime_minutes' => (int) Env::get('CSRF_TOKEN_LIFETIME_MINUTES', 120),
        'field_name'             => '_csrf_token',
        'header_name'            => 'X-CSRF-Token',
    ],

    // Cheap defense-in-depth for file uploads (UploadHelper enforces
    // both extension AND real MIME sniffing — this list is the allow-list).
    'uploads' => [
        'max_size_bytes' => 15 * 1024 * 1024, // 15 MB
        'allowed_mime_types' => [
            'image/jpeg', 'image/png', 'image/webp', 'image/gif',
            'application/pdf',
            'application/vnd.openxmlformats-officedocument.wordprocessingml.document',
            'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet',
            'text/csv', 'text/plain',
        ],
    ],
];
