<?php
namespace IntentDeep\VirtualFiles\Traits;

trait VirtualFilesValidator {

    /**
     * Normalize file path (remove leading/trailing slashes, handle multiple slashes)
     */
    private function normalizePath($path) {
        // Remove leading and trailing slashes
        $path = trim($path, '/');

        // Replace multiple slashes with single slash
        $path = preg_replace('#/+#', '/', $path);

        // Remove any parent directory references for security
        $path = str_replace('../', '', $path);
        $path = str_replace('..\\', '', $path);

        return $path;
    }

    /**
     * Sanitize file path while preserving slashes and leading dots (for folders like .well-known)
     * Custom sanitization to avoid WordPress quirks with single letters
     */
    private function sanitizeFilePath($path) {
        // Split path into components
        $parts = explode('/', $path);

        // Sanitize each component
        $sanitized_parts = [];
        foreach ($parts as $part) {
            if (empty($part)) {
                continue; // Skip empty parts
            }

            // Preserve leading dot for hidden folders/files
            $has_leading_dot = strpos($part, '.') === 0;

            // Remove the leading dot temporarily if present
            $part_to_sanitize = $has_leading_dot ? substr($part, 1) : $part;

            // Custom sanitization to avoid WordPress sanitize_file_name() quirks
            // (it treats single letters like 'c' as extensions: 'c' -> 'unnamed-file.c')
            if ($this->isLastPathComponent($path, $part)) {
                // Last component (actual filename) - use WordPress sanitization
                $sanitized = sanitize_file_name($part_to_sanitize);
            } else {
                // Directory component - use safer sanitization
                // Remove invalid characters but don't treat single letters as extensions
                $sanitized = $this->sanitizeDirectoryName($part_to_sanitize);
            }

            // Restore leading dot if it was there
            if ($has_leading_dot) {
                $sanitized = '.' . $sanitized;
            }

            $sanitized_parts[] = $sanitized;
        }

        return implode('/', $sanitized_parts);
    }

    /**
     * Check if a part is the last component in the path
     */
    private function isLastPathComponent($path, $part) {
        $parts = explode('/', $path);
        $last_part = end($parts);
        return $part === $last_part;
    }

    /**
     * Sanitize directory name without WordPress quirks
     * Removes invalid characters but preserves valid directory names
     */
    private function sanitizeDirectoryName($name) {
        // Remove invalid filesystem characters
        $name = str_replace(['<', '>', ':', '"', '|', '?', '*', '\\'], '', $name);

        // Remove control characters
        $name = preg_replace('/[\x00-\x1F\x7F]/', '', $name);

        // Convert multiple spaces to single space
        $name = preg_replace('/\s+/', ' ', $name);

        // Trim spaces and dots from ends (but preserve internal dots)
        $name = trim($name, ' .');

        // Convert to lowercase for consistency (optional - remove if you want case-sensitive)
        // $name = strtolower($name);

        return $name;
    }

    /**
     * Check if path is restricted (WordPress core paths)
     */
    private function isRestrictedPath($path) {
        // Normalize the path first
        $normalized_path = strtolower($this->normalizePath($path));

        // Restricted path prefixes
        $restricted_path_prefixes = [
            'wp-admin/',
            'wp-includes/',
            'wp-content/plugins/',
            'wp-content/themes/',
            'wp-json/',
        ];

        // Check if path starts with any restricted prefix
        foreach ($restricted_path_prefixes as $prefix) {
            if (strpos($normalized_path, $prefix) === 0) {
                return true;
            }
        }

        // Restricted slugs (exact match for first path segment)
        $restricted_slugs = [
            'feed',
            'embed',
            'trackback',
        ];

        // Get first path segment
        $first_segment = strtok($normalized_path, '/');

        if (in_array($first_segment, $restricted_slugs)) {
            return true;
        }

        return false;
    }

