<?php
/**
 * Structure Viewer — Scanner
 *
 * Renders the file tree and handles lazy-loading AJAX for All-mode.
 */

if ( ! defined( 'ABSPATH' ) ) {
    exit( 'Direct access denied.' );
}

final class SV_Scanner {

    const MAX_DEPTH = 20;

    // ── Public API ────────────────────────────────────────────────────────────

    /**
     * Output the full plugin/theme file tree for the Explorer tab.
     *
     * @param string $type          'plugin' | 'theme'
     * @param string $selected_item Folder name, or '' for all-items mode.
     */
    public static function display_structures( $type, $selected_item ) {
        if ( ! current_user_can( 'manage_options' ) ) {
            return;
        }

        $items_dir     = ( $type === 'plugin' ) ? WP_PLUGIN_DIR : get_theme_root();
        $selected_item = SV_Sanitizer::item_name( $selected_item );

        if ( $selected_item ) {
            $item_path = realpath( $items_dir . '/' . $selected_item );
            if ( ! $item_path
                || strpos( $item_path, realpath( $items_dir ) ) !== 0
                || ! is_dir( $item_path ) ) {
                echo '<p>' . esc_html__( 'Invalid selection.', 'structure-viewer' ) . '</p>';
                return;
            }
            $items = [ $item_path ];
        } else {
            $items = glob( $items_dir . '/*', GLOB_ONLYDIR ) ?: [];
            if ( empty( $items ) ) {
                echo '<p>' . esc_html__( 'No items found.', 'structure-viewer' ) . '</p>';
                return;
            }
        }

        $data_list = ( $type === 'plugin' )
            ? SV_ItemData::get_plugin_list( $items )
            : SV_ItemData::get_theme_list( $items );

        $excl         = SV_Filters::exclusion_patterns();
        $content_base = content_url();

        echo '<div class="sv-container">';

        foreach ( $items as $item_path ) {
            if ( ! is_dir( $item_path ) ) {
                continue;
            }

            $folder    = basename( $item_path );
            $item_name = isset( $data_list[ $folder ]['Name'] )
                ? $data_list[ $folder ]['Name']
                : $folder;

            if ( $selected_item ) {
                // ── Single-item mode: full rendered tree ──────────────
                self::render_item_heading( $item_name, $type, $folder, $item_path, $data_list[ $folder ] ?? [] );

                echo '<ul class="sv-tree sv-tree-' . esc_attr( sanitize_html_class( $folder ) ) . '">';
                self::scan_directory( $item_path, $item_path, 0, $folder, $type, $selected_item, $excl, $content_base );
                echo '</ul>';

            } else {
                // ── All-items mode: heading + lazy placeholder ─────────
                echo '<div class="sv-plugin-heading sv-plugin-heading-simple"'
                    . ' data-item="' . esc_attr( $folder ) . '"'
                    . ' data-type="' . esc_attr( $type ) . '"'
                    . ' data-name="' . esc_attr( $item_name ) . '"'
                    . ' tabindex="0" role="button" aria-label="' . esc_attr( $item_name ) . '">';
                self::render_heading_left( $item_name, $type, $data_list[ $folder ] ?? [] );
                echo '</div>';

                echo '<div class="sv-lazy-tree" data-item="' . esc_attr( $folder ) . '" data-type="' . esc_attr( $type ) . '" style="display:none;"></div>';
            }
        }

        echo '</div>';
    }

    /** AJAX: load a single plugin/theme tree on demand (all-mode lazy load). */
    public static function ajax_load_tree() {
        check_ajax_referer( STRUCTUREVIEWER_NONCE, 'nonce' );

        if ( ! current_user_can( 'manage_options' ) ) {
            wp_send_json_error( [ 'message' => 'Permission denied.' ] );
        }

        $type = isset( $_POST['type'] ) ? sanitize_text_field( wp_unslash( $_POST['type'] ) ) : '';
        // phpcs:ignore WordPress.Security.ValidatedSanitizedInput.InputNotSanitized -- sanitized via SV_Sanitizer::item_name() which calls sanitize_text_field + preg_replace
        $item = isset( $_POST['item'] ) ? SV_Sanitizer::item_name( sanitize_text_field( wp_unslash( $_POST['item'] ) ) ) : '';

        if ( ! in_array( $type, [ 'plugin', 'theme' ], true ) || ! $item ) {
            wp_send_json_error( [ 'message' => 'Invalid parameters.' ] );
        }

        $items_dir = ( $type === 'plugin' ) ? WP_PLUGIN_DIR : get_theme_root();
        $base      = realpath( $items_dir . '/' . $item );

        if ( ! $base || strpos( $base, realpath( $items_dir ) ) !== 0 || ! is_dir( $base ) ) {
            wp_send_json_error( [ 'message' => 'Item not found.' ] );
        }

        ob_start();
        echo '<ul class="sv-tree sv-tree-' . esc_attr( sanitize_html_class( $item ) ) . '">';
        self::scan_directory( $base, $base, 0, $item, $type, $item, SV_Filters::exclusion_patterns(), content_url() );
        echo '</ul>';
        $html = ob_get_clean();

        wp_send_json_success( [ 'html' => $html ] );
    }

