<?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
 */

/**
 * CastConductor Activation Wizard
 * 
 * Handles first-run setup and content seeding
 * Creates real test data during setup for immediate value
 */

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

class CastConductor_Activation_Wizard {
    
    /**
     * Constructor
     */
    public function __construct() {
        add_action('admin_init', array($this, 'check_activation_wizard'));
        add_action('wp_ajax_castconductor_run_setup_wizard', array($this, 'run_setup_wizard'));
    }
    
    /**
     * Check if activation wizard should run
     */
    public function check_activation_wizard() {
        if (get_option('castconductor_activation_wizard_pending') && current_user_can('manage_options')) {
            add_action('admin_notices', array($this, 'show_activation_notice'));
        }
    }
    
    /**
     * Show activation setup notice
     */
    public function show_activation_notice() {
        // Don't show the notice if we're already on the setup wizard page
        $current_screen = get_current_screen();
        if ($current_screen && $current_screen->id === 'castconductor_page_castconductor-setup') {
            return;
        }
        
        ?>
        <div class="notice notice-info is-dismissible castconductor-setup-notice">
            <h3><?php _e('🚀 Welcome to CastConductor!', 'castconductor'); ?></h3>
            <p><?php _e('Complete the setup wizard to configure your station.', 'castconductor'); ?></p>
            <p>
                <?php _e('This wizard will create sample shoutouts, sponsors, and promos to get you started.', 'castconductor'); ?>
            </p>
            <p>
                <a href="<?php echo admin_url('admin.php?page=castconductor-setup'); ?>" class="button button-primary">
                    <?php _e('Run Setup Wizard', 'castconductor'); ?>
                </a>
                <a href="#" class="button button-secondary castconductor-skip-setup">
                    <?php _e('Skip Setup (Manual Configuration)', 'castconductor'); ?>
                </a>
            </p>
        </div>
        
        <script>
        jQuery(document).ready(function($) {
            $('.castconductor-skip-setup').on('click', function(e) {
                e.preventDefault();
                if (confirm('<?php _e('Skip setup? You can run it later from CastConductor → Settings.', 'castconductor'); ?>')) {
                    $.post(ajaxurl, {
                        action: 'castconductor_skip_setup',
                        nonce: '<?php echo wp_create_nonce('castconductor_setup'); ?>'
                    }, function() {
                        $('.castconductor-setup-notice').fadeOut();
                    });
                }
            });
        });
        </script>
        <?php
    }
    
    /**
     * Run the complete setup wizard
     */
    public function run_setup_wizard() {
        // Verify nonce
        if (!wp_verify_nonce($_POST['nonce'], 'castconductor_setup')) {
            wp_die('Security check failed');
        }
        
        // Check permissions
        if (!current_user_can('manage_options')) {
            wp_die('Insufficient permissions');
        }
        
        // Prevent duplicate content creation if wizard already completed
        // User must explicitly confirm to re-run
        $already_completed = get_option('castconductor_setup_completed');
        $force_rerun = isset($_POST['force_rerun']) && $_POST['force_rerun'] === 'true';
        
        if ($already_completed && !$force_rerun) {
            wp_send_json_error(array(
                'message' => 'Setup wizard has already been completed. Running it again would create duplicate content.',
                'already_completed' => true,
                'completed_date' => get_option('castconductor_setup_date'),
                'allow_rerun' => true
            ));
            return;
        }
        
        $setup_results = array();
        $timing = array();
        $start_total = microtime(true);
        
        try {
            // Step 1: Create database structure
            $step_start = microtime(true);
            $setup_results['database'] = $this->setup_database();
            $timing['1_database'] = round((microtime(true) - $step_start) * 1000) . 'ms';
            error_log('[CC Wizard Timing] Step 1 (database): ' . $timing['1_database']);
            
            // Step 2: Create sample content
            $step_start = microtime(true);
            $setup_results['content'] = $this->create_real_test_content();
            $timing['2_content'] = round((microtime(true) - $step_start) * 1000) . 'ms';
            error_log('[CC Wizard Timing] Step 2 (content): ' . $timing['2_content']);
            
            // Step 3: Setup default branding
            $step_start = microtime(true);
            $setup_results['branding'] = $this->setup_default_branding();
            $timing['3_branding'] = round((microtime(true) - $step_start) * 1000) . 'ms';
            error_log('[CC Wizard Timing] Step 3 (branding): ' . $timing['3_branding']);
            
            // Step 3b: Import misc-elements and resolve placeholders in content blocks
            $step_start = microtime(true);
            $setup_results['misc_elements'] = $this->import_misc_elements_and_resolve_placeholders();
            $timing['3b_misc_elements'] = round((microtime(true) - $step_start) * 1000) . 'ms';
            error_log('[CC Wizard Timing] Step 3b (misc_elements): ' . $timing['3b_misc_elements']);
            
            // Step 4: Configure real API endpoints
            $step_start = microtime(true);
            $setup_results['apis'] = $this->configure_real_apis();
            $timing['4_apis'] = round((microtime(true) - $step_start) * 1000) . 'ms';
            error_log('[CC Wizard Timing] Step 4 (apis): ' . $timing['4_apis']);
            
            // Step 5: (removed) Create default container assignments
            // Duplicate seeding removed to avoid conflicts with production defaults applied in setup_database()
            // $setup_results['assignments'] = $this->create_default_assignments();

            // Step 6: Seed Upper Third Left/Right zone assignments (Phase 3 parity)
            $step_start = microtime(true);
            $setup_results['zone_assignments'] = $this->seed_upper_third_zone_assignments();
            $timing['6_zone_assignments'] = round((microtime(true) - $step_start) * 1000) . 'ms';
            error_log('[CC Wizard Timing] Step 6 (zone_assignments): ' . $timing['6_zone_assignments']);

            // Step 7: Create a default Scene with global branding/background and standard containers
            $step_start = microtime(true);
            $setup_results['default_scene'] = $this->create_default_scene_with_containers();
            $timing['7_default_scene'] = round((microtime(true) - $step_start) * 1000) . 'ms';
            error_log('[CC Wizard Timing] Step 7 (default_scene): ' . $timing['7_default_scene']);
            
            $timing['TOTAL'] = round((microtime(true) - $start_total) * 1000) . 'ms';
            error_log('[CC Wizard Timing] TOTAL: ' . $timing['TOTAL']);
            $setup_results['timing'] = $timing;
            
            // Mark setup as completed
            update_option('castconductor_setup_completed', true);
            update_option('castconductor_activation_wizard_pending', false);
            update_option('castconductor_setup_date', current_time('mysql'));
            
            wp_send_json_success(array(
                'message' => 'CastConductor V5 setup completed successfully!',
                'details' => $setup_results,
                'redirect' => admin_url('admin.php?page=castconductor')
            ));
            
        } catch (Exception $e) {
            error_log('CastConductor Setup Error: ' . $e->getMessage());
            wp_send_json_error(array(
                'message' => 'Setup failed: ' . $e->getMessage(),
                'details' => $setup_results
            ));
        }
    }
    