    /**
     * Validate file path with restricted paths checking
     */
    private function validateFilePath($path) {
        // Check if path is restricted
        if ($this->isRestrictedPath($path)) {
            $normalized_path = $this->normalizePath($path);
            return sprintf(
                /* translators: %s: the restricted path */
                __('Cannot use WordPress reserved path "%s". Please choose a different path.', 'intentdeep-virtual-files'),
                $normalized_path
            );
        }

        // Check for .htaccess files (server restriction)
        $normalized_path = strtolower($this->normalizePath($path));
        if (strpos($normalized_path, '.htaccess') === 0 || strpos($normalized_path, '/.htaccess') !== false) {
            return __('Warning: .htaccess files are blocked by most web servers and will not be accessible. Please choose a different filename.', 'intentdeep-virtual-files');
        }

        // Check for invalid path patterns
        if (strpos($path, '..') !== false) {
            return __('Path cannot contain parent directory references (..).', 'intentdeep-virtual-files');
        }

        // Check for trailing slash (files can't end with /)
        if (substr($path, -1) === '/') {
            return __('File path cannot end with a slash.', 'intentdeep-virtual-files');
        }

        return true;
    }

    /**
     * Simple filename validation using WordPress functions
     * Now supports paths with slashes (e.g., .well-known/security.txt)
     */
    private function isValidFilename($filename, $exclude_id = 0) {
        // Check for empty filename
        if (empty($filename)) {
            return false;
        }

        // Check for valid characters using our custom path sanitizer
        $sanitized = $this->sanitizeFilePath($filename);
        if ($filename !== $sanitized) {
            return false;
        }

        // Check for extension
        $extension = pathinfo($filename, PATHINFO_EXTENSION);
        if (empty($extension)) {
            return false;
        }

        // Check allowed extensions
        $allowed_extensions = ['txt', 'md', 'json', 'jsonld', 'xml', 'rss', 'csv', 'yml', 'yaml', 'log'];
        if (!in_array(strtolower($extension), $allowed_extensions)) {
            return false;
        }

        // Check for uniqueness (excluding current file)
        $existing_files = get_posts([
            'post_type' => 'idep_virtual_file',
            'posts_per_page' => 1,
            'meta_key' => '_vf_filename',
            'meta_value' => $filename,
            'post__not_in' => [$exclude_id],
        ]);

        return empty($existing_files);
    }

    /**
     * Validate filename with detailed error messages
     */
    private function validateFilename($filename, $exclude_id = 0) {
        if (empty($filename)) {
            return false;
        }

        // Check for forbidden characters
        if (preg_match('/[<>:"|?*]/', $filename)) {
            return __('Filename contains invalid characters. Please avoid using < > : " | ? *', 'intentdeep-virtual-files');
        }

        // Check for extension
        if (strpos($filename, '.') === false) {
            return __('Filename must include a file extension (e.g., robots.txt)', 'intentdeep-virtual-files');
        }

        $extension = $this->getFileExtension($filename);
        if (empty($extension)) {
            return __('Filename must include a valid file extension', 'intentdeep-virtual-files');
        }

        $allowed_extensions = $this->getAllowedExtensions();

        if (!in_array(strtolower($extension), $allowed_extensions)) {
            return sprintf(
                /* translators: %1$s: file extension, %2$s: list of allowed extensions */
                __('File extension .%1$s is not allowed. Allowed extensions: %2$s', 'intentdeep-virtual-files'),
                $extension,
                implode(', ', $allowed_extensions)
            );
        }

        // Check for uniqueness (excluding current file)
        $existing_files = get_posts([
            'post_type' => 'idep_virtual_file',
            'posts_per_page' => 1,
            'meta_key' => '_vf_filename',
            'meta_value' => $filename,
            'post__not_in' => [$exclude_id],
        ]);

        if (!empty($existing_files)) {
            /* translators: %s: filename that already exists */
            return sprintf(
                __('A file with the filename "%s" already exists. Please choose a different filename.', 'intentdeep-virtual-files'),
                $filename
            );
        }

        return true;
    }

