<?php
/**
 * Structure Viewer — Exporter
 *
 * Handles all structure export formats: TXT, JSON, XML, clipboard.
 */

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

final class SV_Exporter {

    // ── AJAX entry points ─────────────────────────────────────────────────────

    public static function ajax_export() {
        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'] ) )           : 'plugin';
        $item   = isset( $_POST['item'] )   ? SV_Sanitizer::item_name( sanitize_text_field( wp_unslash( $_POST['item'] ) ) )       : '';
        $format = isset( $_POST['format'] ) ? sanitize_text_field( wp_unslash( $_POST['format'] ) )         : 'txt';

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

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

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

        $dirs = ( $type === 'plugin' )
            ? SV_ItemData::get_plugin_list( [ $base ] )
            : SV_ItemData::get_theme_list(  [ $base ] );
        $name = $dirs[ $item ]['Name'] ?? $item;

        // Clear any output buffered before this handler (notices, warnings from
        // other plugins) — without this the JSON response gets prefixed with
        // garbage text, jQuery can't parse it, and response.data.data === undefined.
        if ( ob_get_length() ) {
            ob_clean();
        }

        switch ( $format ) {
            case 'json':
                wp_send_json_success( [
                    'data'      => self::build_json( $base, $name, $type ),
                    'format'    => 'json',
                    'filename'  => sanitize_file_name( $name ) . '-structure.json',
                    'mime_type' => 'application/json',
                ] );
                break;
            case 'xml':
                wp_send_json_success( [
                    'data'      => self::build_xml( $base, $name, $type ),
                    'format'    => 'xml',
                    'filename'  => sanitize_file_name( $name ) . '-structure.xml',
                    'mime_type' => 'application/xml',
                ] );
                break;
            default:
                $content = $name . ' (' . ucfirst( $type ) . ")\n"
                    . self::build_txt( $base, $base )
                    . SV_Scanner::promo_note();
                wp_send_json_success( [
                    'data'      => $content,
                    'format'    => 'txt',
                    'filename'  => sanitize_file_name( $name ) . '-structure.txt',
                    'mime_type' => 'text/plain',
                ] );
        }
    }

    public static function ajax_copy_clipboard() {
        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'] ) ) : 'plugin';
        $item   = isset( $_POST['item'] ) ? SV_Sanitizer::item_name( sanitize_text_field( wp_unslash( $_POST['item'] ) ) ) : '';
        $format = isset( $_POST['copy_format'] ) ? sanitize_text_field( wp_unslash( $_POST['copy_format'] ) ) : 'json';

        if ( ! in_array( $format, [ 'json', 'text', 'xml' ], true ) ) {
            $format = 'json';
        }

        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 || ! is_dir( $base ) ) {
            wp_send_json_error( [ 'message' => 'Item not found.' ] );
        }

        if ( ob_get_length() ) {
            ob_clean();
        }

        switch ( $format ) {
            case 'json':
                $data = self::build_json( $base, $item, $type );
                break;
            case 'xml':
                $data = self::build_xml( $base, $item, $type );
                break;
            case 'text':
            default:
                $data = self::build_clipboard( $base, $base );
                break;
        }

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

    // ── TXT builder ───────────────────────────────────────────────────────────

    public static function build_txt( $base_path, $current_path, $depth = 0 ) {
        $base_path    = realpath( $base_path );
        $current_path = realpath( $current_path );

        if ( ! $base_path || ! $current_path || ! is_dir( $current_path ) ) {
            return '';
        }

        $excl   = SV_Filters::exclusion_patterns();
        $output = '';
        // ASCII-safe tree characters — Unicode box-drawing (├, │, ─) are not
        // supported by jsPDF's built-in Courier font and render as "%" in PDFs.
        $indent = str_repeat( '|   ', $depth );
        $items  = glob( $current_path . '/*' ) ?: [];

        $folders = array_filter( $items, 'is_dir' );
        $files   = array_filter( $items, fn( $i ) => ! is_dir( $i ) );
        sort( $folders );
        sort( $files );

        foreach ( array_merge( $folders, $files ) as $path ) {
            $name = basename( $path );
            $skip = false;
            foreach ( $excl as $pat ) {
                if ( fnmatch( $pat, $name ) ) { $skip = true; break; }
            }
            if ( $skip ) continue;

            if ( is_dir( $path ) ) {
                $output .= $indent . '+-- ' . $name . "/\n";
                $output .= self::build_txt( $base_path, $path, $depth + 1 );
            } else {
                $output .= $indent . '+-- ' . $name . "\n";
            }
        }

        return $output;
    }

    // ── Clipboard builder ─────────────────────────────────────────────────────

