<?php
/**
 * REST API Controller for Proto Preview WordPress CPTs
 */

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

class Proto_Preview_REST {

    protected $namespace = 'proto-preview/v1';

    public function init() {
        add_action('rest_api_init', [$this, 'register_routes']);
    }

    public function register_routes() {
        // Projects List (CPT proto_project)
        register_rest_route($this->namespace, '/projects', [
            [
                'methods' => WP_REST_Server::READABLE,
                'callback' => [$this, 'get_projects'],
                'permission_callback' => '__return_true'
            ],
            [
                'methods' => WP_REST_Server::CREATABLE,
                'callback' => [$this, 'create_project'],
                'permission_callback' => '__return_true'
            ]
        ]);

        register_rest_route($this->namespace, '/projects/(?P<id>\d+)', [
            [
                'methods' => WP_REST_Server::READABLE,
                'callback' => [$this, 'get_project'],
                'permission_callback' => '__return_true'
            ]
        ]);

        // Layout Variation Creation & Management
        register_rest_route($this->namespace, '/layouts', [
            [
                'methods' => WP_REST_Server::CREATABLE,
                'callback' => [$this, 'create_layout'],
                'permission_callback' => '__return_true'
            ]
        ]);

        register_rest_route($this->namespace, '/layouts/(?P<id>\d+)', [
            [
                'methods' => WP_REST_Server::DELETABLE,
                'callback' => [$this, 'delete_layout'],
                'permission_callback' => '__return_true'
            ]
        ]);

        // File / Image Upload Endpoint
        register_rest_route($this->namespace, '/upload', [
            [
                'methods' => WP_REST_Server::CREATABLE,
                'callback' => [$this, 'upload_file'],
                'permission_callback' => '__return_true'
            ]
        ]);

        // Layout Variation Choice Selection
        register_rest_route($this->namespace, '/layouts/choose', [
            [
                'methods' => WP_REST_Server::CREATABLE,
                'callback' => [$this, 'choose_variation'],
                'permission_callback' => '__return_true'
            ]
        ]);

        // Update Screen Status (Progress Tracking)
        register_rest_route($this->namespace, '/projects/(?P<id>\d+)/screens/(?P<screen_id>\d+)/status', [
            [
                'methods' => WP_REST_Server::CREATABLE,
                'callback' => [$this, 'update_screen_status'],
                'permission_callback' => '__return_true'
            ]
        ]);

        // Comments
        register_rest_route($this->namespace, '/comments', [
            [
                'methods' => WP_REST_Server::READABLE,
                'callback' => [$this, 'get_comments'],
                'permission_callback' => '__return_true'
            ],
            [
                'methods' => WP_REST_Server::CREATABLE,
                'callback' => [$this, 'create_comment'],
                'permission_callback' => '__return_true'
            ]
        ]);

        register_rest_route($this->namespace, '/comments/resolve', [
            [
                'methods' => WP_REST_Server::CREATABLE,
                'callback' => [$this, 'resolve_comment'],
                'permission_callback' => '__return_true'
            ]
        ]);

        register_rest_route($this->namespace, '/comments/reply', [
            [
                'methods' => WP_REST_Server::CREATABLE,
                'callback' => [$this, 'add_reply'],
                'permission_callback' => '__return_true'
            ]
        ]);

        register_rest_route($this->namespace, '/comments/(?P<id>\d+)', [
            [
                'methods' => [WP_REST_Server::DELETABLE, 'POST'],
                'callback' => [$this, 'delete_comment'],
                'permission_callback' => '__return_true'
            ]
        ]);

        register_rest_route($this->namespace, '/comments/delete', [
            [
                'methods' => WP_REST_Server::CREATABLE,
                'callback' => [$this, 'delete_comment'],
                'permission_callback' => '__return_true'
            ]
        ]);

        register_rest_route($this->namespace, '/comments/move', [
            [
                'methods' => WP_REST_Server::CREATABLE,
                'callback' => [$this, 'move_comment'],
                'permission_callback' => '__return_true'
            ]
        ]);

        register_rest_route($this->namespace, '/comments/toggle-lock', [
            [
                'methods' => WP_REST_Server::CREATABLE,
                'callback' => [$this, 'toggle_lock_comment'],
                'permission_callback' => '__return_true'
            ]
        ]);

        register_rest_route($this->namespace, '/comments/update', [
            [
                'methods' => WP_REST_Server::CREATABLE,
                'callback' => [$this, 'update_comment'],
                'permission_callback' => '__return_true'
            ]
        ]);

        register_rest_route($this->namespace, '/comments/(?P<id>\d+)/update', [
            [
                'methods' => WP_REST_Server::CREATABLE,
                'callback' => [$this, 'update_comment'],
                'permission_callback' => '__return_true'
            ]
        ]);

        // Approvals
        register_rest_route($this->namespace, '/approvals', [
            [
                'methods' => WP_REST_Server::READABLE,
                'callback' => [$this, 'get_approvals'],
                'permission_callback' => '__return_true'
            ],
            [
                'methods' => WP_REST_Server::CREATABLE,
                'callback' => [$this, 'save_approval'],
                'permission_callback' => '__return_true'
            ]
        ]);

        // Story / Design Guide Walkthrough Steps
        register_rest_route($this->namespace, '/story-steps', [
            [
                'methods' => WP_REST_Server::READABLE,
                'callback' => [$this, 'get_story_steps'],
                'permission_callback' => '__return_true'
            ],
            [
                'methods' => WP_REST_Server::CREATABLE,
                'callback' => [$this, 'create_story_step'],
                'permission_callback' => '__return_true'
            ]
        ]);

        register_rest_route($this->namespace, '/story-steps/(?P<id>\d+)', [
            [
                'methods' => WP_REST_Server::EDITABLE,
                'callback' => [$this, 'update_story_step'],
                'permission_callback' => '__return_true'
            ],
            [
                'methods' => WP_REST_Server::DELETABLE,
                'callback' => [$this, 'delete_story_step'],
                'permission_callback' => '__return_true'
            ]
        ]);

        register_rest_route($this->namespace, '/story-steps/(?P<id>\d+)/approve', [
            [
                'methods' => WP_REST_Server::CREATABLE,
                'callback' => [$this, 'approve_story_step'],
                'permission_callback' => '__return_true'
            ]
        ]);

        // Users
        register_rest_route($this->namespace, '/users', [
            [
                'methods' => WP_REST_Server::READABLE,
                'callback' => [$this, 'get_users'],
                'permission_callback' => '__return_true'
            ]
        ]);

        register_rest_route($this->namespace, '/switch-user', [
            [
                'methods' => WP_REST_Server::CREATABLE,
                'callback' => [$this, 'switch_user'],
                'permission_callback' => '__return_true'
            ]
        ]);

        // Screens CRUD & Reorder Management
        register_rest_route($this->namespace, '/projects/(?P<id>\d+)/screens', [
            [
                'methods' => WP_REST_Server::CREATABLE,
                'callback' => [$this, 'create_screen'],
                'permission_callback' => '__return_true'
            ]
        ]);

        register_rest_route($this->namespace, '/projects/(?P<id>\d+)/screens/reorder', [
            [
                'methods' => WP_REST_Server::CREATABLE,
                'callback' => [$this, 'reorder_screens'],
                'permission_callback' => '__return_true'
            ]
        ]);

        register_rest_route($this->namespace, '/projects/(?P<id>\d+)/screens/(?P<screen_id>\d+)/status', [
            [
                'methods' => WP_REST_Server::CREATABLE,
                'callback' => [$this, 'update_screen_status'],
                'permission_callback' => '__return_true'
            ]
        ]);

        register_rest_route($this->namespace, '/projects/(?P<id>\d+)/screens/bulk-update', [
            [
                'methods' => WP_REST_Server::CREATABLE,
                'callback' => [$this, 'bulk_update_screens'],
                'permission_callback' => '__return_true'
            ]
        ]);

        register_rest_route($this->namespace, '/projects/(?P<id>\d+)/screens/(?P<screen_id>\d+)', [
            [
                'methods' => WP_REST_Server::DELETABLE,
                'callback' => [$this, 'delete_screen'],
                'permission_callback' => '__return_true'
            ]
        ]);
    }

