<?php
/**
 * Cast Conductor Proprietary License v5
 * SPDX-License-Identifier: LicenseRef-CastConductor-Proprietary-v5
 * 
 * Copyright (c) 2025 CastConductor.com. All Rights Reserved.
 * 
 * This file is part of Cast Conductor ("Software"). The Software and its source
 * code constitute proprietary, confidential, and trade secret information of
 * CastConductor.com ("Company"). Any access or use is governed strictly by the
 * Cast Conductor Proprietary License v5 ("License"). By installing, copying,
 * accessing, compiling, or otherwise using the Software you agree to be bound by
 * all terms of the License. If you do not agree, you must cease use immediately.
 * 
 * Key Terms (Summary – see full License for binding terms):
 *  1. No Redistribution: You may not publish, distribute, sublicense, rent,
 *     lease, transfer, sell, or otherwise make the Software (or any derivative)
 *     available to any third party without prior written consent of Company.
 *  2. No Modification: Modification, reverse engineering, decompilation, or
 *     disassembly is prohibited except to the limited extent expressly permitted
 *     by applicable law that cannot be contractually waived.
 *  3. Confidentiality: Treat all source code and related artifacts as Company
 *     Confidential Information. Maintain at least the same degree of care as for
 *     your own confidential materials, and not less than reasonable care.
 *  4. No Patent License: No express or implied patent rights are granted. Future
 *     patents (if any) are fully reserved.
 *  5. No Trademark License: Company names, marks, and logos may not be used
 *     without prior written permission.
 *  6. Limited Internal Use: Use is limited solely to internal evaluation and
 *     operation of licensed Cast Conductor deployments. Commercial hosting or
 *     resale as a service requires a separate written agreement.
 *  7. Telemetry & License Validation: The Software may periodically transmit a
 *     hashed installation identifier, domain (or site ID), plugin/app version,
 *     and a truncated (non-reversible) fragment of the license key solely to
 *     validate activation status and enforce licensing. This minimal "phone home"
 *     check contains no personal or content data. If optional telemetry is later
 *     introduced it will be limited to aggregate operational metrics (no PII),
 *     fully documented, and optionally disableable per published instructions.
 *  8. Third-Party Components: The Software may include open source components
 *     covered by their own licenses. See THIRD-PARTY-NOTICES.md. Those licenses
 *     govern their respective components; this License governs all remaining code.
 *  9. Export Compliance: You are responsible for compliance with all applicable
 *     export control and sanctions laws.
 * 10. Warranty Disclaimer: THE SOFTWARE IS PROVIDED "AS IS" WITHOUT WARRANTY OF
 *     ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO MERCHANTABILITY,
 *     FITNESS FOR A PARTICULAR PURPOSE, TITLE, AND NON-INFRINGEMENT.
 * 11. Limitation of Liability: IN NO EVENT WILL COMPANY OR AUTHORS BE LIABLE FOR
 *     ANY INDIRECT, SPECIAL, INCIDENTAL, CONSEQUENTIAL, EXEMPLARY, OR PUNITIVE
 *     DAMAGES, OR LOST PROFITS, EVEN IF ADVISED OF THE POSSIBILITY.
 * 12. Acceptance: Use of the Software constitutes acceptance of the License.
 * 13. Enforcement: Unauthorized reproduction or distribution may result in civil
 *     and criminal penalties and will be prosecuted to the maximum extent allowed
 *     by law.
 * 
 * Authoritative EULA: EULA-v5.1.md (repository root – private) and https://castconductor.com/eula
 * Precedence: If this summary conflicts with the EULA, the EULA governs.
 * Revision: Current EULA revision v5.1 (subject to update; check EULA for current enterprise thresholds).
 * 
 * Full License text available from: licensing@castconductor.com
 * Security reports: security@castconductor.com
 * Commercial inquiries: licensing@castconductor.com
 * 
 * END OF HEADER
 */

/**
 * Containers API Controller - Phase 3 Implementation
 * 
 * Handles container CRUD operations with background layer integration
 * Supports Z-index layered system for Roku display architecture
 */

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

class CastConductor_Containers_Controller extends WP_REST_Controller {
    
    protected $namespace = 'castconductor/v5';
    protected $rest_base = 'containers';
    
    public function register_routes() {
    // List all containers
        register_rest_route($this->namespace, '/' . $this->rest_base, [
            [
                'methods' => WP_REST_Server::READABLE,
                'callback' => [$this, 'get_items'],
                'permission_callback' => [$this, 'get_items_permissions_check']
            ],
            [
                'methods' => WP_REST_Server::CREATABLE,
                'callback' => [$this, 'create_item'],
                'permission_callback' => [$this, 'create_item_permissions_check'],
        'args' => $this->get_endpoint_args_for_item_schema(WP_REST_Server::CREATABLE)
            ]
        ]);
        
        // Single container operations
    register_rest_route($this->namespace, '/' . $this->rest_base . '/(?P<id>[\d]+)', [
            [
                'methods' => WP_REST_Server::READABLE,
                'callback' => [$this, 'get_item'],
                'permission_callback' => [$this, 'get_item_permissions_check']
            ],
            [
                'methods' => WP_REST_Server::EDITABLE,
                'callback' => [$this, 'update_item'],
                'permission_callback' => [$this, 'update_item_permissions_check'],
        // Don't strictly validate body against schema here; we'll sanitize selectively inside update_item
        // 'args' => $this->get_endpoint_args_for_item_schema(WP_REST_Server::EDITABLE)
            ],
            [
                'methods' => WP_REST_Server::DELETABLE,
                'callback' => [$this, 'delete_item'],
                'permission_callback' => [$this, 'delete_item_permissions_check']
            ]
        ]);
        
        // Container blocks assignment endpoints
        register_rest_route($this->namespace, '/' . $this->rest_base . '/(?P<id>[\d]+)/blocks', [
            [
                'methods' => WP_REST_Server::READABLE,
                'callback' => [$this, 'get_container_blocks'],
                'permission_callback' => [$this, 'get_items_permissions_check']
            ],
            [
                'methods' => WP_REST_Server::CREATABLE,
                'callback' => [$this, 'assign_block_to_container'],
                'permission_callback' => [$this, 'create_item_permissions_check'],
                'args' => [
                    'block_id' => [
                        'required' => true,
                        'type' => 'integer',
                        'description' => 'Content block ID to assign'
                    ],
                    'rotation_percentage' => [
                        'required' => true,
                        'type' => 'number',
                        'minimum' => 0,
                        'maximum' => 100,
                        'description' => 'Rotation percentage (0-100)'
                    ],
                    'rotation_order' => [
                        'required' => false,
                        'type' => 'integer',
                        'default' => 1,
                        'description' => 'Display order within rotation'
                    ]
                ]
            ]
        ]);
        
        // Remove block from container
        register_rest_route($this->namespace, '/' . $this->rest_base . '/(?P<id>[\d]+)/blocks/(?P<block_id>[\d]+)', [
            [
                'methods' => WP_REST_Server::EDITABLE,
                'callback' => [$this, 'update_block_assignment'],
                'permission_callback' => [$this, 'update_item_permissions_check'],
                'args' => [
                    'rotation_percentage' => [
                        'required' => false,
                        'type' => 'number',
                        'minimum' => 0,
                        'maximum' => 100,
                        'description' => 'Rotation percentage (0-100)'
                    ],
                    'rotation_order' => [
                        'required' => false,
                        'type' => 'integer',
                        'minimum' => 0,
                        'description' => 'Display order within rotation'
                    ]
                ]
            ],
            [
                'methods' => WP_REST_Server::DELETABLE,
                'callback' => [$this, 'remove_block_from_container'],
                'permission_callback' => [$this, 'delete_item_permissions_check']
            ]
        ]);

        // Batch reorder assignments within a container
        register_rest_route($this->namespace, '/' . $this->rest_base . '/(?P<id>[\d]+)/blocks/reorder', [
            [
                'methods' => WP_REST_Server::EDITABLE,
                'callback' => [$this, 'reorder_container_blocks'],
                'permission_callback' => [$this, 'update_item_permissions_check'],
                'args' => [
                    'sequence' => [
                        'required' => false,
                        'type' => 'array',
                        'description' => 'Array of content_block_id in desired order (1..n)'
                    ],
                    'order' => [
                        'required' => false,
                        'type' => 'array',
                        'description' => 'Array of {block_id, rotation_order}'
                    ]
                ]
            ]
        ]);

        // Apply production defaults to a container (per-container; interval-only or full reset)
        register_rest_route($this->namespace, '/' . $this->rest_base . '/(?P<id>[\d]+)/defaults', [
            [
                'methods' => WP_REST_Server::CREATABLE,
                'callback' => [$this, 'apply_production_defaults'],
                'permission_callback' => [$this, 'update_item_permissions_check'],
                'args' => [
                    'interval' => [
                        'required' => false,
                        'type' => 'integer',
                        'minimum' => 5,
                        'maximum' => 300,
                        'default' => 15,
                        'description' => 'Rotation interval in seconds (default 15)'
                    ],
                    'mode' => [
                        'required' => false,
                        'type' => 'string',
                        'enum' => ['interval_only','full_reset'],
                        'default' => 'interval_only',
                        'description' => 'interval_only keeps assignments; full_reset wipes and sets 50/30/10/10'
                    ]
                ]
            ]
        ]);

        // Zones convenience: get layout
        register_rest_route($this->namespace, '/' . $this->rest_base . '/(?P<id>[\d]+)/zones', [
            [
                'methods' => WP_REST_Server::READABLE,
                'callback' => [$this, 'get_container_zones'],
                'permission_callback' => [$this, 'get_items_permissions_check']
            ]
        ]);

        // Zone assignments (per-zone schedules)
        register_rest_route($this->namespace, '/' . $this->rest_base . '/(?P<id>[\d]+)/zones/(?P<zone_id>[A-Za-z0-9_\-]+)/assignments', [
            [
                'methods' => WP_REST_Server::READABLE,
                'callback' => [$this, 'get_zone_assignments_endpoint'],
                'permission_callback' => [$this, 'get_items_permissions_check']
            ],
            [
                'methods' => WP_REST_Server::EDITABLE,
                'callback' => [$this, 'put_zone_assignments_endpoint'],
                'permission_callback' => [$this, 'update_item_permissions_check'],
                'args' => [
                    'assignments' => [
                        'required' => true,
                        'type' => 'array',
                        'description' => 'Array of {block_id, rotation_percentage, rotation_order, enabled}'
                    ]
                ]
            ]
        ]);
    }
    