    public static function build_clipboard( $base_path, $current_path, $depth = 0 ) {
        $base_path    = realpath( $base_path );
        $current_path = realpath( $current_path );

        if ( ! $base_path || ! $current_path || ! is_dir( $current_path ) ) {
            return '';
        }

        $excl   = SV_Filters::exclusion_patterns();
        $output = '';
        $items  = glob( $current_path . '/*' ) ?: [];

        $folders = array_filter( $items, 'is_dir' );
        $files   = array_filter( $items, fn( $i ) => ! is_dir( $i ) );
        sort( $folders );
        sort( $files );

        foreach ( array_merge( $folders, $files ) as $path ) {
            $name = basename( $path );
            $skip = false;
            foreach ( $excl as $pat ) {
                if ( fnmatch( $pat, $name ) ) { $skip = true; break; }
            }
            if ( $skip ) continue;

            $prefix  = str_repeat( '  ', $depth );
            $output .= $prefix . ( is_dir( $path ) ? '📁 ' : '📄 ' ) . $name . "\n";
            if ( is_dir( $path ) ) {
                $output .= self::build_clipboard( $base_path, $path, $depth + 1 );
            }
        }

        return $output;
    }

    // ── JSON builder ──────────────────────────────────────────────────────────

    public static function build_json( $base_path, $item_name, $type ) {
        $base_path = realpath( $base_path );
        if ( ! $base_path ) return '{}';

        $data = [
            'name'          => $item_name,
            'type'          => $type,
            'exported'      => gmdate( 'Y-m-d H:i:s' ),
            'path'          => $base_path,
            'generated_by'  => 'Structure Viewer – wordpress.org/plugins/structure-viewer',
            'children'      => self::json_nodes( $base_path, $base_path ),
        ];

        return wp_json_encode( $data, JSON_PRETTY_PRINT | JSON_UNESCAPED_SLASHES );
    }

    private static function json_nodes( $base_path, $current_path ) {
        $excl  = SV_Filters::exclusion_patterns();
        $nodes = [];
        $items = glob( $current_path . '/*' ) ?: [];

        $folders = array_filter( $items, 'is_dir' );
        $files   = array_filter( $items, fn( $i ) => ! is_dir( $i ) );
        sort( $folders );
        sort( $files );

        foreach ( array_merge( $folders, $files ) as $path ) {
            $name = basename( $path );
            $skip = false;
            foreach ( $excl as $pat ) {
                if ( fnmatch( $pat, $name ) ) { $skip = true; break; }
            }
            if ( $skip ) continue;

            $rel = ltrim( str_replace( $base_path, '', $path ), '/' );

            if ( is_dir( $path ) ) {
                $nodes[] = [
                    'name'     => $name,
                    'type'     => 'directory',
                    'path'     => $rel,
                    'children' => self::json_nodes( $base_path, $path ),
                ];
            } else {
                $nodes[] = [
                    'name'     => $name,
                    'type'     => 'file',
                    'path'     => $rel,
                    'size'     => @filesize( $path ), // phpcs:ignore
                    'modified' => gmdate( 'Y-m-d H:i:s', @filemtime( $path ) ), // phpcs:ignore
                ];
            }
        }

        return $nodes;
    }

    // ── XML builder ───────────────────────────────────────────────────────────

    public static function build_xml( $base_path, $item_name, $type ) {
        $base_path = realpath( $base_path );
        if ( ! $base_path ) return '';

        $dom  = new DOMDocument( '1.0', 'UTF-8' );
        $dom->formatOutput = true;
        $root = $dom->createElement( 'structure' );
        $root->setAttribute( 'name', $item_name );
        $root->setAttribute( 'type', $type );
        $root->setAttribute( 'exported', gmdate( 'Y-m-d H:i:s' ) );
        $root->setAttribute( 'generated_by', 'Structure Viewer – wordpress.org/plugins/structure-viewer' );
        $dom->appendChild( $root );

        self::xml_nodes( $dom, $root, $base_path, $base_path );

        return $dom->saveXML();
    }

    private static function xml_nodes( $dom, $parent, $base_path, $current_path ) {
        $excl  = SV_Filters::exclusion_patterns();
        $items = glob( $current_path . '/*' ) ?: [];

        $folders = array_filter( $items, 'is_dir' );
        $files   = array_filter( $items, fn( $i ) => ! is_dir( $i ) );
        sort( $folders );
        sort( $files );

        foreach ( array_merge( $folders, $files ) as $path ) {
            $name = basename( $path );
            $skip = false;
            foreach ( $excl as $pat ) {
                if ( fnmatch( $pat, $name ) ) { $skip = true; break; }
            }
            if ( $skip ) continue;

            if ( is_dir( $path ) ) {
                $node = $dom->createElement( 'directory' );
                $node->setAttribute( 'name', $name );
                $parent->appendChild( $node );
                self::xml_nodes( $dom, $node, $base_path, $path );
            } else {
                $node = $dom->createElement( 'file' );
                $node->setAttribute( 'name', $name );
                $node->setAttribute( 'size', (string) ( @filesize( $path ) ?: 0 ) ); // phpcs:ignore
                $node->setAttribute( 'modified', gmdate( 'Y-m-d H:i:s', @filemtime( $path ) ) ); // phpcs:ignore
                $parent->appendChild( $node );
            }
        }
    }
}
