<?php
/**
 * Filesystem Helper - Wrapper around Flysystem with fallback to native PHP functions
 *
 * @package PrimeLink
 */

namespace PrimeLink\Helpers;

if ( ! defined( 'ABSPATH' ) ) {
    exit;
}

final class FilesystemHelper {

    /**
     * Filesystem instance (lazy loaded)
     *
     * @var object|null
     */
    private static $filesystem = null;

    /**
     * Check if Flysystem is available
     *
     * @return bool
     */
    private static function is_flysystem_available() {
        // Check PHP version first - Flysystem v3 requires PHP 8.0+
        if ( version_compare( PHP_VERSION, '8.0.0', '<' ) ) {
            return false;
        }
        
        return class_exists( '\League\Flysystem\Filesystem' ) && 
               class_exists( '\League\Flysystem\LocalFilesystemAdapter' );
    }

    /**
     * Get Filesystem instance
     *
     * @return object|null
     */
    private static function get_filesystem() {
        if ( ! self::is_flysystem_available() ) {
            return null;
        }

        if ( null === self::$filesystem ) {
            try {
                $upload_dir = wp_upload_dir();
                $base_path = $upload_dir['basedir'] ?? ABSPATH . 'wp-content/uploads/';
                
                $adapter = new \League\Flysystem\LocalFilesystemAdapter( $base_path );
                self::$filesystem = new \League\Flysystem\Filesystem( $adapter );
            } catch ( \Exception $e ) {
                // If Flysystem fails to initialize, we'll use fallback
                self::$filesystem = null;
            }
        }
        return self::$filesystem;
    }

    /**
     * Write file contents
     *
     * @param string $path File path (relative to uploads or absolute)
     * @param string $contents File contents
     * @return bool|int True on success, false on failure, or bytes written
     */
    public static function write_file( $path, $contents ) {
        $upload_dir = wp_upload_dir();
        $base_path = $upload_dir['basedir'] ?? ABSPATH . 'wp-content/uploads/';
        
        // Normalize paths for comparison
        $normalized_path = str_replace( '\\', '/', $path );
        $normalized_base = str_replace( '\\', '/', $base_path );
        
        // Check if path is within base_path
        $is_absolute = ( $normalized_path[0] === '/' || ( strlen( $normalized_path ) >= 3 && $normalized_path[1] === ':' ) );
        $is_within_base = $is_absolute && strpos( $normalized_path, $normalized_base ) === 0;
        
        // If path is outside base_path, use fallback directly
        if ( $is_absolute && ! $is_within_base ) {
            return file_put_contents( $path, $contents );
        }
        
        try {
            $fs = self::get_filesystem();
            if ( $fs ) {
                // Convert to relative path
                if ( $is_within_base ) {
                    $relative_path = str_replace( $normalized_base, '', $normalized_path );
                    $relative_path = ltrim( $relative_path, '/' );
                } else {
                    // Already relative or try as-is
                    $relative_path = ltrim( $normalized_path, '/' );
                }
                
                // Ensure directory exists
                $dir_path = dirname( $relative_path );
                if ( $dir_path && $dir_path !== '.' && $dir_path !== '/' && $dir_path !== '\\' ) {
                    try {
                        if ( ! $fs->directoryExists( $dir_path ) ) {
                            $fs->createDirectory( $dir_path );
                        }
                    } catch ( \Exception $e ) {
                        // Directory might already exist or creation failed, continue
                    }
                }
                
                $fs->write( $relative_path, $contents );
                return strlen( $contents );
            }
        } catch ( \Exception $e ) {
            // Fall through to fallback - log only in debug mode
            if ( defined( 'WP_DEBUG' ) && WP_DEBUG ) {
                error_log( 'FilesystemHelper::write_file Flysystem error: ' . $e->getMessage() . ' - Falling back to native PHP' );
            }
        }

        // Fallback to native PHP
        $result = file_put_contents( $path, $contents );
        return $result !== false ? strlen( $contents ) : false;
    }