    /**
     * Limit virtual file creation for free users
     */
    public function limitVirtualFileCreation($data, $postarr) {
        // Only apply to virtual file post type
        if ($data['post_type'] !== 'idep_virtual_file') {
            return $data;
        }

        // Only check for new posts (not updates)
        if ($postarr['ID'] !== 0) {
            return $data;
        }

        // Skip if user is Pro
        if (function_exists('intentdeep_vf_fs') && intentdeep_vf_fs()->can_use_premium_code__premium_only()) {
            return $data;
        }

        // Count only ACTIVE virtual files (consistent with accessibility logic)
        $active_files = get_posts([
            'post_type' => 'idep_virtual_file',
            'posts_per_page' => -1,
            'post_status' => 'publish',
            'meta_query' => [
                [
                    'key' => '_vf_status',
                    'value' => 'active',
                    'compare' => '='
                ]
            ]
        ]);

        if (count($active_files) >= 5) {
            // Force to draft status if user has 5+ active files
            $data['post_status'] = 'draft';

            // Set error message
            set_transient('intentdeep_vf_error_' . get_current_user_id(),
                __('Free version limited to 5 active virtual files. Current files can be deactivated to make room for new ones, or upgrade to Pro for unlimited files.', 'intentdeep-virtual-files'), 60);
        }

        return $data;
    }

    /**
     * Validate virtual file meta data before saving
     */
    private function validateVirtualFileMeta($post_id, $filename, $extension, $content) {
        $errors = [];

        // Validate filename
        if (empty($filename)) {
            $errors[] = __('Filename is required.', 'intentdeep-virtual-files');
        } elseif (!$this->isValidFilename($filename, $post_id)) {
            $validation_result = $this->validateFilename($filename, $post_id);
            if (is_string($validation_result)) {
                $errors[] = $validation_result;
            } else {
                $errors[] = __('Invalid filename.', 'intentdeep-virtual-files');
            }
        }

        // Validate extension
        if (empty($extension)) {
            $errors[] = __('File extension is required.', 'intentdeep-virtual-files');
        } else {
            $allowed_extensions = $this->getAllowedExtensions();
            if (!in_array(strtolower($extension), $allowed_extensions)) {
                $errors[] = sprintf(
                    /* translators: %1$s: file extension, %2$s: list of allowed extensions */
                    __('File extension .%1$s is not allowed. Allowed extensions: %2$s', 'intentdeep-virtual-files'),
                    $extension,
                    implode(', ', $allowed_extensions)
                );
            }
        }

        // Validate content
        if (empty($content)) {
            $errors[] = __('File content is required.', 'intentdeep-virtual-files');
        }

        // Check file limit for free users
        if (!function_exists('intentdeep_vf_fs') || !intentdeep_vf_fs()->can_use_premium_code__premium_only()) {
            $current_status = get_post_meta($post_id, '_vf_status', true);
            $new_status = isset($_POST['vf_status']) ? sanitize_text_field($_POST['vf_status']) : 'inactive';

            // Only check limit if trying to create a new active file or activate an existing one
            if (($current_status !== 'active' && $new_status === 'active') || get_post_status($post_id) === 'auto-draft') {
                $active_files = $this->getActiveFileCount();
                if ($active_files >= 5) {
                    $errors[] = __('Free version limited to 5 active files. Upgrade to Pro for unlimited files.', 'intentdeep-virtual-files');
                }
            }
        }

        return $errors;
    }

    /**
     * Check if virtual file meta data is valid
     */
    private function isValidVirtualFileMeta($post_id, $filename, $extension, $content) {
        $errors = $this->validateVirtualFileMeta($post_id, $filename, $extension, $content);
        return empty($errors);
    }

    /**
     * Get validation errors for virtual file meta data
     */
    private function getVirtualFileValidationErrors($post_id, $filename, $extension, $content) {
        return $this->validateVirtualFileMeta($post_id, $filename, $extension, $content);
    }
}