<?php
defined( 'ABSPATH' ) || exit;

/**
 * Template Library for Jalpi Page Builder.
 *
 * Stores templates as a private CPT and exposes them via REST.
 * Endpoints live under jalpi-page-builder/v1/templates.
 */
class Jalpi_Templates {

    private static ?self $instance = null;

    const CPT       = 'jalpi_template';
    const NAMESPACE = 'jalpi-page-builder/v1';

    const CATEGORIES = [
        // Full pages
        'landing-pages'   => 'Landing Pages',
        'home-pages'      => 'Home Pages',
        'about-pages'     => 'About Pages',
        'contact-pages'   => 'Contact Pages',
        'services-pages'  => 'Services Pages',
        'portfolio-pages' => 'Portfolio Pages',
        'blog-pages'      => 'Blog Pages',
        // Sections
        'hero-sections'   => 'Hero Sections',
        'features'        => 'Features',
        'cta-blocks'      => 'CTA Blocks',
        'testimonials'    => 'Testimonials',
        'pricing-tables'  => 'Pricing Tables',
        'team'            => 'Team',
        'faq'             => 'FAQ',
        'statistics'      => 'Statistics / Counter',
        'logo-carousel'   => 'Logo Carousel',
        'forms'           => 'Forms',
        // Site chrome
        'headers'         => 'Headers',
        'footers'         => 'Footers',
        'popups'          => 'Popups',
        // Utility pages
        '404-pages'       => '404 Pages',
        'coming-soon'     => 'Coming Soon',
        'maintenance'     => 'Maintenance',
        // Commerce
        'woocommerce'     => 'WooCommerce Templates',
    ];

    public static function instance(): self {
        if ( null === self::$instance ) {
            self::$instance = new self();
        }
        return self::$instance;
    }

    private function __construct() {
        add_action( 'init',          [ $this, 'register_cpt'    ] );
        add_action( 'rest_api_init', [ $this, 'register_routes' ] );

        // Invalidate editor template list cache when templates change.
        add_action( 'save_post_' . self::CPT, [ $this, 'invalidate_list_cache' ] );
        add_action( 'delete_post',            [ $this, 'invalidate_list_cache' ] );
    }

    public function invalidate_list_cache(): void {
        delete_transient( 'jalpi_editor_tpl_list' );
    }

    // =========================================================================
    // CPT
    // =========================================================================

    public function register_cpt(): void {
        register_post_type( self::CPT, [
            'labels'       => [
                'name'          => __( 'Jalpi Templates', 'jalpi-page-builder' ),
                'singular_name' => __( 'Jalpi Template',  'jalpi-page-builder' ),
            ],
            'public'       => false,
            'show_ui'      => false,
            'show_in_rest' => false,
            'supports'     => [ 'title', 'editor', 'custom-fields' ],
        ] );
    }

    // =========================================================================
    // Routes
    // =========================================================================