    /**
     * Read file contents
     *
     * @param string $path File path
     * @return string|false File contents or false on failure
     */
    public static function read_file( $path ) {
        $upload_dir = wp_upload_dir();
        $base_path = $upload_dir['basedir'] ?? ABSPATH . 'wp-content/uploads/';
        
        // Normalize paths for comparison
        $normalized_path = str_replace( '\\', '/', $path );
        $normalized_base = str_replace( '\\', '/', $base_path );
        
        // Check if path is within base_path
        $is_absolute = ( $normalized_path[0] === '/' || ( strlen( $normalized_path ) >= 3 && $normalized_path[1] === ':' ) );
        $is_within_base = $is_absolute && strpos( $normalized_path, $normalized_base ) === 0;
        
        // If path is outside base_path, use fallback directly
        if ( $is_absolute && ! $is_within_base ) {
            if ( file_exists( $path ) && is_readable( $path ) ) {
                return file_get_contents( $path );
            }
            return false;
        }
        
        try {
            $fs = self::get_filesystem();
            if ( $fs ) {
                // Convert to relative path
                if ( $is_within_base ) {
                    $relative_path = str_replace( $normalized_base, '', $normalized_path );
                    $relative_path = ltrim( $relative_path, '/' );
                } else {
                    $relative_path = ltrim( $normalized_path, '/' );
                }
                
                return $fs->read( $relative_path );
            }
        } catch ( \Exception $e ) {
            // Fall through to fallback
        }

        // Fallback to native PHP
        if ( file_exists( $path ) && is_readable( $path ) ) {
            return file_get_contents( $path );
        }
        return false;
    }

    /**
     * Check if file exists
     *
     * @param string $path File path
     * @return bool
     */
    public static function file_exists( $path ) {
        $upload_dir = wp_upload_dir();
        $base_path = $upload_dir['basedir'] ?? ABSPATH . 'wp-content/uploads/';
        
        // Normalize paths for comparison
        $normalized_path = str_replace( '\\', '/', $path );
        $normalized_base = str_replace( '\\', '/', $base_path );
        
        // Check if path is within base_path
        $is_absolute = ( $normalized_path[0] === '/' || ( strlen( $normalized_path ) >= 3 && $normalized_path[1] === ':' ) );
        $is_within_base = $is_absolute && strpos( $normalized_path, $normalized_base ) === 0;
        
        // If path is outside base_path, use fallback directly
        if ( $is_absolute && ! $is_within_base ) {
            return file_exists( $path ) && is_file( $path );
        }
        
        try {
            $fs = self::get_filesystem();
            if ( $fs ) {
                // Convert to relative path
                if ( $is_within_base ) {
                    $relative_path = str_replace( $normalized_base, '', $normalized_path );
                    $relative_path = ltrim( $relative_path, '/' );
                } else {
                    $relative_path = ltrim( $normalized_path, '/' );
                }
                
                return $fs->fileExists( $relative_path );
            }
        } catch ( \Exception $e ) {
            // Fall through to fallback
        }

        // Fallback to native PHP
        return file_exists( $path ) && is_file( $path );
    }

    /**
     * Check if directory exists
     *
     * @param string $path Directory path
     * @return bool
     */
    public static function directory_exists( $path ) {
        $upload_dir = wp_upload_dir();
        $base_path = $upload_dir['basedir'] ?? ABSPATH . 'wp-content/uploads/';
        
        // Normalize paths for comparison
        $normalized_path = str_replace( '\\', '/', $path );
        $normalized_base = str_replace( '\\', '/', $base_path );
        
        // Check if path is within base_path
        $is_absolute = ( $normalized_path[0] === '/' || ( strlen( $normalized_path ) >= 3 && $normalized_path[1] === ':' ) );
        $is_within_base = $is_absolute && strpos( $normalized_path, $normalized_base ) === 0;
        
        // If path is outside base_path, use fallback directly
        if ( $is_absolute && ! $is_within_base ) {
            return file_exists( $path ) && is_dir( $path );
        }
        
        try {
            $fs = self::get_filesystem();
            if ( $fs ) {
                // Convert to relative path
                if ( $is_within_base ) {
                    $relative_path = str_replace( $normalized_base, '', $normalized_path );
                    $relative_path = ltrim( $relative_path, '/' );
                } else {
                    $relative_path = ltrim( $normalized_path, '/' );
                }
                
                // Empty path means base directory
                if ( empty( $relative_path ) ) {
                    return true;
                }
                
                return $fs->directoryExists( $relative_path );
            }
        } catch ( \Exception $e ) {
            // Fall through to fallback
        }

        // Fallback to native PHP
        return file_exists( $path ) && is_dir( $path );
    }