    /**
     * Setup database structure
     */
    private function setup_database() {
        $database = new CastConductor_Database();
        
        // Create default containers
        $container_id = $database->create_default_containers();
        
        // Create default backgrounds  
        $background_id = $database->create_default_backgrounds();
        // Set current background to gradient by default
        if ($background_id) {
            update_option('castconductor_current_background_1920x1080', (int)$background_id);
        }
        
        // Create default content blocks (idempotent) or de-duplicate if they already exist
        $existing = get_option('castconductor_default_content_blocks');
        if (!is_array($existing) || empty($existing)) {
            $content_blocks = $database->create_default_content_blocks();
        } else {
            if (method_exists($database, 'maybe_deduplicate_content_blocks')) {
                $database->maybe_deduplicate_content_blocks();
            }
            $content_blocks = $existing;
        }

        // Seed default lower-third assignments (50/30/10/10) so preview works OOTB
        try {
            $lower_id = (int) get_option('castconductor_default_lower_third_id');
            if ($lower_id > 0) {
                $req = new WP_REST_Request('POST', '/castconductor/v5/containers/' . $lower_id . '/defaults');
                $req->set_param('interval', 15);
                $req->set_param('mode', 'full_reset');
                $ctl = new CastConductor_Containers_Controller();
                $ctl->apply_production_defaults($req);
            }
        } catch (Exception $e) {
            // non-fatal
        }
        
        return array(
            'containers_created' => 1,
            'backgrounds_created' => 3,
            'content_blocks_created' => count($content_blocks),
            'default_container_id' => $container_id,
            'default_background_id' => $background_id,
            'content_block_ids' => $content_blocks
        );
    }
    
    /**
     * Create real test content (skips if sample content already exists)
     */
    private function create_real_test_content() {
        $created_content = array();
        
        // Check if sample content already exists (created by wizard)
        $existing_shoutouts = get_posts(array(
            'post_type' => 'cc_shoutout',
            'meta_key' => 'cc_shoutout_source',
            'meta_value' => 'setup_wizard',
            'posts_per_page' => 1
        ));
        
        $existing_sponsors = get_posts(array(
            'post_type' => 'cc_sponsor',
            'meta_key' => 'cc_sponsor_source',
            'meta_value' => 'setup_wizard',
            'posts_per_page' => 1
        ));
        
        $existing_promos = get_posts(array(
            'post_type' => 'cc_promo',
            'meta_key' => 'cc_promo_source',
            'meta_value' => 'setup_wizard',
            'posts_per_page' => 1
        ));
        
        // Only create if not already present (prevents duplicates on re-run)
        if (empty($existing_shoutouts)) {
            $created_content['shoutouts'] = $this->create_sample_shoutouts();
        } else {
            $created_content['shoutouts'] = array('skipped' => true, 'reason' => 'Sample shoutouts already exist');
        }
        
        if (empty($existing_sponsors)) {
            $created_content['sponsors'] = $this->create_sample_sponsor();
        } else {
            $created_content['sponsors'] = array('skipped' => true, 'reason' => 'Sample sponsor already exists');
        }
        
        if (empty($existing_promos)) {
            $created_content['promos'] = $this->create_sample_promo();
        } else {
            $created_content['promos'] = array('skipped' => true, 'reason' => 'Sample promo already exists');
        }
        
        return $created_content;
    }
    