    public function register_routes(): void {

        $id_arg = [
            'id' => [
                'required'          => true,
                'type'              => 'integer',
                'minimum'           => 1,
                'sanitize_callback' => 'absint',
                'validate_callback' => [ $this, 'validate_template_id' ],
            ],
        ];

        // Categories
        register_rest_route( self::NAMESPACE, '/templates/categories', [
            'methods'             => WP_REST_Server::READABLE,
            'callback'            => [ $this, 'get_categories' ],
            'permission_callback' => [ $this, 'read_permission' ],
        ] );

        // Import (before the /{id} route so it isn't swallowed)
        register_rest_route( self::NAMESPACE, '/templates/import', [
            'methods'             => WP_REST_Server::CREATABLE,
            'callback'            => [ $this, 'import_template' ],
            'permission_callback' => [ $this, 'write_permission' ],
            'args'                => [
                'json' => [
                    'required' => true,
                    'type'     => 'string',
                ],
                'name' => [
                    'type'              => 'string',
                    'sanitize_callback' => 'sanitize_text_field',
                    'default'           => '',
                ],
            ],
        ] );

        // List + Create
        register_rest_route( self::NAMESPACE, '/templates', [
            [
                'methods'             => WP_REST_Server::READABLE,
                'callback'            => [ $this, 'get_templates' ],
                'permission_callback' => [ $this, 'read_permission' ],
                'args'                => [
                    'search'   => [ 'type' => 'string',  'sanitize_callback' => 'sanitize_text_field', 'default' => '' ],
                    'category' => [ 'type' => 'string',  'sanitize_callback' => 'sanitize_key',        'default' => '' ],
                    'type'     => [ 'type' => 'string',  'sanitize_callback' => 'sanitize_key',        'default' => '' ],
                    'tag'      => [ 'type' => 'string',  'sanitize_callback' => 'sanitize_key',        'default' => '' ],
                    'source'   => [ 'type' => 'string',  'sanitize_callback' => 'sanitize_key',        'default' => '' ],
                    'page'     => [ 'type' => 'integer', 'sanitize_callback' => 'absint',              'default' => 1 ],
                    'per_page' => [ 'type' => 'integer', 'sanitize_callback' => 'absint',              'default' => 24 ],
                ],
            ],
            [
                'methods'             => WP_REST_Server::CREATABLE,
                'callback'            => [ $this, 'create_template' ],
                'permission_callback' => [ $this, 'write_permission' ],
                'args'                => [
                    'name'        => [ 'required' => true,  'type' => 'string', 'sanitize_callback' => 'sanitize_text_field' ],
                    'description' => [ 'type' => 'string',  'sanitize_callback' => 'sanitize_textarea_field', 'default' => '' ],
                    'category'    => [ 'type' => 'string',  'sanitize_callback' => 'sanitize_key',            'default' => '' ],
                    'type'        => [ 'required' => true,  'type' => 'string',  'enum' => [ 'page', 'section', 'element' ] ],
                    'data'        => [ 'required' => true ],
                ],
            ],
        ] );

        // Duplicate (before /{id} so the static segment wins)
        register_rest_route( self::NAMESPACE, '/templates/(?P<id>\d+)/duplicate', [
            'methods'             => WP_REST_Server::CREATABLE,
            'callback'            => [ $this, 'duplicate_template' ],
            'permission_callback' => [ $this, 'write_permission_for_post' ],
            'args'                => $id_arg,
        ] );

        // Export
        register_rest_route( self::NAMESPACE, '/templates/(?P<id>\d+)/export', [
            'methods'             => WP_REST_Server::READABLE,
            'callback'            => [ $this, 'export_template' ],
            'permission_callback' => [ $this, 'read_permission_for_post' ],
            'args'                => $id_arg,
        ] );

        // Get + Update + Delete single
        // Read-only kit templates. Kept on a separate route from the numeric
        // one so a `kit:` id can never reach update_template/delete_template.
        // Generic resolver for every virtual kit id shape (kit: / kitsec: /
        // kitchrome:). The id is passed base64url-encoded because it contains
        // ':' and WP route regexes cannot use '@' or ':' safely.
        register_rest_route( self::NAMESPACE, '/kit-template/(?P<eid>[A-Za-z0-9_-]+)', [
            'methods'             => WP_REST_Server::READABLE,
            'callback'            => [ $this, 'get_kit_template_by_eid' ],
            'permission_callback' => [ $this, 'read_permission' ],
            'args'                => [ 'eid' => [ 'required' => true, 'type' => 'string' ] ],
        ] );

        register_rest_route( self::NAMESPACE, '/kit-templates/(?P<kit>[a-zA-Z0-9_-]+)/(?P<slug>[a-zA-Z0-9_-]+)', [
            'methods'             => WP_REST_Server::READABLE,
            'callback'            => [ $this, 'get_kit_template' ],
            'permission_callback' => [ $this, 'read_permission' ],
            'args'                => [
                'kit'  => [ 'required' => true, 'type' => 'string' ],
                'slug' => [ 'required' => true, 'type' => 'string' ],
            ],
        ] );

        // Per-user favorites and recently-used lists.
        register_rest_route( self::NAMESPACE, '/templates/user-lists', [
            [
                'methods'             => WP_REST_Server::READABLE,
                'callback'            => [ $this, 'get_user_lists' ],
                'permission_callback' => [ $this, 'read_permission' ],
            ],
        ] );
        register_rest_route( self::NAMESPACE, '/templates/favorite', [
            [
                'methods'             => WP_REST_Server::CREATABLE,
                'callback'            => [ $this, 'toggle_favorite' ],
                'permission_callback' => [ $this, 'read_permission' ],
                'args'                => [
                    'id' => [ 'required' => true, 'type' => 'string' ],
                ],
            ],
        ] );
        register_rest_route( self::NAMESPACE, '/templates/recent', [
            [
                'methods'             => WP_REST_Server::CREATABLE,
                'callback'            => [ $this, 'push_recent' ],
                'permission_callback' => [ $this, 'read_permission' ],
                'args'                => [
                    'id' => [ 'required' => true, 'type' => 'string' ],
                ],
            ],
        ] );

        register_rest_route( self::NAMESPACE, '/templates/(?P<id>\d+)', [
            'args' => $id_arg,
            [
                'methods'             => WP_REST_Server::READABLE,
                'callback'            => [ $this, 'get_template' ],
                'permission_callback' => [ $this, 'read_permission_for_post' ],
            ],
            [
                'methods'             => WP_REST_Server::EDITABLE,
                'callback'            => [ $this, 'update_template' ],
                'permission_callback' => [ $this, 'write_permission_for_post' ],
                'args'                => [
                    'name'        => [ 'type' => 'string',  'sanitize_callback' => 'sanitize_text_field' ],
                    'description' => [ 'type' => 'string',  'sanitize_callback' => 'sanitize_textarea_field' ],
                    'category'    => [ 'type' => 'string',  'sanitize_callback' => 'sanitize_key' ],
                    'thumbnail'   => [ 'type' => 'integer', 'sanitize_callback' => 'absint' ],
                ],
            ],
            [
                'methods'             => WP_REST_Server::DELETABLE,
                'callback'            => [ $this, 'delete_template' ],
                'permission_callback' => [ $this, 'write_permission_for_post' ],
            ],
        ] );
    }