    /**
     * Get all containers
     */
    public function get_items($request) {
        global $wpdb;
        
        $table_name = $wpdb->prefix . 'castconductor_containers';
        $containers = $wpdb->get_results(
            "SELECT * FROM $table_name ORDER BY z_index ASC, created_at DESC",
            ARRAY_A
        );
        
        if ($wpdb->last_error) {
            return new WP_Error('database_error', 'Failed to retrieve containers', ['status' => 500]);
        }
        
        // Add background information and layout metadata for each container
        foreach ($containers as &$container) {
            $container['background'] = $this->get_container_background($container['id']);
            $container['assigned_blocks'] = $this->get_container_blocks_count($container['id']);
            $container['layout'] = $this->get_container_layout((int)$container['id']);
            // Safety: Lower Third should not be zoned. If zones exist due to legacy data, hide them in response.
            if (isset($container['position']) && strtolower($container['position']) === 'lower_third') {
                if (is_array($container['layout']) && !empty($container['layout']['zones'])) {
                    $container['layout']['zones'] = array();
                    $container['layout']['activeZoneIds'] = array();
                }
            }
            // Expose persisted behavior flags (e.g., shuffle)
            $container['behavior'] = $this->get_container_behavior((int)$container['id']);
            // Expose overlay configuration for menu/navigation system
            $container['overlay'] = $this->get_container_overlay((int)$container['id']);
        }
        
        return rest_ensure_response($containers);
    }

    /** Zones convenience: return layout for a container */
    public function get_container_zones($request) {
        $container_id = (int) $request['id'];
        $container = $this->get_container_by_id($container_id);
        if (!$container) {
            return new WP_Error('container_not_found', 'Container not found', ['status' => 404]);
        }
        return rest_ensure_response($this->get_container_layout($container_id));
    }

    /** GET zone assignments endpoint */
    public function get_zone_assignments_endpoint($request) {
        $container_id = (int) $request['id'];
        $zone_id = sanitize_key($request['zone_id']);
        $layout = $this->get_container_layout($container_id);
        if (!$this->zone_exists($layout, $zone_id)) {
            return new WP_Error('zone_not_found', 'Zone does not exist on this container', ['status' => 404]);
        }
        $map = $this->get_zone_assignments($container_id);
        $rawList = isset($map[$zone_id]) && is_array($map[$zone_id]) ? array_values($map[$zone_id]) : [];
        // Augment with block names/types for UI convenience
        $ids = [];
        foreach ($rawList as $row) { if (!empty($row['block_id'])) $ids[] = intval($row['block_id']); }
        $details = [];
        if (!empty($ids)) {
            global $wpdb;
            $blocks_table = $wpdb->prefix . 'castconductor_content_blocks';
            $in = implode(',', array_fill(0, count($ids), '%d'));
            $rows = $wpdb->get_results($wpdb->prepare("SELECT id, name, type FROM $blocks_table WHERE id IN ($in)", $ids), ARRAY_A);
            foreach ($rows as $r) { $details[intval($r['id'])] = $r; }
        }
        $list = [];
        foreach ($rawList as $row) {
            $bid = intval($row['block_id']);
            $info = isset($details[$bid]) ? $details[$bid] : null;
            $list[] = [
                'content_block_id' => $bid,
                'rotation_percentage' => isset($row['rotation_percentage']) ? floatval($row['rotation_percentage']) : 0,
                'rotation_order' => isset($row['rotation_order']) ? intval($row['rotation_order']) : 0,
                'enabled' => isset($row['enabled']) ? intval($row['enabled']) : 1,
                'block_name' => $info ? $info['name'] : null,
                'block_type' => $info ? $info['type'] : null
            ];
        }
        // Sort by rotation_order if present
        usort($list, function($a, $b) { return ($a['rotation_order'] <=> $b['rotation_order']); });
        return rest_ensure_response($list);
    }

    /** PUT zone assignments endpoint */
    public function put_zone_assignments_endpoint($request) {
        global $wpdb;
        $container_id = (int) $request['id'];
        $zone_id = sanitize_key($request['zone_id']);
        $container = $this->get_container_by_id($container_id);
        if (!$container) {
            return new WP_Error('container_not_found', 'Container not found', ['status' => 404]);
        }
        $layout = $this->get_container_layout($container_id);
        if (!$this->zone_exists($layout, $zone_id)) {
            return new WP_Error('zone_not_found', 'Zone does not exist on this container', ['status' => 404]);
        }
        $assignments = $request['assignments'];
        if (!is_array($assignments)) {
            return new WP_Error('invalid_payload', 'Assignments must be an array', ['status' => 400]);
        }
        // Optional: validate block IDs exist
        $blocks_table = $wpdb->prefix . 'castconductor_content_blocks';
        $sanitized = [];
        foreach ($assignments as $i => $item) {
            if (!is_array($item)) continue;
            $bid = isset($item['block_id']) ? intval($item['block_id']) : 0;
            if ($bid <= 0) continue;
            $pct = isset($item['rotation_percentage']) ? floatval($item['rotation_percentage']) : 0.0;
            if ($pct < 0) $pct = 0; if ($pct > 100) $pct = 100;
            $ord = isset($item['rotation_order']) ? intval($item['rotation_order']) : ($i + 1);
            $ena = isset($item['enabled']) ? (intval($item['enabled']) ? 1 : 0) : 1;
            // Ensure block exists
            $exists = $wpdb->get_var($wpdb->prepare("SELECT COUNT(*) FROM $blocks_table WHERE id = %d", $bid));
            if (!$exists) continue;
            $sanitized[] = [
                'block_id' => $bid,
                'rotation_percentage' => $pct,
                'rotation_order' => $ord,
                'enabled' => $ena
            ];
        }
        // Persist map
        $map = $this->get_zone_assignments($container_id);
        $map[$zone_id] = $sanitized;
        $this->set_zone_assignments($container_id, $map);
        return rest_ensure_response(['success' => true, 'count' => count($sanitized)]);
    }