    /**
     * Create sample shoutouts with realistic content
     */
    private function create_sample_shoutouts() {
        $shoutouts_data = array(
            array(
                'message' => 'Locked in from Bushwick. Big love!',
                'name' => 'Sasha',
                'location' => 'Brooklyn, NY'
            ),
            array(
                'message' => 'Morning commute, 128 BPM. Let\'s gooo!',
                'name' => 'Miguel',
                'location' => 'San Diego, CA'
            ),
            array(
                'message' => 'Birthday beats n basslines to Maya, xox Mami!',
                'name' => 'Ava',
                'location' => 'Miami, FL'
            ),
            array(
                'message' => 'Earbuds in. Warehouse in my head right now!',
                'name' => 'Felix',
                'location' => 'Berlin, Germany'
            ),
            array(
                'message' => 'Bumping bass in traffic. Windows down, PLUR up!',
                'name' => 'Isabel',
                'location' => 'Las Vegas, NV'
            )
        );
        
        $created_shoutouts = array();
        
        foreach ($shoutouts_data as $shoutout) {
            $post_id = wp_insert_post(array(
                'post_title' => sprintf('Shoutout from %s', $shoutout['name']),
                'post_content' => $shoutout['message'],
                'post_type' => 'cc_shoutout',
                'post_status' => 'publish',
                'meta_input' => array(
                    'cc_shoutout_name' => $shoutout['name'],
                    'cc_shoutout_location' => $shoutout['location'],
                    'cc_shoutout_approved' => true,
                    'cc_shoutout_source' => 'setup_wizard'
                )
            ));
            
            if (!is_wp_error($post_id)) {
                $created_shoutouts[] = $post_id;
            }
        }
        
        return $created_shoutouts;
    }
    
    /**
     * Create sample sponsor with featured image
     */
    private function create_sample_sponsor() {
        $sponsor_data = array(
            'name' => 'Cafe y Dulce',
            'message' => 'Espresso Yourself Before You Wreck Yourself',
            'featured_image' => CASTCONDUCTOR_PLUGIN_DIR . 'wizard-content/cafe-pexels-crisferro73-692666.jpeg',
            'image_credit' => 'Photo by Cris Ferrero on Pexels'
        );
        
        $post_id = wp_insert_post(array(
            'post_title' => $sponsor_data['name'],
            'post_content' => $sponsor_data['message'],
            'post_type' => 'cc_sponsor',
            'post_status' => 'publish',
            'meta_input' => array(
                'cc_sponsor_start_date' => date('Y-m-d H:i:s'),
                'cc_sponsor_end_date' => date('Y-m-d H:i:s', strtotime('+90 days')),
                'cc_sponsor_timezone' => wp_timezone_string(),
                'castconductor_image_credit' => $sponsor_data['image_credit'],
                'cc_sponsor_source' => 'setup_wizard'
            )
        ));
        
        // Set featured image if file exists
        if (!is_wp_error($post_id) && file_exists($sponsor_data['featured_image'])) {
            $attachment_id = $this->upload_wizard_image($sponsor_data['featured_image'], $post_id);
            if ($attachment_id) {
                set_post_thumbnail($post_id, $attachment_id);
            }
        }
        
        return array($post_id);
    }
    
    /**
     * Create sample promo with featured image
     */
    private function create_sample_promo() {
        $promo_data = array(
            'title' => 'Mixtape Mondays',
            'message' => 'Straight from the boombox. Is it live or is it Memorex? Mondays 8P - 12A',
            'featured_image' => CASTCONDUCTOR_PLUGIN_DIR . 'wizard-content/boombox-gordon-cowie-qQzw8jPvip8-unsplash.jpeg',
            'image_credit' => 'Photo by Gordon Cowie on Unsplash'
        );
        
        $post_id = wp_insert_post(array(
            'post_title' => $promo_data['title'],
            'post_content' => $promo_data['message'],
            'post_type' => 'cc_promo',
            'post_status' => 'publish',
            'meta_input' => array(
                'cc_promo_start_date' => date('Y-m-d H:i:s'),
                'cc_promo_end_date' => date('Y-m-d H:i:s', strtotime('+365 days')),
                'cc_promo_schedule' => 'recurring_weekly',
                'cc_promo_timezone' => wp_timezone_string(),
                'castconductor_image_credit' => $promo_data['image_credit'],
                'cc_promo_source' => 'setup_wizard'
            )
        ));
        
        // Set featured image if file exists
        if (!is_wp_error($post_id) && file_exists($promo_data['featured_image'])) {
            $attachment_id = $this->upload_wizard_image($promo_data['featured_image'], $post_id);
            if ($attachment_id) {
                set_post_thumbnail($post_id, $attachment_id);
            }
        }
        
        return array($post_id);
    }
    
    /**
     * Setup default branding elements
     */
    private function setup_default_branding() {
        $branding_files = array(
            'square_logo_600x600' => 'default-square-logo-600x600.png',
            'square_logo_bg' => 'default-square-logo-600x600-bg.jpg',
            'animated_center_logo_1280x250' => 'default-center-logo-1280x250.png',
            'background_1920x1080' => 'default-background-1920x1080.jpeg',
            'shoutout_fallback_artwork' => 'default-square-logo-600x600-bg.jpg'
        );
        
        $uploaded_branding = array();
        
        foreach ($branding_files as $key => $filename) {
            $file_path = CASTCONDUCTOR_PLUGIN_DIR . 'wizard-content/' . $filename;
            
            if (file_exists($file_path)) {
                $attachment_id = $this->upload_wizard_image($file_path, 0);
                if ($attachment_id) {
                    update_option("castconductor_default_{$key}", $attachment_id);
                    update_option("castconductor_current_{$key}", $attachment_id);
                    $uploaded_branding[$key] = $attachment_id;
                }
            }
        }
        
        // Configure default branding settings
        update_option('castconductor_branding_config', array(
            'square_logo_enabled' => true,
            'square_logo_fallback_mode' => 'when_no_album_artwork',
            'square_logo_scale' => 1.0,
            'square_logo_position' => array('x' => 60, 'y' => 740),
            'animated_center_logo_enabled' => true,
            'animated_center_logo_duration' => 5,
            'animated_center_logo_scale' => 1.0,
            'animated_center_logo_position' => array('x' => 640, 'y' => 415),
            'background_enabled' => true,
            'background_opacity' => 0.3,
            'background_scale' => 1.0,
            'branding_attribution' => 'Powered by CastConductor'
        ));
        
        return $uploaded_branding;
    }
    
