<?php
/*
 * PuqHosting custom FileGator configuration.
 *
 * Storage: FTP to 127.0.0.1:21. Per-request adapter reads credentials from
 * the session (set by PuqHostingAuth on SSO activation). NO `root` pinned;
 * vsftpd chroots each unix account to /home/<user>/ and the FTP login lands
 * the user there directly. They see web/, conf/, mail/, … and navigate to
 * the right domain themselves.
 *
 * Auth: PuqHostingAuth — no local users.json. SSO handshake from WHMCS
 * with HMAC + IP allow-list.
 *
 * Placeholders {{HMAC_SECRET}} and {{ALLOWED_IPS}} are substituted by the
 * driver at install time.
 */

return [
    'public_path' => APP_PUBLIC_PATH,
    'public_dir'  => APP_PUBLIC_DIR,
    'overwrite_on_upload' => false,
    'timezone' => 'UTC',
    'download_inline' => ['pdf'],

    'frontend_config' => [
        'app_name' => 'File Manager',
        'app_version' => APP_VERSION,
        'language' => 'english',
        'logo' => 'https://filegator.io/filegator_logo.svg',
        'upload_max_size' => 256 * 1024 * 1024,  // 256 MB
        'upload_chunk_size' => 2 * 1024 * 1024,  // 2 MB
        'upload_simultaneous' => 3,
        'default_archive_name' => 'archive.zip',
        'editable' => ['.txt', '.css', '.js', '.ts', '.html', '.htm', '.php', '.json', '.md', '.xml', '.yaml', '.yml', '.ini', '.env', '.htaccess'],
        'date_format' => 'YYYY-MM-DD HH:mm:ss',
        'guest_redirection' => '',
        'search_simultaneous' => 5,
        'filter_entries' => [],
    ],

    'services' => [
        'Filegator\Services\Logger\LoggerInterface' => [
            'handler' => '\Filegator\Services\Logger\Adapters\MonoLogger',
            'config' => [
                'monolog_handlers' => [
                    function () {
                        return new \Monolog\Handler\StreamHandler(
                            __DIR__ . '/private/logs/app.log',
                            \Monolog\Logger::INFO
                        );
                    },
                ],
            ],
        ],
        'Filegator\Services\Session\SessionStorageInterface' => [
            'handler' => '\Filegator\Services\Session\Adapters\SessionStorage',
            'config' => [
                'handler' => function () {
                    $handler = new \Symfony\Component\HttpFoundation\Session\Storage\Handler\NativeFileSessionHandler(
                        __DIR__ . '/private/sessions'
                    );
                    return new \Symfony\Component\HttpFoundation\Session\Storage\NativeSessionStorage([
                        'cookie_samesite' => 'Lax',
                        'cookie_secure'   => null,   // browser detects
                        'cookie_httponly' => true,
                    ], $handler);
                },
            ],
        ],
        'Filegator\Services\Cors\Cors' => [
            'handler' => '\Filegator\Services\Cors\Cors',
            'config' => ['enabled' => false],   // strict same-origin in prod
        ],
        'Filegator\Services\Tmpfs\TmpfsInterface' => [
            'handler' => '\Filegator\Services\Tmpfs\Adapters\Tmpfs',
            'config' => [
                'path' => __DIR__ . '/private/tmp/',
                'gc_probability_perc' => 10,
                'gc_older_than' => 60 * 60 * 24 * 2,
            ],
        ],
        'Filegator\Services\Security\Security' => [
            'handler' => '\Filegator\Services\Security\Security',
            'config' => [
                'csrf_protection' => true,
                'csrf_key' => '{{CSRF_KEY}}',   // 16-char random, set at install
                'ip_allowlist' => [],
                'ip_denylist' => [],
                'allow_insecure_overlays' => false,
            ],
        ],
        'Filegator\Services\View\ViewInterface' => [
            'handler' => '\Filegator\Services\View\Adapters\Vuejs',
            'config' => [
                // Hide filegator's user-profile (change-password) link —
                // customers don't manage filegator credentials directly,
                // they auth via WHMCS SSO. Replace with a "Back" button
                // that closes the tab (SSO opens in window.open new tab).
                'add_to_head' => '<style>'
                    /* === Lockdown CSS === */
                    /* Hide profile (change-password) link. */
                    . '.navbar-item.profile{display:none !important;}'
                    /* Hide the standard login form and its container — direct
                       login is disabled, only WHMCS SSO is allowed. */
                    . '.login,.login-form,form[name="login"],.navbar-item.login{display:none !important;}'
                    /* Our custom blocks */
                    . '.puq-back-link{cursor:pointer;color:#3273dc;font-weight:600;}'
                    . '.puq-back-link:hover{color:#363636;text-decoration:none;}'
                    /* "Use WHMCS" overlay for guests who land here directly. */
                    . '.puq-noaccess{position:fixed;inset:0;background:#fff;z-index:9999;'
                    .   'display:flex;align-items:center;justify-content:center;'
                    .   'flex-direction:column;text-align:center;padding:40px;'
                    .   'font-family:-apple-system,BlinkMacSystemFont,Segoe UI,Roboto,sans-serif;}'
                    . '.puq-noaccess h2{color:#363636;margin:0 0 12px;font-size:20px;}'
                    . '.puq-noaccess p{color:#7a7a7a;font-size:14px;max-width:520px;margin:0 0 18px;line-height:1.5;}'
                    . '.puq-noaccess a{background:#3273dc;color:#fff;padding:10px 20px;border-radius:4px;'
                    .   'text-decoration:none;font-weight:600;font-size:14px;}'
                    . '.puq-noaccess a:hover{background:#276cda;color:#fff;}'
                    . '</style>'
                    . '<script>'
                    . '(function(){'
                    . '/* Read cookies the auth adapter stashed during SSO consume. */'
                    . 'function getCookie(name){'
                    . '  var m=document.cookie.match(new RegExp("(?:^|; )"+name+"=([^;]*)"));'
                    . '  return m?decodeURIComponent(m[1]):"";'
                    . '}'
                    . 'var backUrl=getCookie("puq_back_url");'
                    . ''
                    . 'function injectBack(){'
                    . '  var end=document.querySelector(".navbar-end");'
                    . '  if(!end||document.querySelector(".puq-back-link"))return;'
                    . '  var a=document.createElement("a");'
                    . '  a.className="navbar-item puq-back-link";'
                    . '  a.innerHTML="← Back to client area";'
                    . '  if(backUrl){a.href=backUrl;a.target="_self";}'
                    . '  else{a.href="#";a.onclick=function(e){e.preventDefault();'
                    . '    if(window.opener&&!window.opener.closed){window.close();}'
                    . '    else{history.length>1?history.back():window.close();}'
                    . '  };}'
                    . '  end.insertBefore(a, end.firstChild);'
                    . '}'
                    . ''
                    /* Show a full-screen "Use WHMCS" message when there is no
                       SSO session. Detection: no logout link visible after
                       Vue mount. */
                    . 'function maybeShowNoAccess(){'
                    . '  if(document.querySelector(".puq-noaccess"))return;'
                    . '  /* If a logout link exists, user IS logged in. */'
                    . '  if(document.querySelector(".navbar-item.logout"))return;'
                    . '  /* If the SPA mounted a login form, it should be hidden by CSS already.'
                    . '     Show our overlay so the user gets a clear message. */'
                    . '  var loginVisible=document.querySelector(".login")||document.querySelector("input[name=username]");'
                    . '  if(!loginVisible)return;'
                    . '  var d=document.createElement("div");'
                    . '  d.className="puq-noaccess";'
                    . '  d.innerHTML="<h2>File Manager — access via WHMCS</h2>"'
                    . '    +"<p>Direct login is disabled. Please open the File Manager from your hosting service in the WHMCS client area.</p>"'
                    . '    +(backUrl?"<a href=\\""+backUrl+"\\">← Open client area</a>":"");'
                    . '  document.body.appendChild(d);'
                    . '}'
                    . ''
                    . 'function injectAll(){injectBack();maybeShowNoAccess();}'
                    . 'var tries=0;var t=setInterval(function(){'
                    . '  injectAll();'
                    . '  if(++tries>40)clearInterval(t);'
                    . '},250);'
                    . 'new MutationObserver(injectAll).observe(document.body,{childList:true,subtree:true});'
                    . '})();'
                    . '</script>',
                'add_to_body' => '',
            ],
        ],
        // Per-request FTP adapter. Reads credentials from the session set
        // by PuqHostingAuth. If no session → adapter throws, filegator
        // falls back to guest (read-only / nothing). This is the lazy
        // closure pattern: filegator calls the closure ONCE per request
        // when the storage is first accessed, so per-request credentials
        // are picked up cleanly.
        'Filegator\Services\Storage\Filesystem' => [
            'handler' => '\Filegator\Services\Storage\Filesystem',
            'config' => [
                'separator' => '/',
                'config' => [],
                'adapter' => function () {
                    // Read native $_SESSION (NOT Symfony's bag — that's at
                    // $_SESSION['_sf2_attributes'][KEY]). PuqHostingAuth
                    // writes to both locations so this closure works.
                    $creds = $_SESSION['puq_ftp_creds'] ?? null;
                    if (!is_array($creds) || empty($creds['user']) || empty($creds['pass']) || empty($creds['domain'])) {
                        @error_log('[PuqHostingFM] no FTP creds in $_SESSION — adapter will be no-op. session_status=' . session_status());
                        return new \League\Flysystem\Adapter\NullAdapter();
                    }
                    // Discover the docroot path vsftpd actually accepts —
                    // chroot behaviour varies between hosts. When chroot_local_user
                    // is ON, `/` IS /home/<user>/ so the root is /web/<d>/public_html;
                    // when it is OFF, the FTP session lands at the real fs root and
                    // the path must be /home/<user>/web/<d>/public_html. Hard-coding
                    // only the chrooted form made ftp_chdir() fail with
                    // "Root is invalid or does not exist" on non-chrooting boxes.
                    // Probe both (same logic net2ftp_launch.php uses) and pick the
                    // first that ftp_chdir accepts; fall back to the chrooted form.
                    // Cache the resolved root in the session so we only probe
                    // once per SSO session, not on every file-manager API call.
                    $root = (isset($_SESSION['puq_ftp_root']) && is_string($_SESSION['puq_ftp_root']) && $_SESSION['puq_ftp_root'] !== '')
                        ? $_SESSION['puq_ftp_root'] : null;
                    if ($root === null) {
                        $candidates = [
                            '/web/' . $creds['domain'] . '/public_html',
                            '/home/' . $creds['user'] . '/web/' . $creds['domain'] . '/public_html',
                        ];
                        $root = $candidates[0]; // fallback to the chrooted form
                        $probe = @ftp_connect('{{FTP_HOST}}', 21, 15);
                        if ($probe) {
                            if (@ftp_login($probe, $creds['user'], $creds['pass'])) {
                                @ftp_pasv($probe, true);
                                foreach ($candidates as $c) {
                                    // Only cache a path that actually exists.
                                    if (@ftp_chdir($probe, $c)) { $root = $c; $_SESSION['puq_ftp_root'] = $c; break; }
                                }
                            }
                            @ftp_close($probe);
                        }
                    }
                    // Minimal adapter config (matches the reference working module).
                    // Defaults: passive=true, ssl=false, timeout=90.
                    return new \League\Flysystem\Adapter\Ftp([
                        'host'     => '{{FTP_HOST}}',
                        'port'     => 21,
                        'username' => $creds['user'],
                        'password' => $creds['pass'],
                        'root'     => $root,
                        'timeout'  => 30,
                    ]);
                },
            ],
        ],
        'Filegator\Services\Archiver\ArchiverInterface' => [
            'handler' => '\Filegator\Services\Archiver\Adapters\ZipArchiver',
            'config' => [],
        ],
        // Our custom auth — no local users.json, all auth flows through
        // signed WHMCS handshake.
        'Filegator\Services\Auth\AuthInterface' => [
            'handler' => '\Filegator\Services\Auth\Adapters\PuqHostingAuth',
            'config' => [
                'hmac_secret' => '{{HMAC_SECRET}}',  // 64-char hex, set at install
                'allowed_ips' => json_decode('{{ALLOWED_IPS}}', true) ?: [],  // JSON array of IPs, set at install
                'token_dir'   => __DIR__ . '/private/sso_sessions',
            ],
        ],
        'Filegator\Services\Router\Router' => [
            'handler' => '\Filegator\Services\Router\Router',
            'config' => [
                'query_param' => 'r',
                'routes_file' => __DIR__ . '/backend/Controllers/routes.php',
            ],
        ],
    ],
];
