<?php

namespace Twnt4All\Backend;

use Twnt4All\Support\Paths;
use Twnt4All\Engine\EnvironmentConfig;

class FileServer
{
    /**
     * Allowed values for the $type argument — kept in sync with the two
     * upload subdirectories Paths::get_upload_path() actually exposes.
     * Defense-in-depth : even if a future caller forgets to sanitise
     * `$type`, we still reject anything outside this whitelist.
     */
    private const ALLOWED_TYPES = [ 'temp', 'logs' ];

    /**
     * Resolve `$filename` under the upload directory for `$type` and
     * refuse anything that escapes it. Returns null when the request
     * would break out of the sandbox (attempted traversal, symlink
     * pointing outside, unknown type, empty filename…).
     */
    private static function resolve_safe_path( string $filename, string $type ) : ?string
    {
        if ( ! in_array( $type, self::ALLOWED_TYPES, true ) ) {
            return null;
        }
        // basename() strips any «../», back/forward slashes and NUL
        // bytes the caller might have missed. An empty result means the
        // input was purely path characters — refuse.
        $safe_name = basename( $filename );
        if ( $safe_name === '' || $safe_name !== $filename ) {
            return null;
        }
        $base = Paths::get_upload_path( $type );
        if ( $base === '' ) { return null; }

        $full  = $base . $safe_name;
        $real  = realpath( $full );
        $baseR = realpath( $base );
        if ( $real === false || $baseR === false ) { return null; }
        // Ensure the resolved path still lives under the allowed base
        // directory (blocks symlinks pointing outside).
        if ( strpos( $real, rtrim( $baseR, DIRECTORY_SEPARATOR ) . DIRECTORY_SEPARATOR ) !== 0 ) {
            return null;
        }
        if ( ! is_file( $real ) ) { return null; }
        return $real;
    }

    /**
     * Sends the PDF file to be downloaded.
     *
     * @param $filename
     * @param string $type
     * @param string $name
     */
    public static function send_file( $filename, $type, $name )
    {
        $file = self::resolve_safe_path( (string) $filename, (string) $type );
        if ( $file === null ) {
            status_header( 404 );
            exit;
        }
        $pathinfo = pathinfo( $file );

        header( 'Content-Description: File Transfer' );
        header( 'Content-Type: application/octet-stream' );
        header( 'Content-Disposition: attachment; filename="' . $name . '.' . $pathinfo[ 'extension' ] . '"' );
        //header('Content-Disposition: attachment; filename="'.basename($filepath).'"');
        header( 'Expires: 0' );
        header( 'Cache-Control: must-revalidate' );
        header( 'Pragma: public' );
        header( 'Content-Length: ' . filesize( $file ) );
        flush(); // Flush system output buffer
        // phpcs:ignore WordPress.WP.AlternativeFunctions.file_system_operations_readfile -- Streams a binary attachment directly to the HTTP response; WP_Filesystem returns strings and cannot stream chunked output.
        readfile( $file );
        die();
    }

    /**
     * Displays the PDF in the browser.
     *
     * @param $filename
     * @param string $type
     */
    public static function display_pdf( $filename, $type = 'temp' )
    {
        $file = self::resolve_safe_path( (string) $filename, (string) $type );
        if ( $file === null ) {
            status_header( 404 );
            exit;
        }
        header( 'Content-type: application/pdf' );
        header( 'Content-Disposition: inline; filename="' . basename( $file ) . '"' );
        //header('Content-Transfer-Encoding: binary');
        //header('Accept-Ranges: bytes');
        //@readfile();
        ob_start();
        // phpcs:ignore WordPress.WP.AlternativeFunctions.file_system_operations_readfile -- Streams a binary PDF to the HTTP response; WP_Filesystem returns strings and cannot stream chunked output.
        readfile( $file );
        // Note: no <script> is appended here. The response is a PDF binary
        // (Content-Type: application/pdf) — appending an HTML <script> tag
        // after the PDF body would not be executed by the browser anyway
        // and WordPress.org rejects inline <script> tags outright.
        ob_end_flush();
        exit;
    }
}