    /**
     * Import misc-elements images and resolve {{wizard:...}} placeholders in content blocks
     */
    private function import_misc_elements_and_resolve_placeholders() {
        global $wpdb;
        
        $results = array(
            'images_imported' => array(),
            'blocks_updated' => array(),
            'errors' => array()
        );
        
        // Step 1: Import misc-elements images to media library
        $misc_elements_dir = CASTCONDUCTOR_PLUGIN_DIR . 'wizard-content/misc-elements/';
        $url_map = array(); // Maps relative path to media library URL
        
        if (is_dir($misc_elements_dir)) {
            $files = glob($misc_elements_dir . '*.{png,jpg,jpeg,gif,webp}', GLOB_BRACE);
            foreach ($files as $file_path) {
                $filename = basename($file_path);
                $relative_key = 'misc-elements/' . $filename;
                
                $attachment_id = $this->upload_wizard_image($file_path, 0);
                if ($attachment_id) {
                    $url = wp_get_attachment_url($attachment_id);
                    $url_map[$relative_key] = $url;
                    $results['images_imported'][$filename] = $attachment_id;
                } else {
                    $results['errors'][] = "Failed to import: {$filename}";
                }
            }
        }
        
        if (empty($url_map)) {
            error_log('[CC Wizard] No misc-elements images found or all imports failed');
            return $results;
        }
        
        // Step 2: Find content blocks with {{wizard:...}} placeholders and resolve them
        $table_name = $wpdb->prefix . 'castconductor_content_blocks';
        $blocks = $wpdb->get_results("SELECT id, type, visual_config FROM {$table_name} WHERE visual_config LIKE '%{{wizard:%'");
        
        foreach ($blocks as $block) {
            $visual_config = $block->visual_config;
            $updated = false;
            
            // Replace all {{wizard:...}} placeholders with actual URLs
            // Handle both escaped (JSON) and unescaped versions of the path
            foreach ($url_map as $relative_path => $url) {
                // Unescaped version (e.g., misc-elements/drop-shadow4.png)
                $placeholder = '{{wizard:' . $relative_path . '}}';
                if (strpos($visual_config, $placeholder) !== false) {
                    $visual_config = str_replace($placeholder, $url, $visual_config);
                    $updated = true;
                }
                
                // Escaped version for JSON (e.g., misc-elements\/drop-shadow4.png)
                $escaped_path = str_replace('/', '\\/', $relative_path);
                $escaped_placeholder = '{{wizard:' . $escaped_path . '}}';
                if (strpos($visual_config, $escaped_placeholder) !== false) {
                    $visual_config = str_replace($escaped_placeholder, $url, $visual_config);
                    $updated = true;
                }
            }
            
            if ($updated) {
                $wpdb->update(
                    $table_name,
                    array('visual_config' => $visual_config),
                    array('id' => $block->id),
                    array('%s'),
                    array('%d')
                );
                $results['blocks_updated'][] = $block->type;
                error_log("[CC Wizard] Resolved placeholders in content block: {$block->type} (ID {$block->id})");
            }
        }
        
        error_log('[CC Wizard] Misc-elements import complete: ' . count($results['images_imported']) . ' images, ' . count($results['blocks_updated']) . ' blocks updated');
        
        return $results;
    }
    
    /**
     * Configure real API endpoints (no hardcoded URLs)
     */
    private function configure_real_apis() {
        // Set up URL configuration following V5 architecture
        // No hardcoded URLs - will be configured via Toaster or manual setup
        
        $api_config = array(
            'stream_url_configured' => false,
            'metadata_url_configured' => false,
            'weather_api_configured' => false,
            'geolocation_enabled' => true,
            'artwork_search_enabled' => true
        );
        
        // Enable IP-based geolocation (no API key required)
        update_option('castconductor_geolocation_enabled', true);
        update_option('castconductor_geolocation_service', 'ipinfo');
        
        // Enable album artwork discovery (no API key required for iTunes/MusicBrainz)
        update_option('castconductor_artwork_search_enabled', true);
        update_option('castconductor_artwork_apis', array('itunes', 'musicbrainz', 'deezer'));
        update_option('castconductor_artwork_cache_duration', 3600);
        update_option('castconductor_artwork_search_timeout', 10);
        update_option('castconductor_artwork_max_resolution', '1280x720');
        update_option('castconductor_artwork_resize_method', 'smart_crop');
        
        // Only set URL placeholders if not already configured (preserve existing values)
        if (empty(get_option('castconductor_stream_url', ''))) {
            update_option('castconductor_stream_url', '');
        }
        if (empty(get_option('castconductor_metadata_url', ''))) {
            update_option('castconductor_metadata_url', '');
        }
        if (empty(get_option('castconductor_openweather_api_key', ''))) {
            update_option('castconductor_openweather_api_key', '');
        }
        
        return $api_config;
    }
    
    // create_default_assignments() removed — obsolete and potentially conflicting with setup_database() defaults