    /** Check if a zone exists in layout */
    private function zone_exists($layout, $zone_id) {
        $zones = isset($layout['zones']) && is_array($layout['zones']) ? $layout['zones'] : [];
        foreach ($zones as $z) { if (!empty($z['id']) && $z['id'] === $zone_id) return true; }
        return false;
    }

    /** Load entire zone assignments map for a container */
    private function get_zone_assignments($container_id) {
        $key = 'castconductor_container_zone_assignments_' . intval($container_id);
        $raw = get_option($key, null);
        if (is_string($raw)) { $d = json_decode($raw, true); if (is_array($d)) return $d; }
        if (is_array($raw)) return $raw;
        return [];
    }

    /** Persist entire zone assignments map */
    private function set_zone_assignments($container_id, $map) {
        $key = 'castconductor_container_zone_assignments_' . intval($container_id);
        update_option($key, wp_json_encode($map));
    }

    /**
     * Apply production defaults for the given container:
     * Track info (50%), Shoutouts (30%), Sponsors (10%), Promos (10%), total 100%.
     * Sets rotation_interval to provided value (default 15s).
     */
    public function apply_production_defaults($request) {
        global $wpdb;

        $container_id = (int) $request['id'];
        $interval = isset($request['interval']) ? (int) $request['interval'] : 15;
        $mode = isset($request['mode']) ? sanitize_text_field($request['mode']) : 'interval_only';

        $container = $this->get_container_by_id($container_id);
        if (!$container) {
            return new WP_Error('container_not_found', 'Container not found', ['status' => 404]);
        }

        // Always update interval on the container first
        $table_name = $wpdb->prefix . 'castconductor_containers';
        $upd_base = $wpdb->update(
            $table_name,
            ['rotation_enabled' => 1, 'rotation_interval' => $interval, 'updated_at' => current_time('mysql')],
            ['id' => $container_id],
            ['%d', '%d', '%s'],
            ['%d']
        );
        if ($upd_base === false) {
            return new WP_Error('defaults_failed', 'Failed to update interval: ' . $wpdb->last_error, ['status' => 500]);
        }

        // If only interval update was requested, return now without touching assignments
        if ($mode !== 'full_reset') {
            $req = new WP_REST_Request();
            $req->set_param('id', $container_id);
            return $this->get_item($req);
        }

        // Find required content blocks by type first (singular types), then fallback by name
        $content_blocks_table = $wpdb->prefix . 'castconductor_content_blocks';
        $need = [
            'track_info' => ['type' => 'track_info', 'name_like' => ['Track info','Track'], 'pct' => 50, 'order' => 1],
            'shoutout'   => ['type' => 'shoutout',   'name_like' => ['Shoutout','Shoutouts','Shout'], 'pct' => 30, 'order' => 2],
            'sponsor'    => ['type' => 'sponsor',    'name_like' => ['Sponsor','Sponsors'], 'pct' => 10, 'order' => 3],
            'promo'      => ['type' => 'promo',      'name_like' => ['Promo','Promos'], 'pct' => 10, 'order' => 4],
        ];

        $found = [];
        // Try by type (singular)
        $types = array_values(array_map(function($m){ return $m['type']; }, $need));
        $placeholders = implode(',', array_fill(0, count($types), '%s'));
        $by_type = $wpdb->get_results($wpdb->prepare(
            "SELECT id, type, name FROM $content_blocks_table WHERE enabled = 1 AND type IN ($placeholders)",
            ...$types
        ), ARRAY_A);
        foreach (($by_type ?: []) as $row) {
            $t = $row['type'];
            if (isset($need[$t]) && !isset($found[$t])) {
                $found[$t] = (int) $row['id'];
            }
        }

        // Fallback by name contains (case-insensitive)
        foreach ($need as $t => $meta) {
            if (isset($found[$t])) continue;
            $names = (array)($meta['name_like'] ?? []);
            foreach ($names as $nm) {
                $like = '%' . $wpdb->esc_like($nm) . '%';
                $row = $wpdb->get_row($wpdb->prepare(
                    "SELECT id, name, type FROM $content_blocks_table WHERE enabled = 1 AND (name LIKE %s)",
                    $like
                ), ARRAY_A);
                if ($row) { $found[$t] = (int) $row['id']; break; }
            }
        }

        // Verify all required blocks found
        $missing = array_diff(array_keys($need), array_keys($found));
        if (!empty($missing)) {
            return new WP_Error(
                'defaults_missing_blocks',
                'Missing required content blocks: ' . implode(', ', $missing),
                ['status' => 400]
            );
        }

        // Start transaction, wipe existing assignments, set interval, and assign defaults
        $blocks_table = $wpdb->prefix . 'castconductor_container_blocks';
        $wpdb->query('START TRANSACTION');
        $ok = true;

        // Delete existing assignments for this container
        $del = $wpdb->delete($blocks_table, ['container_id' => $container_id], ['%d']);
        if ($del === false) { $ok = false; }

    // rotation interval already updated above

        // Insert assignments in specified order
        if ($ok) {
            foreach ($need as $t => $meta) {
                $res = $wpdb->insert(
                    $blocks_table,
                    [
                        'container_id' => $container_id,
                        'content_block_id' => $found[$t],
                        'rotation_percentage' => (float) $meta['pct'],
                        'rotation_order' => (int) $meta['order'],
                        'enabled' => 1,
                        'created_at' => current_time('mysql')
                    ],
                    ['%d', '%d', '%f', '%d', '%d', '%s']
                );
                if ($res === false) { $ok = false; break; }
            }
        }

        if ($ok) {
            $wpdb->query('COMMIT');
        } else {
            $wpdb->query('ROLLBACK');
            return new WP_Error('defaults_failed', 'Failed to apply defaults: ' . $wpdb->last_error, ['status' => 500]);
        }

        // Return updated container details
        $req = new WP_REST_Request();
        $req->set_param('id', $container_id);
        $updated = $this->get_item($req);
        return $updated;
    }
    
    /**
     * Get single container
     */
    public function get_item($request) {
        $container_id = (int) $request['id'];
        $container = $this->get_container_by_id($container_id);
        
        if (!$container) {
            return new WP_Error('container_not_found', 'Container not found', ['status' => 404]);
        }
        
    // Add detailed background, block information, and layout metadata
        $container['background'] = $this->get_container_background($container_id);
        $container['assigned_blocks'] = $this->get_container_assigned_blocks($container_id);
        $container['layout'] = $this->get_container_layout($container_id);
        // Safety: Lower Third should not be zoned in editor preview
        if (isset($container['position']) && strtolower($container['position']) === 'lower_third') {
            if (is_array($container['layout']) && !empty($container['layout']['zones'])) {
                $container['layout']['zones'] = array();
                $container['layout']['activeZoneIds'] = array();
            }
        }
        $container['behavior'] = $this->get_container_behavior($container_id);
        // Expose overlay configuration for menu/navigation system
        $container['overlay'] = $this->get_container_overlay($container_id);
        
        return rest_ensure_response($container);
    }
    