    public function get_projects(WP_REST_Request $request) {
        $posts = get_posts([
            'post_type'      => 'proto_project',
            'post_status'    => 'publish',
            'numberposts'    => -1,
            'orderby'        => 'date',
            'order'          => 'ASC'
        ]);

        $projects = [];
        foreach ($posts as $post) {
            $projects[] = $this->format_project_post($post);
        }

        return rest_ensure_response($projects);
    }

    public function get_project(WP_REST_Request $request) {
        $id = (int)$request->get_param('id');
        $post = get_post($id);
        if (!$post || $post->post_type !== 'proto_project') {
            return new WP_Error('not_found', 'Project not found', ['status' => 404]);
        }
        return rest_ensure_response($this->format_project_post($post));
    }

    public function format_project_post($post) {
        $screens = get_post_meta($post->ID, '_proto_screens', true);
        if (!is_array($screens) || empty($screens)) {
            $screens = [
                [
                    'id' => 1,
                    'name' => 'Homepage',
                    'slug' => 'home',
                    'layouts' => [
                        [
                            'id' => 1,
                            'version' => 'Option 1',
                            'name' => $post->post_title . ' Option 1',
                            'file_path' => PROTO_PREVIEW_PLUGIN_URL . 'layouts/saas-v1.html',
                            'badge' => 'Initial Draft',
                            'color' => 'red',
                            'is_selected_choice' => 1
                        ]
                    ]
                ]
            ];
        }

        $is_designer_user = is_user_logged_in() && (current_user_can('manage_options') || current_user_can('edit_posts'));

        // Normalize visibility & checkpoint for screens:
        // If a screen has layouts/mockups built, default to client-facing.
        // If a screen is a blank template without layouts, default to internal draft!
        foreach ($screens as &$screen) {
            if (!isset($screen['visibility'])) {
                $has_layouts = !empty($screen['layouts']);
                $screen['visibility'] = $has_layouts ? 'client' : 'internal';
            }
            if (!isset($screen['is_master_checkpoint'])) {
                $is_home = (($screen['slug'] ?? '') === 'home' || ($screen['id'] ?? 0) === 1);
                $screen['is_master_checkpoint'] = $is_home ? 1 : 0;
            }
        }
        unset($screen);

        // For non-logged-in visitors / client reviewers, strictly exclude internal draft screens from delivery
        if (!$is_designer_user) {
            $screens = array_values(array_filter($screens, function($s) {
                return ($s['visibility'] ?? 'client') !== 'internal';
            }));
        }

        // Get Client Organization Name
        $client_name = get_post_meta($post->ID, '_proto_client_name', true);
        if (empty($client_name)) {
            $client_terms = wp_get_post_terms($post->ID, 'proto_client', ['fields' => 'names']);
            $client_name = !empty($client_terms) ? $client_terms[0] : 'Client Stakeholder';
        }

        // Fetch story note counts per layout
        global $wpdb;
        $table_comments = $wpdb->prefix . 'pp_comments';
        $story_counts_rows = $wpdb->get_results("SELECT layout_id, COUNT(*) as cnt FROM $table_comments WHERE comment_type = 'story' GROUP BY layout_id", ARRAY_A);
        $story_counts = [];
        if (is_array($story_counts_rows)) {
            foreach ($story_counts_rows as $row) {
                $story_counts[(int)$row['layout_id']] = (int)$row['cnt'];
            }
        }

        // Extract flattened layouts and attach story_count
        $all_layouts = [];
        foreach ($screens as &$screen) {
            if (isset($screen['layouts']) && is_array($screen['layouts'])) {
                foreach ($screen['layouts'] as &$l) {
                    $l['story_count'] = (int)($story_counts[(int)$l['id']] ?? 0);
                    $all_layouts[] = $l;
                }
            }
        }
        unset($screen);

        // Fetch assigned team members
        $assigned_user_ids = get_post_meta($post->ID, '_proto_assigned_users', true);
        if (!is_array($assigned_user_ids) || empty($assigned_user_ids)) {
            $all_users = get_users(['number' => 5]);
            $assigned_user_ids = !empty($all_users) ? wp_list_pluck($all_users, 'ID') : [1];
        }

        $user_project_roles = get_post_meta($post->ID, '_proto_user_project_roles', true) ?: [];

        $team_members = [];
        foreach ($assigned_user_ids as $u_id) {
            $u = get_user_by('id', (int)$u_id);
            if ($u) {
                $custom_role = !empty($user_project_roles[$u->ID]) 
                    ? $user_project_roles[$u->ID] 
                    : ($u->user_email === 'rian@rian.ca' ? 'Project Manager' : ($u->ID == 1 ? 'Designer' : (user_can($u->ID, 'manage_options') ? 'Designer' : 'Client Reviewer')));

                $team_members[] = [
                    'id'     => $u->ID,
                    'name'   => $u->display_name,
                    'email'  => $u->user_email,
                    'role'   => $custom_role,
                    'avatar' => get_avatar_url($u->ID)
                ];
            }
        }

        $client_stakeholders = get_post_meta($post->ID, '_proto_client_stakeholders', true);
        if (!is_array($client_stakeholders)) {
            $client_stakeholders = [];
        }

        return [
            'id'                     => $post->ID,
            'name'                   => $post->post_title,
            'client_name'            => $client_name,
            'description'            => $post->post_content,
            'share_token'            => get_post_meta($post->ID, '_proto_share_token', true),
            'pages'                  => $screens,
            'layouts'                => $all_layouts,
            'team'                   => $team_members,
            'client_stakeholders'    => $client_stakeholders,
            'notification_emails'    => get_post_meta($post->ID, '_proto_notification_emails', true) ?: '',
            'notifications_enabled'  => get_post_meta($post->ID, '_proto_enable_notifications', true) !== '0'
        ];
    }

    public function choose_variation(WP_REST_Request $request) {
        $params = $request->get_json_params() ?: $request->get_params();
        $project_id = (int)($params['project_id'] ?? 0);
        $layout_id = (int)($params['layout_id'] ?? 0);

        if ($project_id <= 0) {
            $posts = get_posts(['post_type' => 'proto_project', 'numberposts' => 1]);
            if (!empty($posts)) $project_id = $posts[0]->ID;
        }

        $screens = get_post_meta($project_id, '_proto_screens', true);
        if (is_array($screens)) {
            foreach ($screens as &$screen) {
                $has_layout = false;
                if (isset($screen['layouts']) && is_array($screen['layouts'])) {
                    foreach ($screen['layouts'] as $l) {
                        if ($l['id'] == $layout_id) $has_layout = true;
                    }
                    if ($has_layout) {
                        foreach ($screen['layouts'] as &$l) {
                            $l['is_selected_choice'] = ($l['id'] == $layout_id) ? 1 : 0;
                        }
                    }
                }
            }
            update_post_meta($project_id, '_proto_screens', $screens);
        }

        return rest_ensure_response(['success' => true, 'project_id' => $project_id, 'layout_id' => $layout_id]);
    }


    public function upload_file(WP_REST_Request $request) {
        if (!function_exists('wp_handle_upload')) {
            require_once(ABSPATH . 'wp-admin/includes/file.php');
            require_once(ABSPATH . 'wp-admin/includes/image.php');
            require_once(ABSPATH . 'wp-admin/includes/media.php');
        }

        $files = $request->get_file_params();
        if (empty($files['file'])) {
            return new WP_Error('no_file', 'No file provided', ['status' => 400]);
        }

        $upload_overrides = ['test_form' => false];
        $movefile = wp_handle_upload($files['file'], $upload_overrides);

        if ($movefile && !isset($movefile['error'])) {
            $url = $movefile['url'];
            $type = $movefile['type'] ?? '';
            $is_image = strpos($type, 'image/') === 0 || preg_match('/\.(png|jpe?g|webp|svg|gif)$/i', $url);

            return rest_ensure_response([
                'success'   => true,
                'url'       => $url,
                'file_type' => $is_image ? 'image' : 'html',
                'mime_type' => $type,
                'file_name' => basename($url)
            ]);
        } else {
            return new WP_Error('upload_error', $movefile['error'] ?? 'Upload failed', ['status' => 500]);
        }
    }