    // =========================================================================
    // Permissions
    // =========================================================================

    public function read_permission(): bool {
        return is_user_logged_in() && current_user_can( 'edit_posts' );
    }

    public function write_permission(): bool {
        return is_user_logged_in() && current_user_can( 'edit_posts' );
    }

    public function read_permission_for_post( WP_REST_Request $request ): bool {
        if ( ! is_user_logged_in() ) {
            return false;
        }
        $id = absint( $request->get_param( 'id' ) );
        if ( $id ) {
            return current_user_can( 'edit_post', $id );
        }
        return current_user_can( 'edit_posts' );
    }

    public function write_permission_for_post( WP_REST_Request $request ): bool {
        if ( ! is_user_logged_in() ) {
            return false;
        }
        $id = absint( $request->get_param( 'id' ) );
        if ( $id ) {
            return current_user_can( 'edit_post', $id );
        }
        return current_user_can( 'edit_posts' );
    }

    // =========================================================================
    // Validators
    // =========================================================================

    public function validate_template_id( $value ): bool {
        $id   = absint( $value );
        $post = $id > 0 ? get_post( $id ) : null;
        return $post && self::CPT === $post->post_type;
    }

    // =========================================================================
    // Callbacks
    // =========================================================================

    public function get_categories( WP_REST_Request $request ): WP_REST_Response {
        // Counts must include the read-only kit templates, otherwise a category
        // shows "0" while the grid below it lists kit entries.
        $kit_counts = Jalpi_Kit_Templates::counts();

        $cats  = [];
        $total = 0;
        foreach ( self::CATEGORIES as $slug => $label ) {
            $q = new WP_Query( [
                'post_type'      => self::CPT,
                'post_status'    => 'publish',
                'posts_per_page' => 1,
                'fields'         => 'ids',
                'meta_query'     => [ [ 'key' => '_jalpi_tpl_category', 'value' => $slug ] ], // phpcs:ignore WordPress.DB.SlowDBQuery.slow_db_query_meta_query
            ] );
            $count  = (int) $q->found_posts + (int) ( $kit_counts[ $slug ] ?? 0 );
            $total += $count;
            $cats[] = [ 'slug' => $slug, 'label' => $label, 'count' => $count ];
        }

        return rest_ensure_response( [
            'categories' => $cats,
            'total'      => $total,
            'tags'       => $this->all_tags(),
        ] );
    }

    /** Every distinct tag across saved templates and kit templates, sorted. */
    private function all_tags(): array {
        $tags = [];
        foreach ( Jalpi_Kit_Templates::all( false ) as $item ) {
            foreach ( (array) ( $item['tags'] ?? [] ) as $t ) {
                $tags[ $t ] = true;
            }
        }
        $q = new WP_Query( [
            'post_type'      => self::CPT,
            'post_status'    => 'publish',
            'posts_per_page' => 500,
            'fields'         => 'ids',
        ] );
        foreach ( $q->posts as $pid ) {
            foreach ( (array) get_post_meta( $pid, '_jalpi_tpl_tags', true ) as $t ) {
                $t = sanitize_key( $t );
                if ( $t ) {
                    $tags[ $t ] = true;
                }
            }
        }
        $out = array_keys( $tags );
        sort( $out );
        return $out;
    }