    /**
     * Create new container
     */
    public function create_item($request) {
        global $wpdb;
        
        $table_name = $wpdb->prefix . 'castconductor_containers';
        
        // Validate and sanitize input
        $name = sanitize_text_field($request['name']);
        $position = sanitize_text_field($request['position']);
        $width = (int) $request['width'];
        $height = (int) $request['height'];
        $x_position = (int) $request['x_position'];
        $y_position = (int) $request['y_position'];
        $z_index = (int) $request['z_index'];
        $rotation_enabled = (bool) $request['rotation_enabled'];
        $rotation_interval = (int) $request['rotation_interval'];
        
        // Insert container
        $result = $wpdb->insert(
            $table_name,
            [
                'name' => $name,
                'position' => $position,
                'width' => $width,
                'height' => $height,
                'x_position' => $x_position,
                'y_position' => $y_position,
                'z_index' => $z_index,
                'rotation_enabled' => $rotation_enabled ? 1 : 0,
                'rotation_interval' => $rotation_interval,
                'enabled' => 1,
                'created_at' => current_time('mysql')
            ],
            ['%s', '%s', '%d', '%d', '%d', '%d', '%d', '%d', '%d', '%d', '%s']
        );
        
        if ($result === false) {
            return new WP_Error('creation_failed', 'Failed to create container: ' . $wpdb->last_error, ['status' => 500]);
        }
        
        $container_id = $wpdb->insert_id;
        
        // Create background record if specified
        if (!empty($request['background'])) {
            $this->create_container_background($container_id, $request['background']);
        }

        // Persist layout (zones) if provided
        if (isset($request['layout'])) {
            $layout = $this->sanitize_container_layout($request['layout']);
            $this->set_container_layout($container_id, $layout);
        }

        // Persist behavior (e.g., shuffle) if provided
        if (isset($request['behavior'])) {
            $behavior = $this->sanitize_container_behavior($request['behavior']);
            $this->set_container_behavior($container_id, $behavior);
        }
        
    // Return created container
    $req = new WP_REST_Request();
    $req->set_param('id', $container_id);
    return $this->get_item($req);
    }
    
    /**
     * Update container
     */
    public function update_item($request) {
        global $wpdb;
        
        $container_id = (int) $request['id'];
        $container = $this->get_container_by_id($container_id);
        
        if (!$container) {
            return new WP_Error('container_not_found', 'Container not found', ['status' => 404]);
        }
        
        $table_name = $wpdb->prefix . 'castconductor_containers';
        
        // Build update data
        $update_data = [];
        $update_format = [];
        
        $updateable_fields = [
            'name' => '%s',
            'position' => '%s', 
            'width' => '%d',
            'height' => '%d',
            'x_position' => '%d',
            'y_position' => '%d',
            'z_index' => '%d',
            'rotation_enabled' => '%d',
            'rotation_interval' => '%d',
            'enabled' => '%d'
        ];
        
        // Normalize geometry authored in admin 1920x1080 space to Roku 1280x720 if needed
        $scale = 1280 / 1920; // 0.666...
        $w_in = isset($request['width']) ? (int)$request['width'] : null;
        $h_in = isset($request['height']) ? (int)$request['height'] : null;
        $x_in = isset($request['x_position']) ? (int)$request['x_position'] : null;
        $y_in = isset($request['y_position']) ? (int)$request['y_position'] : null;
        $needs_scale = false;
        if (($w_in !== null && $w_in > 1280) || ($h_in !== null && $h_in > 720) || ($x_in !== null && $x_in > 1280) || ($y_in !== null && $y_in > 720)) {
            $needs_scale = true;
        }
        foreach ($updateable_fields as $field => $format) {
            if (isset($request[$field])) {
                if ($field === 'rotation_enabled' || $field === 'enabled') {
                    $update_data[$field] = (bool) $request[$field] ? 1 : 0;
                } else if (in_array($format, ['%d'])) {
                    $val = (int) $request[$field];
                    // Apply scaling for geometry fields when admin units are detected
                    if ($needs_scale) {
                        if ($field === 'width') { $val = max(1, min(1280, (int) round($val * $scale))); }
                        if ($field === 'height') { $val = max(1, min(720, (int) round($val * $scale))); }
                        if ($field === 'x_position') { $val = max(0, min(1280, (int) round($val * $scale))); }
                        if ($field === 'y_position') { $val = max(0, min(720, (int) round($val * $scale))); }
                    }
                    $update_data[$field] = $val;
                } else {
                    $update_data[$field] = sanitize_text_field($request[$field]);
                }
                $update_format[] = $format;
            }
        }
        
        if (!empty($update_data)) {
            $update_data['updated_at'] = current_time('mysql');
            $update_format[] = '%s';
            
            $result = $wpdb->update(
                $table_name,
                $update_data,
                ['id' => $container_id],
                $update_format,
                ['%d']
            );
            
            if ($result === false) {
                return new WP_Error('update_failed', 'Failed to update container: ' . $wpdb->last_error, ['status' => 500]);
            }
        }
        
        // Update background if specified
        if (isset($request['background'])) {
            $this->update_container_background($container_id, $request['background']);
        }

        // Update layout (zones) if provided
        if (isset($request['layout'])) {
            $layout_in = is_array($request['layout']) ? $request['layout'] : array();
            // If zones rects look like admin space, scale them down to Roku and mark authored_space
            $layout = $this->sanitize_container_layout($layout_in);
            $authored_space = 'roku_1280x720';
            $needs_zone_scale = false;
            foreach (($layout['zones'] ?? array()) as $z) {
                $rw = isset($z['rect']['w']) ? (int)$z['rect']['w'] : 0;
                $rh = isset($z['rect']['h']) ? (int)$z['rect']['h'] : 0;
                if ($rw > 1280 || $rh > 720) { $needs_zone_scale = true; break; }
            }
            if ($needs_zone_scale) {
                $authored_space = 'admin_1920x1080';
                foreach ($layout['zones'] as &$z) {
                    $z['rect']['x'] = max(0, (int) round(($z['rect']['x'] ?? 0) * $scale));
                    $z['rect']['y'] = max(0, (int) round(($z['rect']['y'] ?? 0) * $scale));
                    $z['rect']['w'] = max(1, (int) round(($z['rect']['w'] ?? 0) * $scale));
                    $z['rect']['h'] = max(1, (int) round(($z['rect']['h'] ?? 0) * $scale));
                }
                unset($z);
            }
            $layout['authored_space'] = $authored_space;
            $this->set_container_layout($container_id, $layout);
        }

        // Update behavior (e.g., shuffle) if provided
        if (isset($request['behavior'])) {
            $behavior = $request['behavior'];
            $this->set_container_behavior($container_id, $this->sanitize_container_behavior($behavior));
        }

        // Update overlay configuration (display_mode, trigger, position, animation) if provided
        if (isset($request['overlay'])) {
            $overlay = $request['overlay'];
            $this->set_container_overlay($container_id, $this->sanitize_container_overlay($overlay));
        }
        
    $req = new WP_REST_Request();
    $req->set_param('id', $container_id);
    return $this->get_item($req);
    }

    /**
     * Retrieve container layout (zones) from options storage.
     */
    private function get_container_layout($container_id) {
        $key = 'castconductor_container_layout_' . intval($container_id);
        $raw = get_option($key, null);
        if (is_string($raw)) {
            $decoded = json_decode($raw, true);
            if (is_array($decoded)) return $decoded;
        } elseif (is_array($raw)) {
            return $raw;
        }
        // Default layout structure
        return [
            'zones' => [],
            'activeZoneIds' => []
        ];
    }