    public function create_layout(WP_REST_Request $request) {
        $params = $request->get_json_params() ?: $request->get_params();
        $project_id = (int)($params['project_id'] ?? 0);
        $page_id = (int)($params['page_id'] ?? 1);
        $name = sanitize_text_field($params['name'] ?? 'New Variation');
        $badge = sanitize_text_field($params['badge'] ?? 'New Option');
        $file_path = esc_url_raw($params['file_path'] ?? '');

        // Handle uploaded file if present in multipart request
        $files = $request->get_file_params();
        if (!empty($files['file'])) {
            if (!function_exists('wp_handle_upload')) {
                require_once(ABSPATH . 'wp-admin/includes/file.php');
            }
            $uploaded = wp_handle_upload($files['file'], ['test_form' => false]);
            if ($uploaded && empty($uploaded['error'])) {
                $file_path = $uploaded['url'];
            }
        }

        if ($project_id <= 0) {
            $posts = get_posts(['post_type' => 'proto_project', 'numberposts' => 1]);
            if (!empty($posts)) $project_id = $posts[0]->ID;
        }

        $screens = get_post_meta($project_id, '_proto_screens', true);
        if (!is_array($screens)) $screens = [];

        // Determine next layout ID and version
        $max_id = 0;
        $count_for_page = 0;
        foreach ($screens as &$s) {
            if (isset($s['layouts']) && is_array($s['layouts'])) {
                foreach ($s['layouts'] as $l) {
                    if (isset($l['id']) && (int)$l['id'] > $max_id) $max_id = (int)$l['id'];
                }
                if ((int)($s['id'] ?? 0) === $page_id) {
                    $count_for_page = count($s['layouts']);
                }
            }
        }
        $new_id = $max_id + 1;
        $version = 'Option ' . ($count_for_page + 1);

        $new_layout = [
            'id'                 => $new_id,
            'version'            => $version,
            'name'               => $name,
            'file_path'          => $file_path,
            'badge'              => $badge,
            'color'              => 'red',
            'is_selected_choice' => 0
        ];

        // Append to page
        $appended = false;
        foreach ($screens as &$s) {
            if ((int)($s['id'] ?? 0) === $page_id) {
                if (!isset($s['layouts']) || !is_array($s['layouts'])) $s['layouts'] = [];
                $s['layouts'][] = $new_layout;
                $appended = true;
                break;
            }
        }

        if (!$appended && !empty($screens)) {
            $screens[0]['layouts'][] = $new_layout;
        }

        update_post_meta($project_id, '_proto_screens', $screens);

        return rest_ensure_response($new_layout);
    }