    /**
     * Seed Upper Third Left/Right zone assignments with real blocks:
     * - upper_left: Location & Time (100%)
     * - upper_right: Weather (100%)
     * Skips gracefully if IDs are missing.
     */
    public function seed_upper_third_zone_assignments() {
        $upper_container_id = (int) get_option('castconductor_default_upper_third_id');
        $content_blocks = get_option('castconductor_default_content_blocks');

        if ($upper_container_id <= 0 || !is_array($content_blocks)) {
            return array('seeded' => false, 'reason' => 'Upper Third container or content blocks not found');
        }

        $loc_time_id = isset($content_blocks['location_time']) ? (int) $content_blocks['location_time'] : 0;
        $weather_id  = isset($content_blocks['weather']) ? (int) $content_blocks['weather'] : 0;

        if ($loc_time_id <= 0 && $weather_id <= 0) {
            return array('seeded' => false, 'reason' => 'Required blocks (location_time/weather) not available');
        }

        // Ensure both zones are active if layout.activeZoneIds is missing
        try {
            $getReq = new WP_REST_Request('GET', '/castconductor/v5/containers/' . $upper_container_id);
            $getRes = rest_do_request($getReq);
            if (!$getRes->is_error()) {
                $data = $getRes->get_data();
                $layout = (isset($data['layout']) && is_array($data['layout'])) ? $data['layout'] : array();
                $active = (isset($layout['activeZoneIds']) && is_array($layout['activeZoneIds'])) ? $layout['activeZoneIds'] : array();
                $want = array('upper_left', 'upper_right');
                $need = array_diff($want, $active);
                if (!empty($need)) {
                    $patch = new WP_REST_Request('PATCH', '/castconductor/v5/containers/' . $upper_container_id);
                    $patch->set_param('layout', array('activeZoneIds' => array_values(array_unique(array_merge($active, $want)))));
                    rest_do_request($patch);
                }
            }
        } catch (Exception $e) {
            // non-fatal
        }

        // Helper to PUT assignments into a zone with content_block_id; returns true on success
        $put_zone = function($zone, $block_id, $order = 1) use ($upper_container_id) {
            if ($block_id <= 0) return false;
            $req = new WP_REST_Request('PUT', '/castconductor/v5/containers/' . $upper_container_id . '/zones/' . $zone . '/assignments');
            $req->set_param('assignments', array(
                array(
                    'block_id'            => $block_id,
                    'rotation_percentage' => 100.0,
                    'rotation_order'      => $order,
                    'enabled'             => 1,
                )
            ));
            $res = rest_do_request($req);
            return !$res->is_error();
        };

        $seeded = array();
        $leftOk = ($loc_time_id > 0) ? $put_zone('upper_left', $loc_time_id, 1) : false;
        $rightOk = ($weather_id > 0) ? $put_zone('upper_right', $weather_id, 1) : false;
        if ($leftOk) $seeded[] = 'upper_left';
        if ($rightOk) $seeded[] = 'upper_right';

        // Fallback path: if REST seeding failed (common during activation when routes aren't registered), write options directly
        if (empty($seeded)) {
            $map_key = 'castconductor_container_zone_assignments_' . $upper_container_id;
            $map = array();
            if ($loc_time_id > 0) {
                $map['upper_left'] = array(array(
                    'block_id' => $loc_time_id,
                    'rotation_percentage' => 100.0,
                    'rotation_order' => 1,
                    'enabled' => 1
                ));
                $seeded[] = 'upper_left';
            }
            if ($weather_id > 0) {
                $map['upper_right'] = array(array(
                    'block_id' => $weather_id,
                    'rotation_percentage' => 100.0,
                    'rotation_order' => 1,
                    'enabled' => 1
                ));
                $seeded[] = 'upper_right';
            }
            if (!empty($map)) {
                update_option($map_key, wp_json_encode($map));
                // Ensure layout.activeZoneIds includes both
                $layout_key = 'castconductor_container_layout_' . $upper_container_id;
                $raw = get_option($layout_key, '{}');
                $layout = is_string($raw) ? json_decode($raw, true) : (is_array($raw) ? $raw : array());
                if (!is_array($layout)) $layout = array();
                $active = isset($layout['activeZoneIds']) && is_array($layout['activeZoneIds']) ? $layout['activeZoneIds'] : array();
                $want = array('upper_left','upper_right');
                $layout['activeZoneIds'] = array_values(array_unique(array_merge($active, $want)));
                update_option($layout_key, wp_json_encode($layout));
            }
        }

        return array('seeded' => !empty($seeded), 'container_id' => $upper_container_id, 'zones' => $seeded);
    }
    
    /**
     * Upload wizard image to WordPress media library
     */
    private function upload_wizard_image($file_path, $parent_post_id = 0) {
        $img_start = microtime(true);
        $filename = basename($file_path);
        
        if (!file_exists($file_path)) {
            error_log("[CC Wizard Image] {$filename}: file not found");
            return false;
        }
        
        require_once(ABSPATH . 'wp-admin/includes/file.php');
        require_once(ABSPATH . 'wp-admin/includes/media.php');
        require_once(ABSPATH . 'wp-admin/includes/image.php');
        
        // Check if file already exists in media library
        $existing = get_posts(array(
            'post_type' => 'attachment',
            'meta_query' => array(
                array(
                    'key' => '_wp_attached_file',
                    'value' => $filename,
                    'compare' => 'LIKE'
                )
            ),
            'posts_per_page' => 1
        ));
        
        if (!empty($existing)) {
            $elapsed = round((microtime(true) - $img_start) * 1000);
            error_log("[CC Wizard Image] {$filename}: SKIPPED (exists as ID {$existing[0]->ID}) - {$elapsed}ms");
            return $existing[0]->ID;
        }
        
        // Upload new file
        $upload_dir = wp_upload_dir();
        $new_file_path = $upload_dir['path'] . '/' . $filename;
        
        $copy_start = microtime(true);
        if (copy($file_path, $new_file_path)) {
            $copy_time = round((microtime(true) - $copy_start) * 1000);
            
            $filetype = wp_check_filetype($filename);
            $attachment = array(
                'guid' => $upload_dir['url'] . '/' . $filename,
                'post_mime_type' => $filetype['type'],
                'post_title' => preg_replace('/\.[^.]+$/', '', $filename),
                'post_content' => '',
                'post_status' => 'inherit'
            );
            
            $insert_start = microtime(true);
            $attachment_id = wp_insert_attachment($attachment, $new_file_path, $parent_post_id);
            $insert_time = round((microtime(true) - $insert_start) * 1000);
            
            if (!is_wp_error($attachment_id)) {
                // Force GD instead of ImageMagick for wizard image processing
                // ImageMagick has severe performance issues with PNGs in recent Docker images
                add_filter('wp_image_editors', function() {
                    return array('WP_Image_Editor_GD');
                }, 99);
                
                $meta_start = microtime(true);
                $attachment_data = wp_generate_attachment_metadata($attachment_id, $new_file_path);
                $meta_time = round((microtime(true) - $meta_start) * 1000);
                
                // Remove the filter after processing
                remove_all_filters('wp_image_editors', 99);
                
                wp_update_attachment_metadata($attachment_id, $attachment_data);
                
                $total_time = round((microtime(true) - $img_start) * 1000);
                error_log("[CC Wizard Image] {$filename}: UPLOADED (ID {$attachment_id}) - copy:{$copy_time}ms, insert:{$insert_time}ms, metadata:{$meta_time}ms, TOTAL:{$total_time}ms");
                return $attachment_id;
            }
        }
        
        $elapsed = round((microtime(true) - $img_start) * 1000);
        error_log("[CC Wizard Image] {$filename}: FAILED - {$elapsed}ms");
        return false;
    }
    