    /**
     * Sanitize incoming layout payload.
     */
    private function sanitize_container_layout($layout) {
        $out = [ 'zones' => [], 'activeZoneIds' => [] ];
        if (is_array($layout)) {
            // zones
            if (!empty($layout['zones']) && is_array($layout['zones'])) {
                foreach ($layout['zones'] as $z) {
                    if (!is_array($z)) continue;
                    $id = isset($z['id']) ? sanitize_key($z['id']) : uniqid('zone_');
                    $name = isset($z['name']) ? sanitize_text_field($z['name']) : $id;
                    $rect = isset($z['rect']) && is_array($z['rect']) ? $z['rect'] : [];
                    $x = isset($rect['x']) ? intval($rect['x']) : 0;
                    $y = isset($rect['y']) ? intval($rect['y']) : 0;
                    $w = isset($rect['w']) ? intval($rect['w']) : 0;
                    $h = isset($rect['h']) ? intval($rect['h']) : 0;
                    $grid = isset($z['grid']) && is_array($z['grid']) ? $z['grid'] : [];
                    $cols = isset($grid['cols']) ? intval($grid['cols']) : 12;
                    $gutter = isset($grid['gutter']) ? intval($grid['gutter']) : 24;
                    $snap = isset($grid['snap']) ? intval($grid['snap']) : 8;
                    $allow = [];
                    if (isset($z['allow']) && is_array($z['allow'])) {
                        foreach ($z['allow'] as $t) { $allow[] = sanitize_key($t); }
                    }
                    $zIndex = isset($z['zIndex']) ? intval($z['zIndex']) : null;
                    $out['zones'][] = [
                        'id' => $id,
                        'name' => $name,
                        'rect' => ['x' => $x, 'y' => $y, 'w' => $w, 'h' => $h],
                        'grid' => ['cols' => $cols, 'gutter' => $gutter, 'snap' => $snap],
                        'allow' => $allow,
                        'zIndex' => $zIndex
                    ];
                }
            }
            // activeZoneIds
            if (!empty($layout['activeZoneIds']) && is_array($layout['activeZoneIds'])) {
                foreach ($layout['activeZoneIds'] as $id) { $out['activeZoneIds'][] = sanitize_key($id); }
            }
        }
        return $out;
    }

    /**
     * Persist container layout (zones) into options storage.
     */
    private function set_container_layout($container_id, $layout) {
        $key = 'castconductor_container_layout_' . intval($container_id);
        // Store as JSON for readability and portability
        update_option($key, wp_json_encode($layout));
    }

    /**
     * Retrieve per-container behavior flags (persisted in options)
     */
    private function get_container_behavior($container_id) {
        $key = 'castconductor_container_behavior_' . intval($container_id);
        $raw = get_option($key, null);
        $default = ['shuffle_items' => false];
        if ($raw === null) return $default;
        if (is_string($raw)) {
            $decoded = json_decode($raw, true);
            if (is_array($decoded)) return array_merge($default, $decoded);
        }
        if (is_array($raw)) return array_merge($default, $raw);
        return $default;
    }

    /** Persist per-container behavior flags */
    private function set_container_behavior($container_id, $behavior) {
        $key = 'castconductor_container_behavior_' . intval($container_id);
        update_option($key, wp_json_encode($behavior));
    }

    /** Sanitize behavior payload */
    private function sanitize_container_behavior($behavior) {
        $out = ['shuffle_items' => false];
        if (is_array($behavior)) {
            if (isset($behavior['shuffle_items'])) {
                $out['shuffle_items'] = (bool)$behavior['shuffle_items'] ? true : false;
            }
        }
        return $out;
    }

    /**
     * Retrieve per-container overlay configuration (persisted in options)
     * Used for menu/navigation system - overlays can slide up on remote button press
     */
    private function get_container_overlay($container_id) {
        $key = 'castconductor_container_overlay_' . intval($container_id);
        $raw = get_option($key, null);
        $default = [
            'display_mode' => 'visible',        // 'visible' (always shown) or 'overlay' (hidden until triggered)
            'trigger' => 'none',                // 'up', 'menu', 'ok', 'right', 'left', 'down', 'none'
            'position' => 'bottom',             // 'bottom', 'top', 'left', 'right', 'fullscreen'
            'animation' => 'slide',             // 'slide', 'fade', 'none'
            'dismiss_on_select' => true,        // Whether to hide overlay when user selects an item
            'background_color' => '#000000CC',  // Semi-transparent background
            'auto_dismiss_seconds' => 0         // 0 = no auto-dismiss, >0 = dismiss after N seconds of inactivity
        ];
        if ($raw === null) return $default;
        if (is_string($raw)) {
            $decoded = json_decode($raw, true);
            if (is_array($decoded)) return array_merge($default, $decoded);
        }
        if (is_array($raw)) return array_merge($default, $raw);
        return $default;
    }

    /** Persist per-container overlay configuration */
    private function set_container_overlay($container_id, $overlay) {
        $key = 'castconductor_container_overlay_' . intval($container_id);
        update_option($key, wp_json_encode($overlay));
    }

    /** Sanitize overlay payload */
    private function sanitize_container_overlay($overlay) {
        $out = [
            'display_mode' => 'visible',
            'trigger' => 'none',
            'position' => 'bottom',
            'animation' => 'slide',
            'dismiss_on_select' => true,
            'background_color' => '#000000CC',
            'auto_dismiss_seconds' => 0
        ];

        if (!is_array($overlay)) return $out;

        // display_mode: 'visible' or 'overlay'
        if (isset($overlay['display_mode'])) {
            $mode = sanitize_text_field($overlay['display_mode']);
            if (in_array($mode, ['visible', 'overlay'], true)) {
                $out['display_mode'] = $mode;
            }
        }

        // trigger: which remote button shows this overlay
        if (isset($overlay['trigger'])) {
            $trigger = sanitize_text_field($overlay['trigger']);
            $valid_triggers = ['up', 'down', 'left', 'right', 'ok', 'menu', 'back', 'play', 'replay', 'none'];
            if (in_array($trigger, $valid_triggers, true)) {
                $out['trigger'] = $trigger;
            }
        }

        // position: where the overlay appears from
        if (isset($overlay['position'])) {
            $pos = sanitize_text_field($overlay['position']);
            $valid_positions = ['bottom', 'top', 'left', 'right', 'fullscreen', 'center'];
            if (in_array($pos, $valid_positions, true)) {
                $out['position'] = $pos;
            }
        }

        // animation: how the overlay appears
        if (isset($overlay['animation'])) {
            $anim = sanitize_text_field($overlay['animation']);
            $valid_animations = ['slide', 'fade', 'none'];
            if (in_array($anim, $valid_animations, true)) {
                $out['animation'] = $anim;
            }
        }

        // dismiss_on_select: boolean
        if (isset($overlay['dismiss_on_select'])) {
            $out['dismiss_on_select'] = (bool)$overlay['dismiss_on_select'];
        }

        // background_color: hex color with optional alpha
        if (isset($overlay['background_color'])) {
            $color = sanitize_text_field($overlay['background_color']);
            // Validate hex color (with optional alpha: #RRGGBB or #RRGGBBAA)
            if (preg_match('/^#[0-9a-fA-F]{6}([0-9a-fA-F]{2})?$/', $color)) {
                $out['background_color'] = $color;
            }
        }

        // auto_dismiss_seconds: integer 0-300
        if (isset($overlay['auto_dismiss_seconds'])) {
            $seconds = (int)$overlay['auto_dismiss_seconds'];
            $out['auto_dismiss_seconds'] = max(0, min(300, $seconds));
        }

        return $out;
    }
    