    public function delete_layout(WP_REST_Request $request) {
        $layout_id = (int)$request->get_param('id');
        $posts = get_posts(['post_type' => 'proto_project', 'numberposts' => -1]);

        foreach ($posts as $p) {
            $screens = get_post_meta($p->ID, '_proto_screens', true);
            if (is_array($screens)) {
                $changed = false;
                foreach ($screens as &$s) {
                    if (isset($s['layouts']) && is_array($s['layouts'])) {
                        $before = count($s['layouts']);
                        $s['layouts'] = array_values(array_filter($s['layouts'], function($l) use ($layout_id) {
                            return (int)$l['id'] !== $layout_id;
                        }));
                        if (count($s['layouts']) !== $before) $changed = true;
                    }
                }
                if ($changed) {
                    update_post_meta($p->ID, '_proto_screens', $screens);
                }
            }
        }

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

    private function ensure_comments_table_schema() {
        global $wpdb;
        require_once(ABSPATH . 'wp-admin/includes/upgrade.php');
        $charset_collate = $wpdb->get_charset_collate();

        $table_comments = $wpdb->prefix . 'pp_comments';
        $sql_comments = "CREATE TABLE $table_comments (
            id bigint(20) NOT NULL AUTO_INCREMENT,
            layout_id bigint(20) NOT NULL,
            user_id bigint(20) NOT NULL DEFAULT 0,
            author_name varchar(150) NOT NULL DEFAULT '',
            author_email varchar(150) NOT NULL DEFAULT '',
            x_percent double NOT NULL,
            y_percent double NOT NULL,
            title varchar(255) NOT NULL DEFAULT '',
            text text NOT NULL,
            comment_type varchar(30) NOT NULL DEFAULT 'comment',
            action_type varchar(50) NOT NULL DEFAULT 'none',
            action_target text NOT NULL,
            action_label varchar(255) NOT NULL DEFAULT '',
            action_duration int(5) NOT NULL DEFAULT 3,
            spotlight_mode varchar(20) NOT NULL DEFAULT 'spotlight',
            is_resolved int(1) DEFAULT 0,
            is_locked int(1) DEFAULT 1,
            resolved_by bigint(20),
            resolved_at datetime,
            created_at datetime DEFAULT CURRENT_TIMESTAMP,
            PRIMARY KEY  (id),
            KEY layout_id (layout_id)
        ) $charset_collate;";
        dbDelta($sql_comments);

        $table_replies = $wpdb->prefix . 'pp_comment_replies';
        $sql_replies = "CREATE TABLE $table_replies (
            id bigint(20) NOT NULL AUTO_INCREMENT,
            comment_id bigint(20) NOT NULL,
            user_id bigint(20) NOT NULL DEFAULT 0,
            author_name varchar(150) NOT NULL DEFAULT '',
            author_email varchar(150) NOT NULL DEFAULT '',
            text text NOT NULL,
            created_at datetime DEFAULT CURRENT_TIMESTAMP,
            PRIMARY KEY  (id),
            KEY comment_id (comment_id)
        ) $charset_collate;";
        dbDelta($sql_replies);
    }

    public function get_comments(WP_REST_Request $request) {
        global $wpdb;
        $this->ensure_comments_table_schema();
        $table_comments = $wpdb->prefix . 'pp_comments';
        $table_replies = $wpdb->prefix . 'pp_comment_replies';
        $layout_id = (int)$request->get_param('layout_id');

        if (!headers_sent()) {
            header("Cache-Control: no-store, no-cache, must-revalidate, max-age=0");
            header("Pragma: no-cache");
        }

        if ($layout_id > 0) {
            $comments = $wpdb->get_results($wpdb->prepare("SELECT * FROM $table_comments WHERE layout_id = %d ORDER BY id ASC", $layout_id), ARRAY_A);
        } else {
            $comments = $wpdb->get_results("SELECT * FROM $table_comments ORDER BY id DESC", ARRAY_A);
        }

        if (!is_array($comments)) {
            $comments = [];
        }

        foreach ($comments as &$c) {
            $replies = $wpdb->get_results($wpdb->prepare("SELECT * FROM $table_replies WHERE comment_id = %d ORDER BY id ASC", $c['id']), ARRAY_A) ?: [];
            $user = ($c['user_id'] > 0) ? get_user_by('id', $c['user_id']) : null;
            $disp_name = $user ? $user->display_name : (!empty($c['author_name']) ? $c['author_name'] : 'Guest Reviewer');
            $disp_email = $user ? $user->user_email : (!empty($c['author_email']) ? $c['author_email'] : '');
            
            $avatar_url = $user 
                ? get_avatar_url($user->ID) 
                : ($disp_email 
                    ? get_avatar_url($disp_email, ['default' => 'https://ui-avatars.com/api/?name=' . urlencode(substr($disp_name, 0, 1)) . '&background=b02b2b&color=ffffff&bold=true']) 
                    : 'https://ui-avatars.com/api/?name=' . urlencode(substr($disp_name, 0, 1)) . '&background=b02b2b&color=ffffff&bold=true');

            $c['comment_type'] = !empty($c['comment_type']) ? $c['comment_type'] : 'comment';
            $c['is_story'] = ($c['comment_type'] === 'story');
            $c['title'] = !empty($c['title']) ? $c['title'] : '';
            $c['action_type'] = !empty($c['action_type']) ? $c['action_type'] : 'none';
            $c['action_target'] = !empty($c['action_target']) ? $c['action_target'] : '';
            $c['action_label'] = !empty($c['action_label']) ? $c['action_label'] : '';
            $c['action_duration'] = isset($c['action_duration']) ? (int)$c['action_duration'] : 3;
            $c['spotlight_mode'] = !empty($c['spotlight_mode']) ? $c['spotlight_mode'] : 'spotlight';
            $is_author_designer = ($user && (user_can($user->ID, 'manage_options') || user_can($user->ID, 'edit_posts'))) || ($c['comment_type'] === 'story');

            $c['author'] = [
                'id'     => (int)$c['user_id'],
                'name'   => $disp_name,
                'email'  => $disp_email,
                'role'   => $is_author_designer ? 'designer' : 'client',
                'avatar' => $avatar_url
            ];
            $c['is_resolved'] = (int)$c['is_resolved'];
            $c['resolved'] = ($c['is_resolved'] === 1);
            $c['createdAt'] = date('M j, g:i a', strtotime($c['created_at']));
            $c['replies'] = array_map(function($rep) {
                $r_user = ($rep['user_id'] > 0) ? get_user_by('id', $rep['user_id']) : null;
                $r_name = $r_user ? $r_user->display_name : (!empty($rep['author_name']) ? $rep['author_name'] : 'Reviewer');
                $r_email = $r_user ? $r_user->user_email : (!empty($rep['author_email']) ? $rep['author_email'] : '');
                $r_avatar = $r_user 
                    ? get_avatar_url($r_user->ID) 
                    : ($r_email 
                        ? get_avatar_url($r_email, ['default' => 'https://ui-avatars.com/api/?name=' . urlencode(substr($r_name, 0, 1)) . '&background=b02b2b&color=ffffff&bold=true']) 
                        : 'https://ui-avatars.com/api/?name=' . urlencode(substr($r_name, 0, 1)) . '&background=b02b2b&color=ffffff&bold=true');

                return [
                    'id'        => $rep['id'],
                    'text'      => $rep['text'],
                    'createdAt' => date('M j, g:i a', strtotime($rep['created_at'])),
                    'author'    => [
                        'id'     => (int)$rep['user_id'],
                        'name'   => $r_name,
                        'email'  => $r_email,
                        'role'   => ($r_user && (user_can($r_user->ID, 'manage_options') || user_can($r_user->ID, 'edit_posts'))) ? 'designer' : 'client',
                        'avatar' => $r_avatar
                    ]
                ];
            }, $replies);
        }

        return rest_ensure_response($comments);
    }

    public function create_comment(WP_REST_Request $request) {
        global $wpdb;
        $this->ensure_comments_table_schema();
        $table_comments = $wpdb->prefix . 'pp_comments';
        $params = $request->get_json_params() ?: $request->get_params();
        $layout_id = (int)($params['layout_id'] ?? 0);
        $x = (float)($params['x_percent'] ?? 0);
        $y = (float)($params['y_percent'] ?? 0);
        $title = sanitize_text_field($params['title'] ?? '');
        $text = sanitize_textarea_field($params['text'] ?? '');
        $comment_type = sanitize_text_field($params['comment_type'] ?? 'comment');
        if (!in_array($comment_type, ['comment', 'story'])) {
            $comment_type = 'comment';
        }
        $action_type = sanitize_text_field($params['action_type'] ?? 'none');
        $action_target = sanitize_text_field($params['action_target'] ?? '');
        $action_label = sanitize_text_field($params['action_label'] ?? '');
        $action_duration = isset($params['action_duration']) ? max(0, (int)$params['action_duration']) : 3;
        $spotlight_mode = sanitize_text_field($params['spotlight_mode'] ?? 'spotlight');
        $author_name = sanitize_text_field($params['author_name'] ?? '');
        $author_email = sanitize_email($params['author_email'] ?? '');
        $user_id = get_current_user_id() ?: 0;
        $is_designer_user = ($user_id && (user_can($user_id, 'manage_options') || user_can($user_id, 'edit_posts')));

        // If author explicitly posted a story note or has designer privileges, preserve story type
        if ($comment_type !== 'story' && $is_designer_user && !empty($title)) {
            $comment_type = 'story';
        }

        $inserted = $wpdb->insert($table_comments, [
            'layout_id'       => $layout_id,
            'user_id'         => $user_id,
            'author_name'     => $author_name,
            'author_email'    => $author_email,
            'x_percent'       => $x,
            'y_percent'       => $y,
            'title'           => $title,
            'text'            => $text,
            'comment_type'    => $comment_type,
            'action_type'     => $action_type,
            'action_target'   => $action_target,
            'action_label'    => $action_label,
            'action_duration' => $action_duration,
            'spotlight_mode'  => $spotlight_mode,
            'is_resolved'     => 0,
            'is_locked'       => 1,
            'created_at'      => current_time('mysql')
        ]);

        if ($inserted === false || empty($wpdb->insert_id)) {
            return new WP_Error('db_error', 'Failed to save comment to database: ' . ($wpdb->last_error ?: 'Unknown DB insert failure'), ['status' => 500]);
        }
        $new_id = (int)$wpdb->insert_id;

        $user = $user_id ? get_user_by('id', $user_id) : null;
        $disp_name = $user ? $user->display_name : ($author_name ?: 'Guest Reviewer');
        $avatar_url = $user ? get_avatar_url($user_id) : ($author_email ? get_avatar_url($author_email, ['default' => 'https://ui-avatars.com/api/?name=' . urlencode(substr($disp_name, 0, 1)) . '&background=b02b2b&color=ffffff&bold=true']) : 'https://ui-avatars.com/api/?name=' . urlencode(substr($disp_name, 0, 1)) . '&background=b02b2b&color=ffffff&bold=true');

        // Dispatch Email Notifications to Project Stakeholders
        if (class_exists('Proto_Preview_Notifications')) {
            Proto_Preview_Notifications::notify_new_comment($new_id, $layout_id, $text, $user_id, $author_name, $author_email);
        }

        return rest_ensure_response([
            'id'              => $new_id,
            'layout_id'       => $layout_id,
            'x_percent'       => $x,
            'y_percent'       => $y,
            'title'           => $title,
            'text'            => $text,
            'comment_type'    => $comment_type,
            'action_type'     => $action_type,
            'action_target'   => $action_target,
            'action_label'    => $action_label,
            'action_duration' => $action_duration,
            'spotlight_mode'  => $spotlight_mode,
            'is_story'        => ($comment_type === 'story'),
            'resolved'      => false,
            'createdAt'     => 'Just now',
            'author'       => [
                'id'     => $user_id,
                'name'   => $disp_name,
                'email'  => $author_email,
                'role'   => ($is_designer_user || $comment_type === 'story') ? 'designer' : 'client',
                'avatar' => $avatar_url
            ],
            'replies'      => []
        ]);
    }

    public function resolve_comment(WP_REST_Request $request) {
        global $wpdb;
        $table_comments = $wpdb->prefix . 'pp_comments';
        $params = $request->get_json_params() ?: $request->get_params();
        $comment_id = (int)($params['comment_id'] ?? 0);
        $curr = $wpdb->get_var($wpdb->prepare("SELECT is_resolved FROM $table_comments WHERE id = %d", $comment_id));
        $new_val = $curr ? 0 : 1;
        $wpdb->update($table_comments, ['is_resolved' => $new_val], ['id' => $comment_id]);
        return rest_ensure_response(['id' => $comment_id, 'is_resolved' => (bool)$new_val]);
    }

    public function add_reply(WP_REST_Request $request) {
        global $wpdb;
        $table_replies = $wpdb->prefix . 'pp_comment_replies';
        $params = $request->get_json_params() ?: $request->get_params();
        $comment_id = (int)($params['comment_id'] ?? 0);
        $text = sanitize_textarea_field($params['text'] ?? '');
        $author_name = sanitize_text_field($params['author_name'] ?? '');
        $author_email = sanitize_email($params['author_email'] ?? '');
        $user_id = get_current_user_id() ?: 0;

        if ($comment_id <= 0 || empty($text)) {
            return new WP_Error('invalid_param', 'comment_id and text required', ['status' => 400]);
        }

        $wpdb->insert($table_replies, [
            'comment_id'   => $comment_id,
            'user_id'      => $user_id,
            'author_name'  => $author_name,
            'author_email' => $author_email,
            'text'         => $text,
            'created_at'   => current_time('mysql')
        ]);
        $new_id = $wpdb->insert_id;

        $user = $user_id ? get_user_by('id', $user_id) : null;
        $disp_name = $user ? $user->display_name : ($author_name ?: 'Reviewer');
        $avatar_url = $user ? get_avatar_url($user_id) : ($author_email ? get_avatar_url($author_email, ['default' => 'https://ui-avatars.com/api/?name=' . urlencode(substr($disp_name, 0, 1)) . '&background=b02b2b&color=ffffff&bold=true']) : 'https://ui-avatars.com/api/?name=' . urlencode(substr($disp_name, 0, 1)) . '&background=b02b2b&color=ffffff&bold=true');

        // Dispatch Email Notifications on Thread Reply
        if (class_exists('Proto_Preview_Notifications')) {
            Proto_Preview_Notifications::notify_new_reply($new_id, $comment_id, $text, $user_id, $author_name, $author_email);
        }

        return rest_ensure_response([
            'id'        => $new_id,
            'text'      => $text,
            'createdAt' => 'Just now',
            'author'    => [
                'id'     => $user_id,
                'name'   => $disp_name,
                'email'  => $author_email,
                'role'   => ($user && user_can($user_id, 'manage_options')) ? 'designer' : 'client',
                'avatar' => $avatar_url
            ]
        ]);
    }

    public function delete_comment(WP_REST_Request $request) {
        global $wpdb;
        $params = $request->get_json_params() ?: $request->get_params();
        $comment_id = (int)($request->get_param('id') ?: ($params['comment_id'] ?? ($params['id'] ?? 0)));

        if ($comment_id <= 0) {
            return new WP_Error('invalid_id', 'Comment ID required', ['status' => 400]);
        }

        $wpdb->delete($wpdb->prefix . 'pp_comment_replies', ['comment_id' => $comment_id]);
        $deleted = $wpdb->delete($wpdb->prefix . 'pp_comments', ['id' => $comment_id]);
        if ($deleted === false) {
            return new WP_Error('db_error', 'Failed to delete from database: ' . ($wpdb->last_error ?: 'DB delete failure'), ['status' => 500]);
        }
        return rest_ensure_response(['deleted' => true, 'id' => $comment_id, 'affected_rows' => $deleted]);
    }

    public function move_comment(WP_REST_Request $request) {
        global $wpdb;
        $table_comments = $wpdb->prefix . 'pp_comments';
        $params = $request->get_json_params() ?: $request->get_params();
        $comment_id = (int)($params['comment_id'] ?? 0);
        $x = (float)($params['x_percent'] ?? 0);
        $y = (float)($params['y_percent'] ?? 0);

        if ($comment_id <= 0) {
            return new WP_Error('invalid_param', 'comment_id required', ['status' => 400]);
        }

        $existing = $wpdb->get_row($wpdb->prepare("SELECT * FROM $table_comments WHERE id = %d", $comment_id), ARRAY_A);
        if (!$existing) {
            return new WP_Error('not_found', 'Comment not found', ['status' => 404]);
        }

        // If this is a Story Note, only designers can permanently save its new position in the DB
        $is_story = ($existing['comment_type'] === 'story');
        $user_id = get_current_user_id() ?: 0;
        $is_designer_user = ($user_id && (user_can($user_id, 'manage_options') || user_can($user_id, 'edit_posts')));

        if ($is_story && !$is_designer_user) {
            // Return response without modifying database so guest refresh retains original designer position
            return rest_ensure_response([
                'id' => $comment_id,
                'x_percent' => (float)$existing['x_percent'],
                'y_percent' => (float)$existing['y_percent'],
                'moved' => false,
                'notice' => 'Story note position changes by guests are temporary and not saved.'
            ]);
        }

        $wpdb->update($table_comments, [
            'x_percent' => $x,
            'y_percent' => $y
        ], ['id' => $comment_id]);

        return rest_ensure_response(['id' => $comment_id, 'x_percent' => $x, 'y_percent' => $y, 'moved' => true]);
    }

    public function toggle_lock_comment(WP_REST_Request $request) {
        global $wpdb;
        $table_comments = $wpdb->prefix . 'pp_comments';
        $params = $request->get_json_params() ?: $request->get_params();
        $comment_id = (int)($params['comment_id'] ?? 0);
        $curr = $wpdb->get_var($wpdb->prepare("SELECT is_locked FROM $table_comments WHERE id = %d", $comment_id));
        $new_val = ($curr == 1 || $curr === null) ? 0 : 1;
        $wpdb->update($table_comments, ['is_locked' => $new_val], ['id' => $comment_id]);
        return rest_ensure_response(['id' => $comment_id, 'is_locked' => (bool)$new_val]);
    }

    public function update_comment(WP_REST_Request $request) {
        global $wpdb;
        $table_comments = $wpdb->prefix . 'pp_comments';
        $params = $request->get_json_params() ?: $request->get_params();
        $comment_id = (int)($request->get_param('id') ?: ($params['comment_id'] ?? ($params['id'] ?? 0)));

        if ($comment_id <= 0) {
            return new WP_Error('invalid_id', 'Comment ID required', ['status' => 400]);
        }

        $existing = $wpdb->get_row($wpdb->prepare("SELECT * FROM $table_comments WHERE id = %d", $comment_id), ARRAY_A);
        if (!$existing) {
            return new WP_Error('not_found', 'Comment not found', ['status' => 404]);
        }

        $update_data = [];
        if (isset($params['title'])) {
            $update_data['title'] = sanitize_text_field($params['title']);
        }
        if (isset($params['text'])) {
            $update_data['text'] = sanitize_textarea_field($params['text']);
        }
        if (isset($params['action_type'])) {
            $update_data['action_type'] = sanitize_text_field($params['action_type']);
        }
        if (isset($params['action_target'])) {
            $update_data['action_target'] = sanitize_text_field($params['action_target']);
        }
        if (isset($params['action_label'])) {
            $update_data['action_label'] = sanitize_text_field($params['action_label']);
        }
        if (isset($params['action_duration'])) {
            $update_data['action_duration'] = max(0, (int)$params['action_duration']);
        }
        if (isset($params['spotlight_mode'])) {
            $update_data['spotlight_mode'] = sanitize_text_field($params['spotlight_mode']);
        }
        if (isset($params['comment_type'])) {
            $c_type = sanitize_text_field($params['comment_type']);
            if (in_array($c_type, ['comment', 'story'])) {
                $update_data['comment_type'] = $c_type;
            }
        }

        if (!empty($update_data)) {
            $wpdb->update($table_comments, $update_data, ['id' => $comment_id]);
        }

        $updated = $wpdb->get_row($wpdb->prepare("SELECT * FROM $table_comments WHERE id = %d", $comment_id), ARRAY_A);
        return rest_ensure_response([
            'id'              => (int)$updated['id'],
            'title'           => $updated['title'] ?? '',
            'text'            => $updated['text'] ?? '',
            'comment_type'    => $updated['comment_type'] ?? 'comment',
            'action_type'     => $updated['action_type'] ?? 'none',
            'action_target'   => $updated['action_target'] ?? '',
            'action_label'    => $updated['action_label'] ?? '',
            'action_duration' => isset($updated['action_duration']) ? (int)$updated['action_duration'] : 3,
            'spotlight_mode'  => !empty($updated['spotlight_mode']) ? $updated['spotlight_mode'] : 'spotlight',
            'is_story'        => ($updated['comment_type'] === 'story'),
            'updated'         => true
        ]);
    }

    public function get_approvals(WP_REST_Request $request) {
        global $wpdb;
        $table_approvals = $wpdb->prefix . 'pp_section_approvals';
        $layout_id = (int)$request->get_param('layout_id');
        $user_id = get_current_user_id() ?: 1;

        if ($wpdb->get_var("SHOW TABLES LIKE '$table_approvals'") != $table_approvals) {
            return rest_ensure_response([
                'layout_id' => $layout_id,
                'total_steps' => 7,
                'approved_count' => 0,
                'is_fully_approved' => false,
                'approvals' => []
            ]);
        }

        $approvals = $wpdb->get_results($wpdb->prepare("SELECT * FROM $table_approvals WHERE layout_id = %d AND user_id = %d ORDER BY step_order ASC", $layout_id, $user_id), ARRAY_A);
        $approved_count = 0;
        foreach ($approvals as $a) {
            if ($a['status'] === 'approved') $approved_count++;
        }

        return rest_ensure_response([
            'layout_id' => $layout_id,
            'total_steps' => 7,
            'approved_count' => $approved_count,
            'is_fully_approved' => ($approved_count >= 7),
            'approvals' => $approvals
        ]);
    }

    public function save_approval(WP_REST_Request $request) {
        global $wpdb;
        $table_approvals = $wpdb->prefix . 'pp_section_approvals';
        $params = $request->get_json_params() ?: $request->get_params();
        $layout_id = (int)($params['layout_id'] ?? 0);
        $step_order = (int)($params['step_order'] ?? 1);
        $selector = sanitize_text_field($params['target_selector'] ?? '');
        $status = sanitize_text_field($params['status'] ?? 'approved');
        $note = sanitize_textarea_field($params['note'] ?? '');
        $user_id = get_current_user_id() ?: 1;

        $wpdb->replace($table_approvals, [
            'layout_id'       => $layout_id,
            'user_id'         => $user_id,
            'step_order'      => $step_order,
            'target_selector' => $selector,
            'status'          => $status,
            'note'            => $note,
            'created_at'      => current_time('mysql')
        ]);

        return $this->get_approvals($request);
    }

    public function get_users(WP_REST_Request $request) {
        $users = [];
        $wp_users = get_users(['number' => 10]);
        foreach ($wp_users as $u) {
            $is_admin = user_can($u->ID, 'manage_options');
            $users[] = [
                'id'      => $u->ID,
                'name'    => $u->display_name,
                'email'   => $u->user_email,
                'role'    => $is_admin ? 'designer' : 'client',
                'company' => $is_admin ? 'Bowden Works (Design Lead)' : 'Client Reviewer',
                'avatar'  => get_avatar_url($u->ID)
            ];
        }

        if (count($users) <= 1) {
            $users[] = [
                'id'      => 2,
                'name'    => 'Sarah Jenkins',
                'email'   => 'sarah@nexusfintech.io',
                'role'    => 'client',
                'company' => 'Nexus Financial Inc. (Client Stakeholder)',
                'avatar'  => 'https://ui-avatars.com/api/?name=S&background=b02b2b&color=ffffff&bold=true'
            ];
            $users[] = [
                'id'      => 3,
                'name'    => 'Marcus Vance',
                'email'   => 'marcus@nexusfintech.io',
                'role'    => 'client',
                'company' => 'Nexus Financial Inc. (VP Product)',
                'avatar'  => 'https://ui-avatars.com/api/?name=M&background=334155&color=ffffff&bold=true'
            ];
        }

        return rest_ensure_response($users);
    }

    public function switch_user(WP_REST_Request $request) {
        $params = $request->get_json_params() ?: $request->get_params();
        $user_id = (int)($params['user_id'] ?? 1);
        $user = get_user_by('id', $user_id);
        return rest_ensure_response([
            'id'     => $user_id,
            'name'   => $user ? $user->display_name : 'Sarah Jenkins',
            'role'   => ($user && user_can($user->ID, 'manage_options')) ? 'designer' : 'client',
            'avatar' => get_avatar_url($user_id)
        ]);
    }

    /**
     * Get Story / Walkthrough Steps for a Layout or Project
     */
    public function get_story_steps(WP_REST_Request $request) {
        global $wpdb;
        $layout_id = (int)$request->get_param('layout_id');
        $project_id = (int)$request->get_param('project_id');

        $table_steps = $wpdb->prefix . 'pp_story_steps';
        $table_approvals = $wpdb->prefix . 'pp_story_approvals';

        if ($wpdb->get_var("SHOW TABLES LIKE '$table_steps'") !== $table_steps) {
            return rest_ensure_response([]);
        }

        $where = [];
        $args = [];

        if ($layout_id > 0) {
            $where[] = "layout_id = %d";
            $args[] = $layout_id;
        } elseif ($project_id > 0) {
            $where[] = "project_id = %d";
            $args[] = $project_id;
        }

        $where_sql = !empty($where) ? 'WHERE ' . implode(' AND ', $where) : '';
        $query = "SELECT * FROM $table_steps $where_sql ORDER BY step_order ASC, id ASC";

        if (!empty($args)) {
            $steps = $wpdb->get_results($wpdb->prepare($query, $args), ARRAY_A);
        } else {
            $steps = $wpdb->get_results($query, ARRAY_A);
        }

        if (empty($steps)) {
            if ($project_id > 0) {
                $meta_steps = get_post_meta($project_id, '_proto_story_steps', true);
                if (!empty($meta_steps) && is_array($meta_steps)) {
                    $result = [];
                    foreach ($meta_steps as $i => $ms) {
                        $result[] = [
                            'id'              => $ms['id'] ?? ($i + 1),
                            'project_id'      => $project_id,
                            'page_id'         => 1,
                            'layout_id'       => $layout_id ?: 1,
                            'step_order'      => $i + 1,
                            'title'           => $ms['title'] ?? '',
                            'content'         => $ms['content'] ?? ($ms['description'] ?? ''),
                            'description'     => $ms['content'] ?? ($ms['description'] ?? ''),
                            'target_selector' => $ms['target_selector'] ?? '',
                            'action_type'     => $ms['action_type'] ?? 'none',
                            'tip'             => $ms['tip'] ?? ''
                        ];
                    }
                    return rest_ensure_response($result);
                }
            }
            return rest_ensure_response([]);
        }

        // Fetch all approvals for these steps
        $step_ids = wp_list_pluck($steps, 'id');
        $approvals_by_step = [];
        if (!empty($step_ids)) {
            $id_placeholders = implode(',', array_map('intval', $step_ids));
            $approvals = $wpdb->get_results("SELECT * FROM $table_approvals WHERE step_id IN ($id_placeholders) ORDER BY created_at DESC", ARRAY_A);
            foreach ($approvals as $appr) {
                $approvals_by_step[$appr['step_id']][] = $appr;
            }
        }

        $result = [];
        foreach ($steps as $step) {
            $step_id = (int)$step['id'];
            $step_approvals = $approvals_by_step[$step_id] ?? [];
            $has_approved = false;
            foreach ($step_approvals as $a) {
                if ($a['status'] === 'approved') {
                    $has_approved = true;
                    break;
                }
            }

            $rect_data = !empty($step['rect_data']) ? json_decode($step['rect_data'], true) : null;

            $result[] = [
                'id'                => $step_id,
                'project_id'        => (int)$step['project_id'],
                'page_id'           => (int)$step['page_id'],
                'layout_id'         => (int)$step['layout_id'],
                'step_order'        => (int)$step['step_order'],
                'title'             => $step['title'],
                'description'       => $step['description'],
                'category'          => $step['category'] ?: 'ux_rationale',
                'target_type'       => $step['target_type'] ?: 'selector',
                'target_selector'   => $step['target_selector'] ?: '',
                'rect_data'         => $rect_data,
                'action_type'       => $step['action_type'] ?: 'none',
                'action_target'     => $step['action_target'] ?: '',
                'requires_approval' => (bool)(int)$step['requires_approval'],
                'author_id'         => (int)$step['author_id'],
                'created_at'        => $step['created_at'],
                'is_approved'       => $has_approved,
                'approvals_count'   => count($step_approvals),
                'approvals'         => $step_approvals
            ];
        }

        return rest_ensure_response($result);
    }

    /**
     * Create Story / Guide Walkthrough Step
     */
    public function create_story_step(WP_REST_Request $request) {
        global $wpdb;
        $params = $request->get_json_params() ?: $request->get_params();

        $table_steps = $wpdb->prefix . 'pp_story_steps';
        $layout_id = (int)($params['layout_id'] ?? 1);
        $page_id = (int)($params['page_id'] ?? 1);
        $project_id = (int)($params['project_id'] ?? 0);
        $title = sanitize_text_field($params['title'] ?? 'Design Decision');
        $description = wp_kses_post($params['description'] ?? '');
        $category = sanitize_text_field($params['category'] ?? 'ux_rationale');
        $target_type = sanitize_text_field($params['target_type'] ?? 'selector');
        $target_selector = sanitize_text_field($params['target_selector'] ?? '');
        $rect_data = isset($params['rect_data']) ? (is_string($params['rect_data']) ? $params['rect_data'] : wp_json_encode($params['rect_data'])) : null;
        $action_type = sanitize_text_field($params['action_type'] ?? 'none');
        $action_target = sanitize_text_field($params['action_target'] ?? '');
        $requires_approval = !empty($params['requires_approval']) ? 1 : 0;
        $author_id = get_current_user_id() ?: 1;

        // Auto-calculate step order
        if (!empty($params['step_order'])) {
            $step_order = (int)$params['step_order'];
        } else {
            $max_order = (int)$wpdb->get_var($wpdb->prepare("SELECT MAX(step_order) FROM $table_steps WHERE layout_id = %d", $layout_id));
            $step_order = $max_order + 1;
        }

        $wpdb->insert($table_steps, [
            'project_id'        => $project_id,
            'page_id'           => $page_id,
            'layout_id'         => $layout_id,
            'step_order'        => $step_order,
            'title'             => $title,
            'description'       => $description,
            'category'          => $category,
            'target_type'       => $target_type,
            'target_selector'   => $target_selector,
            'rect_data'         => $rect_data,
            'action_type'       => $action_type,
            'action_target'     => $action_target,
            'requires_approval' => $requires_approval,
            'author_id'         => $author_id,
            'created_at'        => current_time('mysql')
        ]);

        $step_id = $wpdb->insert_id;

        return rest_ensure_response([
            'id'                => $step_id,
            'project_id'        => $project_id,
            'page_id'           => $page_id,
            'layout_id'         => $layout_id,
            'step_order'        => $step_order,
            'title'             => $title,
            'description'       => $description,
            'category'          => $category,
            'target_type'       => $target_type,
            'target_selector'   => $target_selector,
            'rect_data'         => !empty($rect_data) ? json_decode($rect_data, true) : null,
            'action_type'       => $action_type,
            'action_target'     => $action_target,
            'requires_approval' => (bool)$requires_approval,
            'author_id'         => $author_id,
            'is_approved'       => false,
            'approvals_count'   => 0,
            'approvals'         => []
        ]);
    }

    /**
     * Update Story Step
     */
    public function update_story_step(WP_REST_Request $request) {
        global $wpdb;
        $id = (int)$request->get_param('id');
        $params = $request->get_json_params() ?: $request->get_params();
        $table_steps = $wpdb->prefix . 'pp_story_steps';

        $data = [];
        if (isset($params['title'])) $data['title'] = sanitize_text_field($params['title']);
        if (isset($params['description'])) $data['description'] = wp_kses_post($params['description']);
        if (isset($params['category'])) $data['category'] = sanitize_text_field($params['category']);
        if (isset($params['target_type'])) $data['target_type'] = sanitize_text_field($params['target_type']);
        if (isset($params['target_selector'])) $data['target_selector'] = sanitize_text_field($params['target_selector']);
        if (isset($params['rect_data'])) $data['rect_data'] = is_string($params['rect_data']) ? $params['rect_data'] : wp_json_encode($params['rect_data']);
        if (isset($params['action_type'])) $data['action_type'] = sanitize_text_field($params['action_type']);
        if (isset($params['action_target'])) $data['action_target'] = sanitize_text_field($params['action_target']);
        if (isset($params['step_order'])) $data['step_order'] = (int)$params['step_order'];
        if (isset($params['requires_approval'])) $data['requires_approval'] = !empty($params['requires_approval']) ? 1 : 0;

        if (!empty($data)) {
            $wpdb->update($table_steps, $data, ['id' => $id]);
        }

        $updated = $wpdb->get_row($wpdb->prepare("SELECT * FROM $table_steps WHERE id = %d", $id), ARRAY_A);
        return rest_ensure_response($updated);
    }

    /**
     * Delete Story Step
     */
    public function delete_story_step(WP_REST_Request $request) {
        global $wpdb;
        $id = (int)$request->get_param('id');
        $table_steps = $wpdb->prefix . 'pp_story_steps';
        $table_approvals = $wpdb->prefix . 'pp_story_approvals';

        $wpdb->delete($table_steps, ['id' => $id]);
        $wpdb->delete($table_approvals, ['step_id' => $id]);

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

    /**
     * Approve / Sign off on Story Step
     */
    public function approve_story_step(WP_REST_Request $request) {
        global $wpdb;
        $step_id = (int)$request->get_param('id');
        $params = $request->get_json_params() ?: $request->get_params();

        $table_approvals = $wpdb->prefix . 'pp_story_approvals';
        $user_id = get_current_user_id() ?: 0;
        $client_name = sanitize_text_field($params['client_name'] ?? ($params['name'] ?? 'Client'));
        $client_email = sanitize_email($params['client_email'] ?? ($params['email'] ?? ''));
        $status = sanitize_text_field($params['status'] ?? 'approved');
        $comment = sanitize_textarea_field($params['comment'] ?? '');

        $wpdb->insert($table_approvals, [
            'step_id'      => $step_id,
            'user_id'      => $user_id,
            'client_name'  => $client_name,
            'client_email' => $client_email,
            'status'       => $status,
            'comment'      => $comment,
            'created_at'   => current_time('mysql')
        ]);

        return rest_ensure_response([
            'success'      => true,
            'step_id'      => $step_id,
            'status'       => $status,
            'client_name'  => $client_name,
            'created_at'   => current_time('mysql')
        ]);
    }

    /**
     * Create a new Screen / Page Flow directly from Frontend
     */
    public function create_screen(WP_REST_Request $request) {
        $project_id = (int)$request->get_param('id');
        $params = $request->get_json_params() ?: $request->get_params();

        $screens = get_post_meta($project_id, '_proto_screens', true);
        if (!is_array($screens)) {
            $screens = [];
        }

        // Determine max ID
        $max_id = 0;
        foreach ($screens as $s) {
            if (isset($s['id']) && (int)$s['id'] > $max_id) {
                $max_id = (int)$s['id'];
            }
        }
        $new_id = $max_id + 1;

        $name = sanitize_text_field($params['name'] ?? 'New Screen');
        $slug = sanitize_title($params['slug'] ?? $name);
        $parent_name = sanitize_text_field($params['parent_name'] ?? '');
        $nav_group = sanitize_text_field($params['nav_group'] ?? 'subpage');
        $status = sanitize_text_field($params['status'] ?? 'building_prototype');
        $dev_url = esc_url_raw($params['dev_url'] ?? '');
        $visibility = sanitize_text_field($params['visibility'] ?? 'client'); // 'client' or 'internal'
        $is_master_checkpoint = !empty($params['is_master_checkpoint']) ? 1 : 0;

        $new_screen = [
            'id'                   => $new_id,
            'name'                 => $name,
            'slug'                 => $slug,
            'parent_name'          => $parent_name,
            'nav_group'            => $nav_group,
            'status'               => $status,
            'dev_url'              => $dev_url,
            'visibility'           => $visibility,
            'is_master_checkpoint' => $is_master_checkpoint,
            'layouts'              => []
        ];

        $screens[] = $new_screen;
        update_post_meta($project_id, '_proto_screens', $screens);

        return rest_ensure_response([
            'success' => true,
            'screen'  => $new_screen,
            'screens' => $screens
        ]);
    }

    /**
     * Update Screen Status / Dev URL / Metadata
     */
    public function update_screen_status(WP_REST_Request $request) {
        $project_id = (int)$request->get_param('id');
        $screen_id = (int)$request->get_param('screen_id');
        $params = $request->get_json_params() ?: $request->get_params();

        $screens = get_post_meta($project_id, '_proto_screens', true);
        if (!is_array($screens)) {
            return new WP_Error('no_screens', 'No screens found for project', ['status' => 404]);
        }

        $found = false;
        foreach ($screens as &$s) {
            if ((int)($s['id'] ?? 0) === $screen_id) {
                if (isset($params['status'])) {
                    $s['status'] = sanitize_text_field($params['status']);
                }
                if (isset($params['dev_url'])) {
                    $s['dev_url'] = esc_url_raw($params['dev_url']);
                }
                if (isset($params['visibility'])) {
                    $s['visibility'] = sanitize_text_field($params['visibility']);
                }
                if (isset($params['is_master_checkpoint'])) {
                    $s['is_master_checkpoint'] = !empty($params['is_master_checkpoint']) ? 1 : 0;
                }
                if (isset($params['name'])) {
                    $s['name'] = sanitize_text_field($params['name']);
                }
                if (isset($params['slug'])) {
                    $s['slug'] = sanitize_title($params['slug']);
                }
                if (isset($params['parent_name'])) {
                    $s['parent_name'] = sanitize_text_field($params['parent_name']);
                }
                if (isset($params['nav_group'])) {
                    $s['nav_group'] = sanitize_text_field($params['nav_group']);
                }
                $found = true;
                break;
            }
        }

        if ($found) {
            update_post_meta($project_id, '_proto_screens', $screens);
            return rest_ensure_response(['success' => true, 'screen_id' => $screen_id, 'screens' => $screens]);
        }

        return new WP_Error('screen_not_found', 'Screen ID not found', ['status' => 404]);
    }

    /**
     * Reorder Screens Array from Drag and Drop
     */
    public function reorder_screens(WP_REST_Request $request) {
        $project_id = (int)$request->get_param('id');
        $params = $request->get_json_params() ?: $request->get_params();

        $order = $params['order'] ?? []; // Array of Screen IDs in new sequential order
        if (!is_array($order) || empty($order)) {
            return new WP_Error('invalid_order', 'Order array required', ['status' => 400]);
        }

        $screens = get_post_meta($project_id, '_proto_screens', true);
        if (!is_array($screens)) {
            return new WP_Error('no_screens', 'No screens found', ['status' => 404]);
        }

        // Build lookup map by ID
        $screen_map = [];
        foreach ($screens as $s) {
            $screen_map[(int)$s['id']] = $s;
        }

        // Reorder screens according to $order
        $reordered = [];
        foreach ($order as $id) {
            $id = (int)$id;
            if (isset($screen_map[$id])) {
                $reordered[] = $screen_map[$id];
                unset($screen_map[$id]);
            }
        }

        // Append any remaining screens that weren't in $order
        foreach ($screen_map as $remaining) {
            $reordered[] = $remaining;
        }

        update_post_meta($project_id, '_proto_screens', $reordered);

        return rest_ensure_response([
            'success' => true,
            'screens' => $reordered
        ]);
    }

    /**
     * Delete Screen
     */
    public function delete_screen(WP_REST_Request $request) {
        $project_id = (int)$request->get_param('id');
        $screen_id = (int)$request->get_param('screen_id');

        $screens = get_post_meta($project_id, '_proto_screens', true);
        if (!is_array($screens)) {
            return new WP_Error('no_screens', 'No screens found', ['status' => 404]);
        }

        $new_screens = [];
        foreach ($screens as $s) {
            if ((int)($s['id'] ?? 0) !== $screen_id) {
                $new_screens[] = $s;
            }
        }

        update_post_meta($project_id, '_proto_screens', $new_screens);

        return rest_ensure_response([
            'success' => true,
            'deleted_id' => $screen_id,
            'screens' => $new_screens
        ]);
    }

    /**
     * Bulk Update Screens (Change Parent, Status, Dev URL, Visibility, or Delete)
     */
    public function bulk_update_screens(WP_REST_Request $request) {
        $project_id = (int)$request->get_param('id');
        $params = $request->get_json_params() ?: $request->get_params();

        $screen_ids = $params['screen_ids'] ?? [];
        if (!is_array($screen_ids) || empty($screen_ids)) {
            return new WP_Error('invalid_ids', 'Screen IDs required for bulk update', ['status' => 400]);
        }

        $screen_ids_map = array_fill_keys(array_map('intval', $screen_ids), true);

        $screens = get_post_meta($project_id, '_proto_screens', true);
        if (!is_array($screens)) {
            return new WP_Error('no_screens', 'No screens found', ['status' => 404]);
        }

        $action = $params['action'] ?? 'update'; // 'update' or 'delete'

        if ($action === 'delete') {
            $updated_screens = [];
            foreach ($screens as $s) {
                if (!isset($screen_ids_map[(int)($s['id'] ?? 0)])) {
                    $updated_screens[] = $s;
                }
            }
            update_post_meta($project_id, '_proto_screens', $updated_screens);
            return rest_ensure_response([
                'success' => true,
                'action'  => 'delete',
                'affected_count' => count($screen_ids),
                'screens' => $updated_screens
            ]);
        }

        // Action: Update
        $parent_action = $params['parent_action'] ?? 'no_change'; // 'no_change', 'remove_parent', 'set_parent', 'set_footer'
        $parent_name = sanitize_text_field($params['parent_name'] ?? '');
        $status = sanitize_text_field($params['status'] ?? 'no_change'); // 'no_change' or valid status slug
        $visibility_action = $params['visibility_action'] ?? 'no_change'; // 'no_change', 'set_client', 'set_internal'
        $dev_url_action = $params['dev_url_action'] ?? 'no_change'; // 'no_change', 'clear', 'set', 'prefix'
        $dev_url = esc_url_raw($params['dev_url'] ?? '');

        $affected_count = 0;
        foreach ($screens as &$s) {
            $id = (int)($s['id'] ?? 0);
            if (isset($screen_ids_map[$id])) {
                $affected_count++;

                // Handle Parent / Hierarchy update
                if ($parent_action === 'remove_parent') {
                    $s['parent_name'] = '';
                    $s['nav_group'] = 'primary';
                } elseif ($parent_action === 'set_parent' && !empty($parent_name)) {
                    $s['parent_name'] = $parent_name;
                    $s['nav_group'] = 'subpage';
                } elseif ($parent_action === 'set_footer') {
                    $s['parent_name'] = '';
                    $s['nav_group'] = 'footer';
                }

                // Handle Visibility update
                if ($visibility_action === 'set_client') {
                    $s['visibility'] = 'client';
                } elseif ($visibility_action === 'set_internal') {
                    $s['visibility'] = 'internal';
                }

                // Handle Status update
                if ($status !== 'no_change' && !empty($status)) {
                    $s['status'] = $status;
                }

                // Handle Dev URL update
                if ($dev_url_action === 'clear') {
                    $s['dev_url'] = '';
                } elseif ($dev_url_action === 'set' && !empty($dev_url)) {
                    $s['dev_url'] = $dev_url;
                } elseif ($dev_url_action === 'prefix' && !empty($dev_url)) {
                    $slug = $s['slug'] ?? sanitize_title($s['name']);
                    $s['dev_url'] = rtrim($dev_url, '/') . '/' . ltrim($slug, '/') . '/';
                }
            }
        }

        update_post_meta($project_id, '_proto_screens', $screens);

        return rest_ensure_response([
            'success'        => true,
            'action'         => 'update',
            'affected_count' => $affected_count,
            'screens'        => $screens
        ]);
    }
}