    public function get_templates( WP_REST_Request $request ): WP_REST_Response {
        $search   = (string) $request->get_param( 'search' );
        $category = (string) $request->get_param( 'category' );
        $type     = (string) $request->get_param( 'type' );
        $tag      = (string) $request->get_param( 'tag' );
        $page     = max( 1, (int) $request->get_param( 'page' ) );
        $per_page = (int) $request->get_param( 'per_page' );
        $per_page = $per_page > 0 ? min( 100, $per_page ) : 24;
        $only     = (string) $request->get_param( 'source' ); // '', 'kit', 'user', 'favorites', 'recent'

        // ── Saved (database) templates ───────────────────────────────────────
        $items = [];
        if ( 'kit' !== $only ) {
            $args = [
                'post_type'      => self::CPT,
                'post_status'    => 'publish',
                'posts_per_page' => 500,
                'orderby'        => 'date',
                'order'          => 'DESC',
            ];
            if ( $search ) {
                $args['s'] = $search;
            }
            $meta = [];
            if ( $category ) {
                $meta[] = [ 'key' => '_jalpi_tpl_category', 'value' => $category ];
            }
            if ( $type ) {
                $meta[] = [ 'key' => '_jalpi_tpl_type', 'value' => $type ];
            }
            if ( $meta ) {
                $args['meta_query'] = $meta; // phpcs:ignore WordPress.DB.SlowDBQuery.slow_db_query_meta_query
            }
            $query = new WP_Query( $args );
            $items = array_map( fn( $p ) => $this->format_template( $p, false ), $query->posts );
        }

        // ── Read-only kit templates ──────────────────────────────────────────
        if ( 'user' !== $only ) {
            $kit_items = Jalpi_Kit_Templates::all( false );
            if ( $category ) {
                $kit_items = array_filter( $kit_items, fn( $i ) => $i['category'] === $category );
            }
            if ( $type ) {
                $kit_items = array_filter( $kit_items, fn( $i ) => $i['type'] === $type );
            }
            if ( $search ) {
                $needle    = mb_strtolower( $search );
                $kit_items = array_filter(
                    $kit_items,
                    fn( $i ) => false !== mb_strpos( mb_strtolower( $i['name'] . ' ' . $i['description'] ), $needle )
                );
            }
            $items = array_merge( $items, array_values( $kit_items ) );
        }

        // ── Tag filter (applies to both sources) ─────────────────────────────
        if ( $tag ) {
            $items = array_values( array_filter(
                $items,
                fn( $i ) => in_array( $tag, (array) ( $i['tags'] ?? [] ), true )
            ) );
        }

        // ── Favorites / recent (per-user lists) ──────────────────────────────
        if ( 'favorites' === $only || 'recent' === $only ) {
            $ids   = self::user_list( 'favorites' === $only ? 'favorites' : 'recent' );
            $order = array_flip( $ids );
            $items = array_values( array_filter(
                $items,
                fn( $i ) => isset( $order[ (string) $i['id'] ] )
            ) );
            // Preserve the stored order (most-recent first for 'recent').
            usort( $items, fn( $a, $b ) => $order[ (string) $a['id'] ] <=> $order[ (string) $b['id'] ] );
        }

        $total = count( $items );
        $slice = array_slice( $items, ( $page - 1 ) * $per_page, $per_page );

        return rest_ensure_response( [
            'items'    => $slice,
            'total'    => $total,
            'page'     => $page,
            'per_page' => $per_page,
            'pages'    => (int) ceil( $total / max( 1, $per_page ) ),
        ] );
    }

    // =========================================================================
    // Per-user favorites & recently used
    // =========================================================================

    private const USER_META = [
        'favorites' => '_jalpi_tpl_favorites',
        'recent'    => '_jalpi_tpl_recent',
    ];

    /** Read a per-user id list. Ids are strings so `kit:*` ids round-trip. */
    private static function user_list( string $which ): array {
        $key = self::USER_META[ $which ] ?? '';
        if ( ! $key ) {
            return [];
        }
        $raw = get_user_meta( get_current_user_id(), $key, true );
        return is_array( $raw ) ? array_values( array_map( 'strval', $raw ) ) : [];
    }