    /**
     * Create directory
     *
     * @param string $path Directory path
     * @param int $permissions Permissions (default: 0755)
     * @return bool True on success, false on failure
     */
    public static function create_directory( $path, $permissions = 0755 ) {
        $upload_dir = wp_upload_dir();
        $base_path = $upload_dir['basedir'] ?? ABSPATH . 'wp-content/uploads/';
        
        // Normalize paths for comparison
        $normalized_path = str_replace( '\\', '/', $path );
        $normalized_base = str_replace( '\\', '/', $base_path );
        
        // Check if path is within base_path
        $is_absolute = ( $normalized_path[0] === '/' || ( strlen( $normalized_path ) >= 3 && $normalized_path[1] === ':' ) );
        $is_within_base = $is_absolute && strpos( $normalized_path, $normalized_base ) === 0;
        
        // If path is outside base_path, use fallback directly
        if ( $is_absolute && ! $is_within_base ) {
            return wp_mkdir_p( $path );
        }
        
        try {
            $fs = self::get_filesystem();
            if ( $fs ) {
                // Convert to relative path
                if ( $is_within_base ) {
                    $relative_path = str_replace( $normalized_base, '', $normalized_path );
                    $relative_path = ltrim( $relative_path, '/' );
                } else {
                    $relative_path = ltrim( $normalized_path, '/' );
                }
                
                // Empty path means base directory
                if ( empty( $relative_path ) ) {
                    return true;
                }
                
                $fs->createDirectory( $relative_path );
                return true;
            }
        } catch ( \Exception $e ) {
            // Fall through to fallback
        }

        // Fallback to native PHP / WooCommerce
        return wp_mkdir_p( $path );
    }

    /**
     * Delete file
     *
     * @param string $path File path
     * @return bool True on success, false on failure
     */
    public static function delete_file( $path ) {
        try {
            $fs = self::get_filesystem();
            if ( $fs ) {
                $upload_dir = wp_upload_dir();
                $base_path = $upload_dir['basedir'] ?? ABSPATH . 'wp-content/uploads/';
                
                if ( strpos( $path, $base_path ) === 0 ) {
                    $relative_path = str_replace( $base_path, '', $path );
                    $relative_path = ltrim( str_replace( '\\', '/', $relative_path ), '/' );
                } else {
                    $relative_path = ltrim( str_replace( '\\', '/', $path ), '/' );
                }
                
                $fs->delete( $relative_path );
                return true;
            }
        } catch ( \Exception $e ) {
            // Fall through to fallback
        }

        // Fallback to native PHP
        if ( file_exists( $path ) && is_file( $path ) ) {
            return @unlink( $path );
        }
        return false;
    }

    /**
     * Delete directory
     *
     * @param string $path Directory path
     * @return bool True on success, false on failure
     */
    public static function delete_directory( $path ) {
        try {
            $fs = self::get_filesystem();
            if ( $fs ) {
                $upload_dir = wp_upload_dir();
                $base_path = $upload_dir['basedir'] ?? ABSPATH . 'wp-content/uploads/';
                
                if ( strpos( $path, $base_path ) === 0 ) {
                    $relative_path = str_replace( $base_path, '', $path );
                    $relative_path = ltrim( str_replace( '\\', '/', $relative_path ), '/' );
                } else {
                    $relative_path = ltrim( str_replace( '\\', '/', $path ), '/' );
                }
                
                $fs->deleteDirectory( $relative_path );
                return true;
            }
        } catch ( \Exception $e ) {
            // Fall through to fallback
        }

        // Fallback to native PHP (recursive delete)
        if ( file_exists( $path ) && is_dir( $path ) ) {
            return self::delete_directory_recursive( $path );
        }
        return false;
    }

    /**
     * Recursive directory delete (fallback)
     *
     * @param string $dir Directory path
     * @return bool
     */
    private static function delete_directory_recursive( $dir ) {
        if ( ! is_dir( $dir ) ) {
            return false;
        }

        $files = array_diff( scandir( $dir ), array( '.', '..' ) );
        foreach ( $files as $file ) {
            $path = $dir . DIRECTORY_SEPARATOR . $file;
            if ( is_dir( $path ) ) {
                self::delete_directory_recursive( $path );
            } else {
                @unlink( $path );
            }
        }
        return @rmdir( $dir );
    }

    /**
     * Copy file
     *
     * @param string $source Source file path
     * @param string $destination Destination file path
     * @return bool True on success, false on failure
     */
    public static function copy_file( $source, $destination ) {
        try {
            $fs = self::get_filesystem();
            if ( $fs ) {
                $upload_dir = wp_upload_dir();
                $base_path = $upload_dir['basedir'] ?? ABSPATH . 'wp-content/uploads/';
                
                // Convert source
                if ( strpos( $source, $base_path ) === 0 ) {
                    $source_relative = str_replace( $base_path, '', $source );
                    $source_relative = ltrim( str_replace( '\\', '/', $source_relative ), '/' );
                } else {
                    $source_relative = ltrim( str_replace( '\\', '/', $source ), '/' );
                    // Try to read from absolute path first
                    if ( file_exists( $source ) ) {
                        $contents = file_get_contents( $source );
                    } else {
                        $contents = $fs->read( $source_relative );
                    }
                }
                
                // Convert destination
                if ( strpos( $destination, $base_path ) === 0 ) {
                    $dest_relative = str_replace( $base_path, '', $destination );
                    $dest_relative = ltrim( str_replace( '\\', '/', $dest_relative ), '/' );
                } else {
                    $dest_relative = ltrim( str_replace( '\\', '/', $destination ), '/' );
                }
                
                if ( ! isset( $contents ) ) {
                    $contents = $fs->read( $source_relative );
                }
                $fs->write( $dest_relative, $contents );
                return true;
            }
        } catch ( \Exception $e ) {
            // Fall through to fallback
        }

        // Fallback to native PHP
        if ( file_exists( $source ) ) {
            return copy( $source, $destination );
        }
        return false;
    }