    // ── Tree renderer ─────────────────────────────────────────────────────────

    /**
     * Recursively render a directory as nested <li> elements.
     */
    public static function scan_directory(
        $base_path,
        $current_path,
        $depth        = 0,
        $item_folder  = '',
        $type         = 'plugin',
        $selected_item = '',
        $excl         = null,
        $content_base = ''
    ) {
        static $image_exts = [ 'jpg' => 1, 'jpeg' => 1, 'png' => 1, 'gif' => 1, 'svg' => 1, 'webp' => 1, 'bmp' => 1, 'ico' => 1 ];
        static $type_prefix = [ 'plugin' => 'plugins/', 'theme' => 'themes/' ];

        if ( $depth > self::MAX_DEPTH ) {
            return;
        }

        if ( $excl === null ) {
            $excl = SV_Filters::exclusion_patterns();
        }
        if ( $content_base === '' ) {
            $content_base = content_url();
        }

        $raw = glob( $current_path . '/*' );
        if ( empty( $raw ) ) {
            return;
        }

        $folders = [];
        $files   = [];

        foreach ( $raw as $path ) {
            $bn       = basename( $path );
            $excluded = false;
            foreach ( $excl as $pat ) {
                if ( fnmatch( $pat, $bn ) ) {
                    $excluded = true;
                    break;
                }
            }
            if ( $excluded ) {
                continue;
            }
            is_dir( $path ) ? $folders[] = $path : $files[] = $path;
        }

        sort( $folders );
        sort( $files );

        $url_prefix = $content_base . '/' . ( $type_prefix[ $type ] ?? 'plugins/' ) . $item_folder . '/';
        $base_strip = $base_path . DIRECTORY_SEPARATOR;

        foreach ( array_merge( $folders, $files ) as $path ) {
            // Path-traversal guard
            if ( strpos( $path, $current_path . DIRECTORY_SEPARATOR ) !== 0
                && $path !== $current_path . DIRECTORY_SEPARATOR . basename( $path ) ) {
                $real = realpath( $path );
                if ( ! $real || strpos( $real, $base_path ) !== 0 ) {
                    continue;
                }
                $path = $real;
            }

            $name   = basename( $path );
            $is_dir = is_dir( $path );
            $ext    = $is_dir ? 'folder' : strtolower( pathinfo( $name, PATHINFO_EXTENSION ) );

            echo '<li class="' . ( $is_dir ? 'sv-folder' : 'sv-file' ) . ' sv-item-type-' . esc_attr( $ext ) . '">';

            if ( $is_dir ) {
                $sub_class = $depth > 0 ? 'sv-nested-subtree' : 'sv-subtree';
                echo '<div class="sv-folder-header" tabindex="0">';
                echo '<span class="sv-toggle">+</span>';
                echo '<span class="sv-folder-icon"></span>';
                echo '<strong class="sv-folder-name">' . esc_html( $name ) . '</strong>';
                echo '</div>';
                echo '<ul class="' . esc_attr( $sub_class ) . '" style="display:none;">';
                self::scan_directory( $base_path, $path, $depth + 1, $item_folder, $type, $selected_item, $excl, $content_base );
                echo '</ul>';
            } else {
                $rel      = ltrim( str_replace( $base_strip, '', $path ), '/\\' );
                $file_url = $url_prefix . $rel;
                $is_image = isset( $image_exts[ $ext ] );

                echo '<span class="sv-file-icon sv-file-icon-' . esc_attr( $ext ) . '"></span>';

                if ( $is_image ) {
                    echo '<a href="' . esc_url( $file_url ) . '" target="_blank" class="sv-file-name" tabindex="0">' . esc_html( $name ) . '</a>';
                } else {
                    echo '<a href="' . esc_url( $file_url ) . '" class="sv-file-name" tabindex="0">' . esc_html( $name ) . '</a>';
                }

                $fi   = new SplFileInfo( $path );
                $size = $fi->getSize();
                $mod  = $fi->getMTime();
                echo '<span class="sv-file-info">' . esc_html( size_format( $size, 2 ) ) . ' &bull; ' . esc_html( gmdate( 'Y-m-d H:i', $mod ) ) . '</span>';
            }

            echo '</li>';
        }
    }