    private static function save_user_list( string $which, array $ids ): void {
        $key = self::USER_META[ $which ] ?? '';
        if ( ! $key ) {
            return;
        }
        update_user_meta( get_current_user_id(), $key, array_values( array_map( 'strval', $ids ) ) );
    }

    public function get_user_lists( WP_REST_Request $request ): WP_REST_Response {
        return rest_ensure_response( [
            'favorites' => self::user_list( 'favorites' ),
            'recent'    => self::user_list( 'recent' ),
        ] );
    }

    /** Toggle a favorite. Returns the updated list. */
    public function toggle_favorite( WP_REST_Request $request ): WP_REST_Response {
        $id   = $this->sanitize_template_id( (string) $request->get_param( 'id' ) );
        $list = self::user_list( 'favorites' );
        $pos  = array_search( $id, $list, true );

        if ( false === $pos ) {
            array_unshift( $list, $id );
        } else {
            array_splice( $list, (int) $pos, 1 );
        }
        $list = array_slice( $list, 0, 200 );
        self::save_user_list( 'favorites', $list );

        return rest_ensure_response( [
            'favorites' => $list,
            'favorited' => false === $pos,
        ] );
    }

    /** Record a template as recently used (most-recent first, capped at 24). */
    public function push_recent( WP_REST_Request $request ): WP_REST_Response {
        $id   = $this->sanitize_template_id( (string) $request->get_param( 'id' ) );
        $list = array_values( array_filter( self::user_list( 'recent' ), fn( $x ) => $x !== $id ) );
        array_unshift( $list, $id );
        $list = array_slice( $list, 0, 24 );
        self::save_user_list( 'recent', $list );

        return rest_ensure_response( [ 'recent' => $list ] );
    }

    /**
     * Template ids are either a numeric post id or a virtual `kit:<kit>:<slug>`
     * id. Sanitize both shapes without mangling the colons.
     */
    private function sanitize_template_id( string $id ): string {
        if ( 0 === strpos( $id, 'kit:' ) ) {
            $parts = explode( ':', $id, 3 );
            if ( 3 === count( $parts ) ) {
                $slug   = $parts[2];
                $suffix = '';
                if ( false !== strpos( $slug, '--' ) ) {
                    [ $slug, $cat ] = explode( '--', $slug, 2 );
                    $suffix = '--' . sanitize_key( $cat );
                }
                return 'kit:' . sanitize_key( $parts[1] ) . ':' . sanitize_title( $slug ) . $suffix;
            }
            return '';
        }
        return (string) absint( $id );
    }

    public function get_template( WP_REST_Request $request ): WP_REST_Response {
        $post = get_post( absint( $request->get_param( 'id' ) ) );
        return rest_ensure_response( $this->format_template( $post, true ) );
    }

    /**
     * Resolve any virtual kit template from a base64url-encoded id.
     * Handles `kit:`, `kitsec:` and `kitchrome:` uniformly.
     */
    public function get_kit_template_by_eid( WP_REST_Request $request ) {
        $eid = (string) $request->get_param( 'eid' );
        $id  = base64_decode( strtr( $eid, '-_', '+/' ), true );

        if ( false === $id || ! preg_match( '/^kit(?:sec|chrome)?:[A-Za-z0-9:_-]+$/', $id ) ) {
            return new WP_Error( 'bad_id', __( 'Invalid template id.', 'jalpi-page-builder' ), [ 'status' => 400 ] );
        }

        $item = Jalpi_Kit_Templates::find( $id );
        if ( null === $item ) {
            return new WP_Error( 'not_found', __( 'Kit template not found.', 'jalpi-page-builder' ), [ 'status' => 404 ] );
        }
        return rest_ensure_response( $item );
    }

    /** Resolve a read-only kit template, canvas_data included. */
    public function get_kit_template( WP_REST_Request $request ) {
        // Jalpi_Kit_Templates::find() handles the optional `@<category>`
        // disambiguator, so pass the raw slug straight through to it.
        $id = 'kit:' . sanitize_key( (string) $request->get_param( 'kit' ) )
            . ':' . (string) $request->get_param( 'slug' );
        $item = Jalpi_Kit_Templates::find( $id );

        if ( null === $item ) {
            return new WP_Error(
                'not_found',
                __( 'Kit template not found.', 'jalpi-page-builder' ),
                [ 'status' => 404 ]
            );
        }
        return rest_ensure_response( $item );
    }