    /**
     * Move file
     *
     * @param string $source Source file path
     * @param string $destination Destination file path
     * @return bool True on success, false on failure
     */
    public static function move_file( $source, $destination ) {
        try {
            $fs = self::get_filesystem();
            if ( $fs ) {
                $upload_dir = wp_upload_dir();
                $base_path = $upload_dir['basedir'] ?? ABSPATH . 'wp-content/uploads/';
                
                if ( strpos( $source, $base_path ) === 0 ) {
                    $source_relative = str_replace( $base_path, '', $source );
                    $source_relative = ltrim( str_replace( '\\', '/', $source_relative ), '/' );
                } else {
                    $source_relative = ltrim( str_replace( '\\', '/', $source ), '/' );
                }
                
                if ( strpos( $destination, $base_path ) === 0 ) {
                    $dest_relative = str_replace( $base_path, '', $destination );
                    $dest_relative = ltrim( str_replace( '\\', '/', $dest_relative ), '/' );
                } else {
                    $dest_relative = ltrim( str_replace( '\\', '/', $destination ), '/' );
                }
                
                $fs->move( $source_relative, $dest_relative );
                return true;
            }
        } catch ( \Exception $e ) {
            // Fall through to fallback
        }

        // Fallback to native PHP
        if ( file_exists( $source ) ) {
            // Ensure destination directory exists
            $dest_dir = dirname( $destination );
            if ( ! file_exists( $dest_dir ) ) {
                wp_mkdir_p( $dest_dir );
            }
            return rename( $source, $destination );
        }
        return false;
    }

    /**
     * Get file size
     *
     * @param string $path File path
     * @return int|false File size in bytes or false on failure
     */
    public static function file_size( $path ) {
        try {
            $fs = self::get_filesystem();
            if ( $fs ) {
                $upload_dir = wp_upload_dir();
                $base_path = $upload_dir['basedir'] ?? ABSPATH . 'wp-content/uploads/';
                
                if ( strpos( $path, $base_path ) === 0 ) {
                    $relative_path = str_replace( $base_path, '', $path );
                    $relative_path = ltrim( str_replace( '\\', '/', $relative_path ), '/' );
                } else {
                    $relative_path = ltrim( str_replace( '\\', '/', $path ), '/' );
                }
                
                $attributes = $fs->fileSize( $relative_path );
                return $attributes;
            }
        } catch ( \Exception $e ) {
            // Fall through to fallback
        }

        // Fallback to native PHP
        if ( file_exists( $path ) && is_file( $path ) ) {
            return filesize( $path );
        }
        return false;
    }

    /**
     * List directory contents
     *
     * @param string $path Directory path
     * @param bool $recursive Whether to list recursively
     * @return array List of file paths
     */
    public static function list_contents( $path, $recursive = false ) {
        try {
            $fs = self::get_filesystem();
            if ( $fs ) {
                $upload_dir = wp_upload_dir();
                $base_path = $upload_dir['basedir'] ?? ABSPATH . 'wp-content/uploads/';
                
                if ( strpos( $path, $base_path ) === 0 ) {
                    $relative_path = str_replace( $base_path, '', $path );
                    $relative_path = ltrim( str_replace( '\\', '/', $relative_path ), '/' );
                } else {
                    $relative_path = ltrim( str_replace( '\\', '/', $path ), '/' );
                }
                
                $listing = $fs->listContents( $relative_path, $recursive );
                $files = array();
                foreach ( $listing as $item ) {
                    $files[] = $item->path();
                }
                return $files;
            }
        } catch ( \Exception $e ) {
            // Fall through to fallback
        }

        // Fallback to native PHP
        $files = array();
        if ( file_exists( $path ) && is_dir( $path ) ) {
            $items = scandir( $path );
            foreach ( $items as $item ) {
                if ( $item !== '.' && $item !== '..' ) {
                    $file_path = $path . DIRECTORY_SEPARATOR . $item;
                    $files[] = $file_path;
                    if ( $recursive && is_dir( $file_path ) ) {
                        $files = array_merge( $files, self::list_contents( $file_path, true ) );
                    }
                }
            }
        }
        return $files;
    }
}