    /**
     * Delete container
     */
    public function delete_item($request) {
        global $wpdb;
        
        $container_id = (int) $request['id'];
        $container = $this->get_container_by_id($container_id);
        
        if (!$container) {
            return new WP_Error('container_not_found', 'Container not found', ['status' => 404]);
        }
        
        // Delete container blocks assignments first
        $blocks_table = $wpdb->prefix . 'castconductor_container_blocks';
        $wpdb->delete($blocks_table, ['container_id' => $container_id], ['%d']);
        
        // Delete background records
        $bg_table = $wpdb->prefix . 'castconductor_backgrounds';
        $wpdb->delete($bg_table, ['container_id' => $container_id], ['%d']);

        // Clean up options-based metadata (layout, behavior, overlay, zone assignments)
        delete_option('castconductor_container_layout_' . $container_id);
        delete_option('castconductor_container_behavior_' . $container_id);
        delete_option('castconductor_container_overlay_' . $container_id);
        delete_option('castconductor_container_zone_assignments_' . $container_id);
        
        // Delete container
        $table_name = $wpdb->prefix . 'castconductor_containers';
        $result = $wpdb->delete($table_name, ['id' => $container_id], ['%d']);
        
        if ($result === false) {
            return new WP_Error('deletion_failed', 'Failed to delete container: ' . $wpdb->last_error, ['status' => 500]);
        }
        
        return rest_ensure_response(['deleted' => true, 'id' => $container_id]);
    }
    
    /**
     * Get container blocks
     */
    public function get_container_blocks($request) {
        $container_id = (int) $request['id'];
        return rest_ensure_response($this->get_container_assigned_blocks($container_id));
    }
    
    /**
     * Assign block to container
     */
    public function assign_block_to_container($request) {
        global $wpdb;
        
        $container_id = (int) $request['id'];
        $block_id = (int) $request['block_id'];
        $rotation_percentage = (float) $request['rotation_percentage'];
        $rotation_order = (int) $request['rotation_order'];
        
        // Validate container exists
        if (!$this->get_container_by_id($container_id)) {
            return new WP_Error('container_not_found', 'Container not found', ['status' => 404]);
        }
        
        // Validate content block exists
        $content_blocks_table = $wpdb->prefix . 'castconductor_content_blocks';
        $content_block = $wpdb->get_row($wpdb->prepare(
            "SELECT id FROM $content_blocks_table WHERE id = %d AND enabled = 1",
            $block_id
        ));
        
        if (!$content_block) {
            return new WP_Error('block_not_found', 'Content block not found', ['status' => 404]);
        }
        
        // Check if assignment already exists
        $blocks_table = $wpdb->prefix . 'castconductor_container_blocks';
        $existing = $wpdb->get_var($wpdb->prepare(
            "SELECT id FROM $blocks_table WHERE container_id = %d AND content_block_id = %d",
            $container_id, $block_id
        ));
        
        if ($existing) {
            return new WP_Error('assignment_exists', 'Block already assigned to this container', ['status' => 409]);
        }
        
        // Validate rotation percentages don't exceed 100%
        $current_total = $wpdb->get_var($wpdb->prepare(
            "SELECT SUM(rotation_percentage) FROM $blocks_table WHERE container_id = %d",
            $container_id
        ));
        
        if (($current_total + $rotation_percentage) > 100) {
            return new WP_Error('rotation_exceeded', 'Total rotation percentage would exceed 100%', ['status' => 400]);
        }
        
        // Create assignment
        $result = $wpdb->insert(
            $blocks_table,
            [
                'container_id' => $container_id,
                'content_block_id' => $block_id,
                'rotation_percentage' => $rotation_percentage,
                'rotation_order' => $rotation_order,
                'enabled' => 1,
                'created_at' => current_time('mysql')
            ],
            ['%d', '%d', '%f', '%d', '%d', '%s']
        );
        
        if ($result === false) {
            return new WP_Error('assignment_failed', 'Failed to assign block: ' . $wpdb->last_error, ['status' => 500]);
        }
        
        return rest_ensure_response([
            'id' => $wpdb->insert_id,
            'container_id' => $container_id,
            'content_block_id' => $block_id,
            'rotation_percentage' => $rotation_percentage,
            'rotation_order' => $rotation_order,
            'message' => 'Block assigned successfully'
        ]);
    }
    
    /**
     * Remove block from container
     */
    public function remove_block_from_container($request) {
        global $wpdb;
        
        $container_id = (int) $request['id'];
        $block_id = (int) $request['block_id'];
        
        $blocks_table = $wpdb->prefix . 'castconductor_container_blocks';
        $result = $wpdb->delete(
            $blocks_table,
            ['container_id' => $container_id, 'content_block_id' => $block_id],
            ['%d', '%d']
        );
        
        if ($result === false) {
            return new WP_Error('removal_failed', 'Failed to remove block assignment: ' . $wpdb->last_error, ['status' => 500]);
        }
        
        if ($result === 0) {
            return new WP_Error('assignment_not_found', 'Block assignment not found', ['status' => 404]);
        }
        
        return rest_ensure_response([
            'removed' => true,
            'container_id' => $container_id,
            'content_block_id' => $block_id
        ]);
    }

    /**
     * Update a single block assignment (rotation_percentage and/or rotation_order)
     */
    public function update_block_assignment($request) {
        global $wpdb;

        $container_id = (int) $request['id'];
        $block_id = (int) $request['block_id'];

        // Validate container exists
        if (!$this->get_container_by_id($container_id)) {
            return new WP_Error('container_not_found', 'Container not found', ['status' => 404]);
        }

        $blocks_table = $wpdb->prefix . 'castconductor_container_blocks';

        // Ensure assignment exists
        $assignment = $wpdb->get_row($wpdb->prepare(
            "SELECT * FROM $blocks_table WHERE container_id = %d AND content_block_id = %d",
            $container_id, $block_id
        ), ARRAY_A);

        if (!$assignment) {
            return new WP_Error('assignment_not_found', 'Content block not found in this container', ['status' => 404]);
        }

        $update_data = [];
        $update_format = [];

        if (isset($request['rotation_percentage'])) {
            $new_pct = (float) $request['rotation_percentage'];
            if ($new_pct < 0 || $new_pct > 100) {
                return new WP_Error('invalid_rotation', 'Rotation percentage must be between 0 and 100', ['status' => 400]);
            }
            // Validate total ≤ 100 after change
            $other_total = $this->get_total_rotation_percentage($container_id, $block_id);
            if (($other_total + $new_pct) > 100) {
                return new WP_Error('rotation_exceeded', 'Total rotation percentage would exceed 100%', ['status' => 400]);
            }
            $update_data['rotation_percentage'] = $new_pct;
            $update_format[] = '%f';
        }

        if (isset($request['rotation_order'])) {
            $update_data['rotation_order'] = (int) $request['rotation_order'];
            $update_format[] = '%d';
        }

        if (empty($update_data)) {
            return rest_ensure_response([
                'updated' => false,
                'message' => 'No changes provided'
            ]);
        }

        $result = $wpdb->update(
            $blocks_table,
            $update_data,
            ['container_id' => $container_id, 'content_block_id' => $block_id],
            $update_format,
            ['%d', '%d']
        );

        if ($result === false) {
            return new WP_Error('update_failed', 'Failed to update assignment: ' . $wpdb->last_error, ['status' => 500]);
        }

        // Return updated assignment
        $updated = $wpdb->get_row($wpdb->prepare(
            "SELECT * FROM $blocks_table WHERE container_id = %d AND content_block_id = %d",
            $container_id, $block_id
        ), ARRAY_A);

        return rest_ensure_response([
            'updated' => true,
            'assignment' => $updated
        ]);
    }