    /**
     * Store image attribution information
     */
    private function setup_demo_content_attribution() {
        update_option('castconductor_demo_image_credits', array(
            'cafe_image' => 'Photo by Cris Ferrero on Pexels',
            'boombox_image' => 'Photo by Gordon Cowie on Unsplash'
        ));
    }

    /**
     * Create a default Scene with global branding/background and standard containers.
     * Idempotent: if a scene exists and is active, no duplicate is created.
     */
    private function create_default_scene_with_containers() {
        try {
            global $wpdb;
            $db = new CastConductor_Database();
            $scenes_table = $db->get_table_name('scenes');
            
            // Check if an active scene already exists (from option OR database)
            $existing = get_option('castconductor_active_scene_id');
            $scene_id = !empty($existing) ? (int) $existing : 0;
            
            // If no scene ID from option, check database for any existing scene
            if ($scene_id <= 0) {
                // Look for "Default Scene" first, then any scene
                $scene_id = (int) $wpdb->get_var($wpdb->prepare(
                    "SELECT id FROM {$scenes_table} WHERE name = %s ORDER BY id ASC LIMIT 1",
                    'Default Scene'
                ));
                if ($scene_id <= 0) {
                    $scene_id = (int) $wpdb->get_var("SELECT id FROM {$scenes_table} ORDER BY id ASC LIMIT 1");
                }
            }
            
            $scene_exists = ($scene_id > 0);
            
            // If scene exists, check if it already has container assignments
            if ($scene_exists) {
                $scene_containers_table = $db->get_table_name('scene_containers');
                $has_containers = $wpdb->get_var($wpdb->prepare(
                    "SELECT COUNT(*) FROM {$scene_containers_table} WHERE scene_id = %d",
                    $scene_id
                ));
                
                // If scene already has containers assigned, just ensure branding/background
                if ($has_containers > 0) {
                    $this->maybe_apply_branding_background_to_scene($scene_id);
                    error_log(sprintf('[CC Wizard] Scene %d already has %d containers assigned, skipping seeding', $scene_id, $has_containers));
                    return array('created' => false, 'reason' => 'Active scene already has containers', 'scene_id' => $scene_id, 'container_count' => $has_containers);
                }
                
                // Scene exists but has no containers - we'll seed them below
                error_log(sprintf('[CC Wizard] Scene %d exists but has no containers, will seed assignments', $scene_id));
                $this->maybe_apply_branding_background_to_scene($scene_id);
            }

            // Build default scene payload with proper branding configuration
            $scene_name = 'Default Scene';
            
            // Branding config - use horizontal logo with proper positioning
            $branding = array(
                'enabled' => true,
                'logo_scale' => 60,
                'logo_position' => 'zone-17',
                'logo_animation' => 'float-pulse',
                'logo_animation_speed' => 50
            );
            // Pull current default horizontal logo if available (center logo 1280x250)
            $logo_id = (int) get_option('castconductor_current_animated_center_logo_1280x250');
            if ($logo_id) {
                $logo_url = wp_get_attachment_image_url($logo_id, 'full');
                if ($logo_url) {
                    $branding['logo'] = array('src' => $logo_url);
                    // Get logo dimensions for proper scaling
                    $meta = wp_get_attachment_metadata($logo_id);
                    if ($meta && isset($meta['width']) && isset($meta['height'])) {
                        $branding['logo_width'] = (int) $meta['width'];
                        $branding['logo_height'] = (int) $meta['height'];
                    }
                }
            }
            
            // Background from default background image
            $bg_id = (int) get_option('castconductor_current_background_1920x1080');
            $background = array();
            if ($bg_id) {
                $bg_url = wp_get_attachment_image_url($bg_id, 'full');
                if ($bg_url) {
                    $background = array('type' => 'image', 'sources' => array($bg_url), 'fit' => 'cover');
                }
            }

            // Create scene via REST to ensure consistent storage format if controller exists
            if (class_exists('WP_REST_Request')) {
                $req = new WP_REST_Request('POST', '/castconductor/v5/scenes');
                $req->set_param('name', $scene_name);
                $req->set_param('branding', $branding);
                $req->set_param('background', $background);
                $res = rest_do_request($req);
                if ($res->is_error()) throw new Exception('Failed to create default scene');
                $scene = $res->get_data();
                // Response is wrapped: {success: true, data: {id: X}, message: '...'}
                $scene_id = (int) ($scene['data']['id'] ?? $scene['id'] ?? 0);
                if ($scene_id <= 0) throw new Exception('No scene id');
                // Activate it
                $activate = new WP_REST_Request('POST', '/castconductor/v5/scenes/' . $scene_id . '/activate');
                rest_do_request($activate);
                // Ensure branding/background present
                $this->maybe_apply_branding_background_to_scene($scene_id, $branding, $background);
                
                error_log(sprintf('[CC Wizard] Scene %d created successfully, now seeding container assignments', $scene_id));

                // Add standard containers with seeded content block assignments
                // CRITICAL FIX: Look up container IDs AND content block IDs before assigning to scene
                global $wpdb;
                $db = new CastConductor_Database();
                $containers_table = $db->get_table_name('containers');
                $blocks_table = $db->get_table_name('content_blocks');
                
                // Lookup content block IDs by type
                $get_block_id = function($type) use ($wpdb, $blocks_table) {
                    return $wpdb->get_var($wpdb->prepare("SELECT id FROM {$blocks_table} WHERE type = %s ORDER BY id ASC LIMIT 1", $type));
                };
                
                $track_info_id = $get_block_id('track_info');
                $track_info_hero_id = $get_block_id('track_info_hero');
                $shoutouts_id = $get_block_id('shoutout');
                $promos_id = $get_block_id('promo');
                $sponsors_id = $get_block_id('sponsor');
                $location_time_id = $get_block_id('location_time');
                $weather_id = $get_block_id('weather');
                
                error_log(sprintf('[CC Wizard] Content block IDs: track_info=%d, track_info_hero=%d, shoutouts=%d, promos=%d, sponsors=%d, location=%d, weather=%d',
                    $track_info_id, $track_info_hero_id, $shoutouts_id, $promos_id, $sponsors_id, $location_time_id, $weather_id));
                
                $container_positions = array(
                    // Featured Hero - Upper 2/3 of screen with Track Info Hero
                    array(
                        'position' => 'featured_grid_hero',
                        'overrides' => array(
                            'rect' => array('x' => 0, 'y' => 0, 'width' => 1280, 'height' => 480),
                            'layout' => array('zones' => array(array('id' => 'default', 'name' => 'default', 'order' => 1)))
                        ),
                        'zones' => array(
                            'default' => array(
                                'assignments' => array_filter(array(
                                    $track_info_hero_id ? array('block_id' => (int)$track_info_hero_id, 'weight' => 100, 'interval' => 15, 'order' => 1) : null
                                ))
                            )
                        ),
                        'enabled' => 1
                    ),
                    // Lower Third - Promos, Shoutouts, Sponsors (rotating)
                    array(
                        'position' => 'lower_third',
                        'overrides' => array(
                            'rect' => array('x' => 0, 'y' => 480, 'width' => 1280, 'height' => 240),
                            'layout' => array('zones' => array(array('id' => 'default', 'name' => 'default', 'order' => 1)))
                        ),
                        'zones' => array(
                            'default' => array(
                                'assignments' => array_filter(array(
                                    $promos_id ? array('block_id' => (int)$promos_id, 'weight' => 33, 'interval' => 15, 'order' => 1) : null,
                                    $shoutouts_id ? array('block_id' => (int)$shoutouts_id, 'weight' => 33, 'interval' => 15, 'order' => 2) : null,
                                    $sponsors_id ? array('block_id' => (int)$sponsors_id, 'weight' => 34, 'interval' => 15, 'order' => 3) : null
                                ))
                            )
                        ),
                        'enabled' => 1
                    ),
                    // Upper Third - disabled by default (user can enable for location/weather)
                    array(
                        'position' => 'upper_third',
                        'overrides' => array(
                            'rect' => array('x' => 0, 'y' => 0, 'width' => 1280, 'height' => 240),
                            'layout' => array(
                                'zones' => array(
                                    array('id' => 'upper_left', 'name' => 'Upper Left', 'order' => 1, 'rect' => array('x' => 0, 'y' => 0, 'w' => 640, 'h' => 240)),
                                    array('id' => 'upper_right', 'name' => 'Upper Right', 'order' => 2, 'rect' => array('x' => 640, 'y' => 0, 'w' => 640, 'h' => 240))
                                )
                            )
                        ),
                        'zones' => array(
                            'upper_left' => array(
                                'assignments' => $location_time_id ? array(array('block_id' => (int)$location_time_id, 'weight' => 100, 'interval' => 60, 'order' => 1)) : array()
                            ),
                            'upper_right' => array(
                                'assignments' => $weather_id ? array(array('block_id' => (int)$weather_id, 'weight' => 100, 'interval' => 60, 'order' => 1)) : array()
                            )
                        ),
                        'enabled' => 0
                    ),
                    array('position' => 'left_half', 'overrides' => array('rect' => array('x' => 0, 'y' => 0, 'width' => 640, 'height' => 720)), 'enabled' => 0),
                    array('position' => 'right_half', 'overrides' => array('rect' => array('x' => 640, 'y' => 0, 'width' => 640, 'height' => 720)), 'enabled' => 0),
                    array('position' => 'top_left_quarter', 'overrides' => array('rect' => array('x' => 0, 'y' => 0, 'width' => 640, 'height' => 360)), 'enabled' => 0),
                    array('position' => 'top_right_quarter', 'overrides' => array('rect' => array('x' => 640, 'y' => 0, 'width' => 640, 'height' => 360)), 'enabled' => 0),
                    array('position' => 'bottom_left_quarter', 'overrides' => array('rect' => array('x' => 0, 'y' => 360, 'width' => 640, 'height' => 360)), 'enabled' => 0),
                    array('position' => 'bottom_right_quarter', 'overrides' => array('rect' => array('x' => 640, 'y' => 360, 'width' => 640, 'height' => 360)), 'enabled' => 0),
                );
                
                $containers = array();
                foreach ($container_positions as $spec) {
                    // CRITICAL: Only add containers that are enabled (enabled => 1)
                    // Disabled containers (upper_third, half_screen, quarters) should NOT appear in scene
                    if (!isset($spec['enabled']) || $spec['enabled'] !== 1) {
                        continue;
                    }
                    
                    $container_id = $wpdb->get_var($wpdb->prepare(
                        "SELECT id FROM {$containers_table} WHERE position = %s ORDER BY id ASC LIMIT 1",
                        $spec['position']
                    ));
                    if ($container_id) {
                        $container_data = array(
                            'container_id' => (int) $container_id,
                            'overrides' => $spec['overrides'],
                            'enabled' => $spec['enabled']
                        );
                        // Add zones if present and save to wp_options for admin UI
                        if (isset($spec['zones'])) {
                            $container_data['zones'] = $spec['zones'];
                            
                            // CRITICAL: Also write to wp_options for WordPress admin UI
                            // The admin UI reads from these keys while scene-container table is used by Roku endpoint
                            
                            // 1. Save zone assignments (which blocks are in which zones)
                            $zone_key = 'castconductor_container_zone_assignments_' . intval($container_id);
                            update_option($zone_key, wp_json_encode($spec['zones']));
                            error_log(sprintf('[CC Wizard] Saved zone assignments to wp_options key: %s', $zone_key));
                            
                            // 2. Save layout (zone definitions/geometry) if present in overrides
                            if (isset($spec['overrides']['layout'])) {
                                $layout_key = 'castconductor_container_layout_' . intval($container_id);
                                update_option($layout_key, wp_json_encode($spec['overrides']['layout']));
                                error_log(sprintf('[CC Wizard] Saved layout to wp_options key: %s', $layout_key));
                            }
                        }
                        $containers[] = $container_data;
                        error_log(sprintf('[CC Wizard] Assigning container %d (%s) to scene %d with zones', $container_id, $spec['position'], $scene_id));
                    }
                }

                if (!empty($containers)) {
                    error_log(sprintf('[CC Wizard] Assigning %d containers to scene %d: %s', count($containers), $scene_id, json_encode($containers)));
                    $put = new WP_REST_Request('PUT', '/castconductor/v5/scenes/' . $scene_id . '/containers');
                    $put->set_param('containers', $containers);
                    $result = rest_do_request($put);
                    if ($result->is_error()) {
                        error_log(sprintf('[CC Wizard] ERROR assigning containers: %s', $result->get_error_message()));
                    } else {
                        error_log('[CC Wizard] Successfully assigned containers to scene');
                    }
                } else {
                    error_log('[CC Wizard] WARNING: No containers found to assign to scene');
                }

                update_option('castconductor_active_scene_id', $scene_id);
                return array('created' => true, 'scene_id' => $scene_id);
            }

            return array('created' => false, 'reason' => 'REST not available');
        } catch (Exception $e) {
            error_log(sprintf('[CC Wizard] EXCEPTION in create_default_scene_with_containers: %s at %s:%d', $e->getMessage(), $e->getFile(), $e->getLine()));
            error_log(sprintf('[CC Wizard] Stack trace: %s', $e->getTraceAsString()));
            return array('created' => false, 'error' => $e->getMessage());
        }
    }