    public function create_template( WP_REST_Request $request ) {
        $type = $request->get_param( 'type' );
        $data = $this->sanitize_template_data( $type, $request->get_param( 'data' ) );
        if ( is_wp_error( $data ) ) {
            return $data;
        }

        $post_id = wp_insert_post( [
            'post_type'    => self::CPT,
            'post_status'  => 'publish',
            'post_title'   => $request->get_param( 'name' ),
            'post_excerpt' => $request->get_param( 'description' ) ?? '',
            'post_content' => wp_json_encode( $data ),
        ], true );

        if ( is_wp_error( $post_id ) ) {
            return $post_id;
        }

        $category = $request->get_param( 'category' ) ?? '';
        if ( $category && array_key_exists( $category, self::CATEGORIES ) ) {
            update_post_meta( $post_id, '_jalpi_tpl_category', $category );
        }
        update_post_meta( $post_id, '_jalpi_tpl_type', $type );

        return rest_ensure_response( $this->format_template( get_post( $post_id ), true ) );
    }

    public function update_template( WP_REST_Request $request ) {
        $id      = absint( $request->get_param( 'id' ) );
        $update  = [ 'ID' => $id ];

        if ( $request->has_param( 'name' ) ) {
            $update['post_title'] = sanitize_text_field( $request->get_param( 'name' ) );
        }
        if ( $request->has_param( 'description' ) ) {
            $update['post_excerpt'] = sanitize_textarea_field( $request->get_param( 'description' ) );
        }
        if ( count( $update ) > 1 ) {
            $result = wp_update_post( $update, true );
            if ( is_wp_error( $result ) ) {
                return $result;
            }
        }
        if ( $request->has_param( 'category' ) ) {
            $cat = sanitize_key( $request->get_param( 'category' ) );
            if ( '' === $cat || array_key_exists( $cat, self::CATEGORIES ) ) {
                update_post_meta( $id, '_jalpi_tpl_category', $cat );
            }
        }
        if ( $request->has_param( 'thumbnail' ) ) {
            $thumb = absint( $request->get_param( 'thumbnail' ) );
            $thumb ? update_post_meta( $id, '_jalpi_tpl_thumbnail', $thumb )
                   : delete_post_meta( $id, '_jalpi_tpl_thumbnail' );
        }

        return rest_ensure_response( $this->format_template( get_post( $id ), true ) );
    }

    public function delete_template( WP_REST_Request $request ) {
        $id     = absint( $request->get_param( 'id' ) );
        $result = wp_delete_post( $id, true );

        if ( ! $result ) {
            return new WP_Error( 'delete_failed', __( 'Failed to delete template.', 'jalpi-page-builder' ), [ 'status' => 500 ] );
        }

        return rest_ensure_response( [ 'deleted' => true, 'id' => $id ] );
    }

    public function duplicate_template( WP_REST_Request $request ) {
        $id   = absint( $request->get_param( 'id' ) );
        $post = get_post( $id );

        if ( ! $post ) {
            return new WP_Error( 'not_found', __( 'Template not found.', 'jalpi-page-builder' ), [ 'status' => 404 ] );
        }

        $new_id = wp_insert_post( [
            'post_type'    => self::CPT,
            'post_status'  => 'publish',
            'post_title'   => $post->post_title . ' (Copy)',
            'post_excerpt' => $post->post_excerpt,
            'post_content' => $post->post_content,
        ], true );

        if ( is_wp_error( $new_id ) ) {
            return $new_id;
        }

        foreach ( get_post_meta( $id ) as $key => $values ) {
            foreach ( $values as $value ) {
                update_post_meta( $new_id, $key, maybe_unserialize( $value ) );
            }
        }

        return rest_ensure_response( $this->format_template( get_post( $new_id ), true ) );
    }

    public function export_template( WP_REST_Request $request ): WP_REST_Response {
        $id   = absint( $request->get_param( 'id' ) );
        $post = get_post( $id );

        return rest_ensure_response( [
            'name'        => sanitize_text_field( $post->post_title ),
            'description' => sanitize_textarea_field( $post->post_excerpt ),
            'category'    => sanitize_key( get_post_meta( $id, '_jalpi_tpl_category', true ) ),
            'type'        => sanitize_key( get_post_meta( $id, '_jalpi_tpl_type', true ) ),
            'version'     => JALPI_VERSION,
            'data'        => json_decode( $post->post_content, true ),
        ] );
    }