    /**
     * Batch reorder assignments within a container.
     * Accepts either:
     * - sequence: [block_id1, block_id2, ...] (sets rotation_order 1..n)
     * - order: [{block_id: X, rotation_order: Y}, ...]
     */
    public function reorder_container_blocks($request) {
        global $wpdb;

        $container_id = (int) $request['id'];

        if (!$this->get_container_by_id($container_id)) {
            return new WP_Error('container_not_found', 'Container not found', ['status' => 404]);
        }

        $blocks_table = $wpdb->prefix . 'castconductor_container_blocks';

        $sequence = isset($request['sequence']) && is_array($request['sequence']) ? $request['sequence'] : null;
        $order = isset($request['order']) && is_array($request['order']) ? $request['order'] : null;

        if (!$sequence && !$order) {
            return new WP_Error('invalid_payload', 'Provide either sequence[] or order[] array', ['status' => 400]);
        }

        // Validate all provided blocks belong to this container
        $valid_ids = $wpdb->get_col($wpdb->prepare(
            "SELECT content_block_id FROM $blocks_table WHERE container_id = %d AND enabled = 1",
            $container_id
        ));
        // Normalize types to integers to avoid strict comparison mismatches
        $valid_ids = array_map('intval', $valid_ids ?: []);

        $updates = [];
        if ($sequence) {
            $i = 1;
            foreach ($sequence as $bid) {
                $bid = (int) $bid;
                if (!in_array($bid, $valid_ids, true)) {
                    return new WP_Error('assignment_not_found', 'Content block ' . $bid . ' not in this container', ['status' => 404]);
                }
                $updates[$bid] = $i++;
            }
        } else {
            foreach ($order as $item) {
                if (!isset($item['block_id'])) { continue; }
                $bid = (int) $item['block_id'];
                $ord = isset($item['rotation_order']) ? (int) $item['rotation_order'] : null;
                if (!in_array($bid, $valid_ids, true)) {
                    return new WP_Error('assignment_not_found', 'Content block ' . $bid . ' not in this container', ['status' => 404]);
                }
                if ($ord === null) { continue; }
                $updates[$bid] = $ord;
            }
        }

        if (empty($updates)) {
            return new WP_Error('invalid_payload', 'No valid updates provided', ['status' => 400]);
        }

        // Apply updates in a transaction when available
        $wpdb->query('START TRANSACTION');
        $ok = true;
        foreach ($updates as $bid => $ord) {
            $res = $wpdb->update(
                $blocks_table,
                ['rotation_order' => (int)$ord],
                ['container_id' => $container_id, 'content_block_id' => (int)$bid],
                ['%d'],
                ['%d', '%d']
            );
            if ($res === false) { $ok = false; break; }
        }

        if ($ok) {
            $wpdb->query('COMMIT');
        } else {
            $wpdb->query('ROLLBACK');
            return new WP_Error('update_failed', 'Failed to reorder assignments: ' . $wpdb->last_error, ['status' => 500]);
        }

        // Return new ordered list
        $updated_list = $this->get_container_assigned_blocks($container_id);
        return rest_ensure_response([
            'updated' => true,
            'assignments' => $updated_list
        ]);
    }

    /**
     * Helper: sum rotation percentages for a container, optionally excluding a block ID
     */
    private function get_total_rotation_percentage($container_id, $exclude_block_id = null) {
        global $wpdb;
        $blocks_table = $wpdb->prefix . 'castconductor_container_blocks';
        if ($exclude_block_id) {
            $sql = $wpdb->prepare(
                "SELECT COALESCE(SUM(rotation_percentage),0) FROM $blocks_table WHERE container_id = %d AND content_block_id <> %d",
                $container_id, $exclude_block_id
            );
        } else {
            $sql = $wpdb->prepare(
                "SELECT COALESCE(SUM(rotation_percentage),0) FROM $blocks_table WHERE container_id = %d",
                $container_id
            );
        }
        $val = $wpdb->get_var($sql);
        return (float) ($val ?: 0);
    }
    
    // ===== HELPER METHODS =====
    
    /**
     * Get container by ID
     */
    private function get_container_by_id($container_id) {
        global $wpdb;
        
        $table_name = $wpdb->prefix . 'castconductor_containers';
        return $wpdb->get_row($wpdb->prepare(
            "SELECT * FROM $table_name WHERE id = %d",
            $container_id
        ), ARRAY_A);
    }
    
    /**
     * Get container background configuration
     */
    private function get_container_background($container_id) {
        global $wpdb;
        
        $bg_table = $wpdb->prefix . 'castconductor_backgrounds';
        $row = $wpdb->get_row($wpdb->prepare(
            "SELECT * FROM $bg_table WHERE container_id = %d AND enabled = 1 ORDER BY created_at DESC LIMIT 1",
            $container_id
        ), ARRAY_A);
        
        if (!$row) {
            // Default lightweight background
            return [
                'type' => 'color',
                'color' => get_option('castconductor_background_color', '#000000'),
                'z_index' => 0
            ];
        }
        
        $config = [];
        if (!empty($row['config'])) {
            $decoded = json_decode($row['config'], true);
            if (is_array($decoded)) {
                $config = $decoded;
            }
        }
        
        // Normalize a simple shape expected by the editor
        $bg = [
            'type' => $row['type'] ?? 'color',
            'z_index' => isset($row['z_index']) ? (int)$row['z_index'] : 0,
        ];
        if ($bg['type'] === 'static') {
            $bg['image_url'] = $config['image_url'] ?? '';
        } elseif ($bg['type'] === 'color') {
            $bg['color'] = $config['color'] ?? '#000000';
        } elseif ($bg['type'] === 'rotating') {
            $bg['rotation_interval'] = isset($config['rotation_interval']) ? (int)$config['rotation_interval'] : 30;
        }
        return $bg;
    }
    
    /**
     * Get assigned blocks for container
     */
    private function get_container_assigned_blocks($container_id) {
        global $wpdb;
        
        $blocks_table = $wpdb->prefix . 'castconductor_container_blocks';
        $content_blocks_table = $wpdb->prefix . 'castconductor_content_blocks';
        
        $results = $wpdb->get_results($wpdb->prepare(
            "SELECT cb.*, bl.name as block_name, bl.type as block_type, bl.enabled as block_status, bl.visual_config as block_visual_config, bl.updated_at as block_updated_at
             FROM $blocks_table cb 
             LEFT JOIN $content_blocks_table bl ON cb.content_block_id = bl.id 
             WHERE cb.container_id = %d AND cb.enabled = 1 
             ORDER BY cb.rotation_order ASC, cb.created_at DESC",
            $container_id
        ), ARRAY_A);
        
        $rows = $results ?: [];
        // Option A augmentation: has_config + config_version for caching/capability
        foreach ($rows as &$row) {
            $has_cfg = false;
            if (!empty($row['block_visual_config'])) {
                $decoded = json_decode($row['block_visual_config'], true);
                $has_cfg = is_array($decoded) && !empty($decoded);
            }
            $row['has_config'] = $has_cfg ? 1 : 0;
            $row['config_version'] = !empty($row['block_updated_at']) ? strtotime($row['block_updated_at']) : null;
            unset($row['block_visual_config']);
            unset($row['block_updated_at']);
        }
        unset($row);

        return $rows;
    }
    
    /**
     * Get count of assigned blocks
     */
    private function get_container_blocks_count($container_id) {
        global $wpdb;
        
        $blocks_table = $wpdb->prefix . 'castconductor_container_blocks';
        return (int) $wpdb->get_var($wpdb->prepare(
            "SELECT COUNT(*) FROM $blocks_table WHERE container_id = %d AND enabled = 1",
            $container_id
        ));
    }
    