    // ── Heading helpers ───────────────────────────────────────────────────────

    private static function render_item_heading( $name, $type, $folder, $item_path, $data ) {
        $version    = $data['Version'] ?? '';
        $tree_class = 'sv-tree-' . sanitize_html_class( $folder );
        $type_label = $type === 'plugin'
            ? esc_html__( 'Plugin', 'structure-viewer' )
            : esc_html__( 'Theme', 'structure-viewer' );

        echo '<div class="sv-plugin-heading">';

        // Left side: identical markup to all-items mode (render_heading_left)
        // — same icon size, same name class, same badge layout
        echo '<div class="sv-plugin-heading-left">';
        echo '<div class="sv-plugin-heading-icon-sm">';
        if ( $type === 'plugin' ) {
            echo '<svg viewBox="0 0 24 24" fill="none" width="14" height="14"><path d="M12 2L2 7l10 5 10-5-10-5zM2 17l10 5 10-5M2 12l10 5 10-5" stroke="currentColor" stroke-width="1.8" stroke-linecap="round" stroke-linejoin="round"/></svg>';
        } else {
            echo '<svg viewBox="0 0 24 24" fill="none" width="14" height="14"><path d="M12 2l3.09 6.26L22 9.27l-5 4.87 1.18 6.88L12 17.77l-6.18 3.25L7 14.14 2 9.27l6.91-1.01L12 2z" stroke="currentColor" stroke-width="1.8" stroke-linejoin="round"/></svg>';
        }
        echo '</div>';
        echo '<span class="sv-plugin-heading-name-sm">' . esc_html( $name ) . '</span>';
        echo '<span class="sv-plugin-type-badge">' . esc_html( $type_label ) . '</span>';
        if ( $version ) {
            echo '<span class="sv-plugin-type-badge" style="opacity:0.7;">v' . esc_html( $version ) . '</span>';
        }
        echo '</div>';

        // Right side: expand/collapse controls only — no description
        echo '<div class="sv-plugin-heading-right">';
        echo '<div class="sv-controls" data-tree-class="' . esc_attr( $tree_class ) . '">';
        echo '<div class="sv-button-group sv-toggle-group">';
        echo '<button class="button sv-expand-all">'   . esc_html__( 'Expand All', 'structure-viewer' ) . '</button>';
        echo '<button class="button sv-collapse-all">' . esc_html__( 'Collapse',   'structure-viewer' ) . '</button>';
        echo '</div>';
        echo '</div>';
        echo '</div>';

        echo '</div>';
    }

    private static function render_heading_left( $name, $type, $data ) {
        $version    = $data['Version'] ?? '';
        $type_label = $type === 'plugin'
            ? esc_html__( 'Plugin', 'structure-viewer' )
            : esc_html__( 'Theme',  'structure-viewer' );

        echo '<div class="sv-plugin-heading-left">';
        echo '<div class="sv-plugin-heading-icon-sm">';
        if ( $type === 'plugin' ) {
            echo '<svg viewBox="0 0 24 24" fill="none" width="14" height="14"><path d="M12 2L2 7l10 5 10-5-10-5zM2 17l10 5 10-5M2 12l10 5 10-5" stroke="currentColor" stroke-width="1.8" stroke-linecap="round" stroke-linejoin="round"/></svg>';
        } else {
            echo '<svg viewBox="0 0 24 24" fill="none" width="14" height="14"><path d="M12 2l3.09 6.26L22 9.27l-5 4.87 1.18 6.88L12 17.77l-6.18 3.25L7 14.14 2 9.27l6.91-1.01L12 2z" stroke="currentColor" stroke-width="1.8" stroke-linejoin="round"/></svg>';
        }
        echo '</div>';
        echo '<span class="sv-plugin-heading-name-sm">' . esc_html( $name ) . '</span>';
        echo '<span class="sv-plugin-type-badge">' . esc_html( $type_label ) . '</span>';
        if ( $version ) {
            echo ' <span class="sv-plugin-type-badge" style="opacity:0.7;">v' . esc_html( $version ) . '</span>';
        }
        echo '</div>';
    }

    // ── Misc ──────────────────────────────────────────────────────────────────

    public static function promo_note() {
        return "\n-- Generated by Structure Viewer (wordpress.org/plugins/structure-viewer)\n";
    }
}