    public function import_template( WP_REST_Request $request ) {
        $json    = $request->get_param( 'json' );
        $name    = $request->get_param( 'name' );
        $decoded = json_decode( $json, true );

        if ( null === $decoded || ! is_array( $decoded ) ) {
            return new WP_Error( 'invalid_json', __( 'Invalid JSON.', 'jalpi-page-builder' ), [ 'status' => 400 ] );
        }

        $type = sanitize_key( $decoded['type'] ?? '' );
        if ( ! in_array( $type, [ 'page', 'section' ], true ) ) {
            return new WP_Error( 'invalid_type', __( 'JSON must contain a valid "type" field (page or section).', 'jalpi-page-builder' ), [ 'status' => 400 ] );
        }

        $raw_data = $decoded['data'] ?? null;
        if ( ! $raw_data ) {
            return new WP_Error( 'missing_data', __( 'JSON must contain a "data" field.', 'jalpi-page-builder' ), [ 'status' => 400 ] );
        }

        $data = $this->sanitize_template_data( $type, $raw_data );
        if ( is_wp_error( $data ) ) {
            return $data;
        }

        $template_name = $name ?: sanitize_text_field( $decoded['name'] ?? __( 'Imported Template', 'jalpi-page-builder' ) );
        $category      = sanitize_key( $decoded['category'] ?? '' );

        $post_id = wp_insert_post( [
            'post_type'    => self::CPT,
            'post_status'  => 'publish',
            'post_title'   => $template_name,
            'post_excerpt' => sanitize_textarea_field( $decoded['description'] ?? '' ),
            'post_content' => wp_json_encode( $data ),
        ], true );

        if ( is_wp_error( $post_id ) ) {
            return $post_id;
        }

        if ( $category && array_key_exists( $category, self::CATEGORIES ) ) {
            update_post_meta( $post_id, '_jalpi_tpl_category', $category );
        }
        update_post_meta( $post_id, '_jalpi_tpl_type', $type );

        return rest_ensure_response( $this->format_template( get_post( $post_id ), true ) );
    }

    // =========================================================================
    // Helpers
    // =========================================================================

    private function format_template( WP_Post $post, bool $include_data ): array {
        $id       = $post->ID;
        $type     = sanitize_key( get_post_meta( $id, '_jalpi_tpl_type',     true ) ) ?: 'page';
        $category = sanitize_key( get_post_meta( $id, '_jalpi_tpl_category', true ) );
        $thumb_id = absint( get_post_meta( $id, '_jalpi_tpl_thumbnail', true ) );
        $thumb    = $thumb_id ? esc_url_raw( wp_get_attachment_image_url( $thumb_id, 'medium' ) ) : '';

        $tags = get_post_meta( $id, '_jalpi_tpl_tags', true );
        $tags = is_array( $tags ) ? array_values( array_filter( array_map( 'sanitize_key', $tags ) ) ) : [];

        $item = [
            'id'             => $id,
            'name'           => sanitize_text_field( $post->post_title ),
            'description'    => sanitize_textarea_field( $post->post_excerpt ),
            'type'           => $type,
            'category'       => $category,
            'category_label' => self::CATEGORIES[ $category ] ?? '',
            'thumbnail'      => $thumb,
            'tags'           => $tags,
            'source'         => 'user',
            'kit'            => '',
            'readonly'       => false,
            'created'        => $post->post_date,
            'modified'       => $post->post_modified,
        ];

        if ( $include_data ) {
            $item['data'] = json_decode( $post->post_content, true );
        }

        return $item;
    }

    private function sanitize_template_data( string $type, $raw ) {
        $raw = (array) $raw;

        if ( 'page' === $type ) {
            if ( ! isset( $raw['sections'] ) ) {
                return new WP_Error( 'invalid_data', __( 'Page template must contain a "sections" array.', 'jalpi-page-builder' ), [ 'status' => 400 ] );
            }
            return Jalpi_Sanitizer::sanitize_page_data( $raw );
        }

        if ( ( $raw['type'] ?? '' ) !== 'section' ) {
            return new WP_Error( 'invalid_data', __( 'Section template data must be a section node.', 'jalpi-page-builder' ), [ 'status' => 400 ] );
        }
        return Jalpi_Sanitizer::sanitize_node( $raw );
    }
}