    /**
     * Create container background
     */
    private function create_container_background($container_id, $background_config) {
        global $wpdb;
        
        $bg_table = $wpdb->prefix . 'castconductor_backgrounds';
        $type = sanitize_text_field($background_config['type'] ?? 'color');
        $z_index = (int) ($background_config['z_index'] ?? 0);
        
        // Build config JSON compatible with backgrounds table
        $cfg = [];
        switch ($type) {
            case 'static':
                $cfg['image_url'] = esc_url_raw($background_config['image_url'] ?? '');
                $cfg['fit'] = 'cover';
                break;
            case 'color':
                $cfg['color'] = sanitize_hex_color($background_config['color'] ?? '#000000');
                break;
            case 'rotating':
                $cfg['rotation_interval'] = (int) ($background_config['rotation_interval'] ?? 30);
                $cfg['images'] = [];
                break;
            default:
                // fallback to color
                $type = 'color';
                $cfg['color'] = '#000000';
        }
        
        $wpdb->insert(
            $bg_table,
            [
                'container_id' => $container_id,
                'name' => 'Container Background',
                'type' => $type,
                'config' => wp_json_encode($cfg),
                'z_index' => $z_index,
                'enabled' => 1,
                'is_active' => 0,
                'created_at' => current_time('mysql')
            ],
            ['%d', '%s', '%s', '%s', '%d', '%d', '%d', '%s']
        );
    }
    
    /**
     * Update container background
     */
    private function update_container_background($container_id, $background_config) {
        // For now, delete old and create new (can be optimized later)
        global $wpdb;
        
        $bg_table = $wpdb->prefix . 'castconductor_backgrounds';
        $wpdb->delete($bg_table, ['container_id' => $container_id], ['%d']);
        
        if (!empty($background_config)) {
            $this->create_container_background($container_id, $background_config);
        }
    }
    
    // ===== PERMISSION CALLBACKS =====
    // Allows: Administrators, castconductor_admin role, and Editors (edit_posts)
    
    public function get_items_permissions_check($request) {
        return current_user_can('manage_options') || current_user_can('edit_posts');
    }
    
    public function get_item_permissions_check($request) {
        return current_user_can('manage_options') || current_user_can('edit_posts');
    }
    
    public function create_item_permissions_check($request) {
        return current_user_can('manage_options') || current_user_can('edit_posts');
    }
    
    public function update_item_permissions_check($request) {
        return current_user_can('manage_options') || current_user_can('edit_posts');
    }
    
    public function delete_item_permissions_check($request) {
        return current_user_can('manage_options') || current_user_can('edit_posts');
    }
    
    // ===== SCHEMA =====
    
    public function get_item_schema() {
        $schema = [
            '$schema' => 'http://json-schema.org/draft-04/schema#',
            'title' => 'container',
            'type' => 'object',
            'properties' => [
                'id' => [
                    'description' => 'Unique identifier for the container',
                    'type' => 'integer',
                    'readonly' => true
                ],
                'name' => [
                    'description' => 'Container name',
                    'type' => 'string',
                    'required' => true
                ],
                'position' => [
                    'description' => 'Container position preset',
                    'type' => 'string',
                    'enum' => ['lower_third', 'upper_third', 'full_screen', 'left_sidebar', 'right_sidebar', 'custom']
                ],
                'width' => [
                    'description' => 'Container width in pixels',
                    'type' => 'integer',
                    'minimum' => 1,
                    'maximum' => 1920
                ],
                'height' => [
                    'description' => 'Container height in pixels', 
                    'type' => 'integer',
                    'minimum' => 1,
                    'maximum' => 1080
                ],
                'x_position' => [
                    'description' => 'X coordinate position',
                    'type' => 'integer',
                    'minimum' => 0
                ],
                'y_position' => [
                    'description' => 'Y coordinate position',
                    'type' => 'integer', 
                    'minimum' => 0
                ],
                'z_index' => [
                    'description' => 'Z-index for layering (higher = front)',
                    'type' => 'integer',
                    'minimum' => 0,
                    'maximum' => 999
                ],
                'rotation_enabled' => [
                    'description' => 'Enable content rotation',
                    'type' => 'boolean',
                    'default' => true
                ],
                'rotation_interval' => [
                    'description' => 'Rotation interval in seconds',
                    'type' => 'integer',
                    'minimum' => 5,
                    'maximum' => 300,
                    'default' => 30
                ],
                'enabled' => [
                    'description' => 'Container enabled status',
                    'type' => 'boolean',
                    'default' => true
                ],
                'background' => [
                    'description' => 'Background configuration',
                    'type' => 'object',
                    'context' => ['view','edit'],
                    'properties' => [
                        'type' => [
                            'type' => 'string',
                            'enum' => ['static', 'rotating', 'video', 'color']
                        ],
                        'image_url' => ['type' => 'string', 'format' => 'uri'],
                        'color' => ['type' => 'string', 'pattern' => '^#[0-9a-fA-F]{6}$'],
                        'rotation_interval' => ['type' => 'integer'],
                        'z_index' => ['type' => 'integer']
                    ]
                ],
                'layout' => [
                    'description' => 'Layout metadata including zones and activeZoneIds',
                    'type' => 'object',
                    'context' => ['view','edit'],
                    'properties' => [
                        'zones' => [
                            'type' => 'array',
                            'items' => [
                                'type' => 'object',
                                'properties' => [
                                    'id' => ['type' => 'string'],
                                    'name' => ['type' => 'string'],
                                    'rect' => [
                                        'type' => 'object',
                                        'properties' => [
                                            'x' => ['type' => 'integer'],
                                            'y' => ['type' => 'integer'],
                                            'w' => ['type' => 'integer'],
                                            'h' => ['type' => 'integer']
                                        ]
                                    ],
                                    'grid' => [
                                        'type' => 'object',
                                        'properties' => [
                                            'cols' => ['type' => 'integer'],
                                            'gutter' => ['type' => 'integer'],
                                            'snap' => ['type' => 'integer']
                                        ]
                                    ],
                                    'allow' => ['type' => 'array', 'items' => ['type' => 'string']],
                                    'zIndex' => ['type' => 'integer']
                                ]
                            ]
                        ],
                        'activeZoneIds' => ['type' => 'array', 'items' => ['type' => 'string']]
                    ]
                ],
                'behavior' => [
                    'description' => 'Behavior flags for container scheduling/previews',
                    'type' => 'object',
                    'context' => ['view','edit'],
                    'properties' => [
                        'shuffle_items' => [ 'type' => 'boolean', 'default' => false ]
                    ]
                ],
                'overlay' => [
                    'description' => 'Overlay/navigation configuration for menu system',
                    'type' => 'object',
                    'context' => ['view','edit'],
                    'properties' => [
                        'display_mode' => [
                            'type' => 'string',
                            'enum' => ['visible', 'overlay'],
                            'default' => 'visible',
                            'description' => 'Whether container is always visible or shown as overlay on trigger'
                        ],
                        'trigger' => [
                            'type' => 'string',
                            'enum' => ['up', 'down', 'left', 'right', 'ok', 'menu', 'back', 'play', 'replay', 'none'],
                            'default' => 'none',
                            'description' => 'Remote button that shows this overlay'
                        ],
                        'position' => [
                            'type' => 'string',
                            'enum' => ['bottom', 'top', 'left', 'right', 'fullscreen', 'center'],
                            'default' => 'bottom',
                            'description' => 'Screen position where overlay appears'
                        ],
                        'animation' => [
                            'type' => 'string',
                            'enum' => ['slide', 'fade', 'none'],
                            'default' => 'slide',
                            'description' => 'Animation style for showing/hiding overlay'
                        ],
                        'dismiss_on_select' => [
                            'type' => 'boolean',
                            'default' => true,
                            'description' => 'Whether to hide overlay when user selects an item'
                        ],
                        'background_color' => [
                            'type' => 'string',
                            'pattern' => '^#[0-9a-fA-F]{6}([0-9a-fA-F]{2})?$',
                            'default' => '#000000CC',
                            'description' => 'Overlay background color (hex with optional alpha)'
                        ],
                        'auto_dismiss_seconds' => [
                            'type' => 'integer',
                            'minimum' => 0,
                            'maximum' => 300,
                            'default' => 0,
                            'description' => 'Auto-dismiss after N seconds of inactivity (0=disabled)'
                        ]
                    ]
                ]
            ]
        ];
        
        return $schema;
    }
}