    /**
     * Apply branding/background to a scene if missing; uses provided arrays or pulls from current options.
     */
    private function maybe_apply_branding_background_to_scene($scene_id, $branding = null, $background = null) {
        try {
            if (!$scene_id) return;
            if (!is_array($branding)) {
                $branding = array();
                // Use horizontal center logo (1280x250) for default scene branding
                $logo_id = (int) get_option('castconductor_current_animated_center_logo_1280x250');
                if ($logo_id) {
                    $logo_url = wp_get_attachment_image_url($logo_id, 'full');
                    if ($logo_url) $branding['logo'] = array('src' => $logo_url);
                }
            }
            if (!is_array($background)) {
                $background = array();
                $bg_id = (int) get_option('castconductor_current_background_1920x1080');
                if ($bg_id) {
                    $bg_url = wp_get_attachment_image_url($bg_id, 'full');
                    if ($bg_url) $background = array('type' => 'image', 'sources' => array($bg_url), 'fit' => 'cover');
                }
            }
            if (empty($branding) && empty($background)) return;
            // Fetch current scene
            $get = new WP_REST_Request('GET', '/castconductor/v5/scenes/' . $scene_id);
            $res = rest_do_request($get);
            if ($res->is_error()) return;
            $row = $res->get_data();
            $curBrand = isset($row['branding']) ? json_decode($row['branding'], true) : array();
            $curBg = isset($row['background']) ? json_decode($row['background'], true) : array();
            $payload = array();
            if (empty($curBrand) && !empty($branding)) $payload['branding'] = $branding;
            if (empty($curBg) && !empty($background)) $payload['background'] = $background;
            if (empty($payload)) return;
            $upd = new WP_REST_Request('POST', '/castconductor/v5/scenes/' . $scene_id);
            $upd->set_body_params($payload);
            rest_do_request($upd);
        } catch (Exception $e) {
            // non-fatal
        }
    }
}
