<?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.
 * 
 * END OF HEADER
 */

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

class CastConductor_Scenes_Controller extends WP_REST_Controller {
    protected $namespace = 'castconductor/v5';
    protected $rest_base = 'scenes';

    public function register_routes() {
        // CRUD scenes
        register_rest_route($this->namespace, '/' . $this->rest_base, [
            [
                'methods' => WP_REST_Server::READABLE,
                'callback' => [$this, 'get_items'],
                'permission_callback' => [$this, 'permission_admin']
            ],
            [
                'methods' => WP_REST_Server::CREATABLE,
                'callback' => [$this, 'create_item'],
                'permission_callback' => [$this, 'permission_admin']
            ]
        ]);

        // Get active scene (id, name, branding, background)
        register_rest_route($this->namespace, '/' . $this->rest_base . '/active', [
            [
                'methods' => WP_REST_Server::READABLE,
                'callback' => [$this, 'get_active_scene'],
                'permission_callback' => [$this, 'permission_admin']
            ]
        ]);

        register_rest_route($this->namespace, '/' . $this->rest_base . '/(?P<id>[\d]+)', [
            [
                'methods' => WP_REST_Server::READABLE,
                'callback' => [$this, 'get_item'],
                'permission_callback' => [$this, 'permission_admin']
            ],
            [
                'methods' => WP_REST_Server::EDITABLE,
                'callback' => [$this, 'update_item'],
                'permission_callback' => [$this, 'permission_admin']
            ],
            [
                'methods' => WP_REST_Server::DELETABLE,
                'callback' => [$this, 'delete_item'],
                'permission_callback' => [$this, 'permission_admin']
            ]
        ]);

        // Activate a scene
        register_rest_route($this->namespace, '/' . $this->rest_base . '/(?P<id>[\d]+)/activate', [
            [
                'methods' => WP_REST_Server::CREATABLE,
                'callback' => [$this, 'activate_scene'],
                'permission_callback' => [$this, 'permission_admin']
            ]
        ]);

        // Container overrides for a scene
        register_rest_route($this->namespace, '/' . $this->rest_base . '/(?P<id>[\d]+)/containers', [
            [
                'methods' => WP_REST_Server::READABLE,
                'callback' => [$this, 'get_scene_containers'],
                'permission_callback' => [$this, 'permission_admin']
            ],
            [
                'methods' => WP_REST_Server::EDITABLE,
                'callback' => [$this, 'put_scene_containers'],
                'permission_callback' => [$this, 'permission_admin']
            ]
        ]);
        
        // Clear all containers for a scene
        register_rest_route($this->namespace, '/' . $this->rest_base . '/(?P<id>[\d]+)/containers/clear', [
            [
                'methods' => WP_REST_Server::CREATABLE,
                'callback' => [$this, 'clear_scene_containers'],
                'permission_callback' => [$this, 'permission_admin']
            ]
        ]);
    }

    public function permission_admin() {
        return current_user_can('manage_options') || current_user_can('edit_posts');
    }

    public function get_items($request) {
        global $wpdb;
        $db = new CastConductor_Database();
        $table = $db->get_table_name('scenes');
        $rows = $wpdb->get_results("SELECT * FROM {$table} ORDER BY is_active DESC, id ASC");
        return CastConductor_REST_API::format_response($rows);
    }

    public function create_item($request) {
        global $wpdb;
        $db = new CastConductor_Database();
        $table = $db->get_table_name('scenes');

        $name = sanitize_text_field($request->get_param('name'));
        $description = sanitize_textarea_field($request->get_param('description'));
        $branding = $request->get_param('branding');
        $background = $request->get_param('background');
        $metadata = $request->get_param('metadata');
        $schedule_days = $request->get_param('schedule_days');

        // Auto-generate name if empty
        if (!$name || trim($name) === '' || strtolower(trim($name)) === 'untitled scene') {
            $base_name = 'Scene';
            $count = (int) $wpdb->get_var("SELECT COUNT(*) FROM {$table}");
            $name = $base_name . ' ' . ($count + 1);
            // Ensure uniqueness
            $suffix = $count + 1;
            while ($wpdb->get_var($wpdb->prepare("SELECT id FROM {$table} WHERE name = %s LIMIT 1", $name))) {
                $suffix++;
                $name = $base_name . ' ' . $suffix;
            }
        } else {
            // Check for duplicate names - if exists, return existing scene (idempotent)
            $existing_id = $wpdb->get_var($wpdb->prepare("SELECT id FROM {$table} WHERE name = %s LIMIT 1", $name));
            if ($existing_id) {
                // Return existing scene with flag indicating it already existed
                return CastConductor_REST_API::format_response(
                    ['id' => (int) $existing_id, 'existing' => true], 
                    true, 
                    'A scene with the same name already exists.'
                );
            }
        }

        $wpdb->insert($table, [
            'name' => $name,
            'description' => $description,
            'is_active' => 0,
            'rotation_enabled' => (int) ($request->get_param('rotation_enabled') ? 1 : 0),
            'rotation_interval' => absint($request->get_param('rotation_interval') ?: 60),
            'schedule_enabled' => (int) ($request->get_param('schedule_enabled') ? 1 : 0),
            'schedule_start' => $request->get_param('schedule_start') ?: null,
            'schedule_end' => $request->get_param('schedule_end') ?: null,
            'schedule_timezone' => sanitize_text_field($request->get_param('schedule_timezone') ?: 'America/Los_Angeles'),
            'schedule_days' => $schedule_days ? wp_json_encode(is_array($schedule_days) ? $schedule_days : []) : null,
            'schedule_time_start' => $request->get_param('schedule_time_start') ?: null,
            'schedule_time_end' => $request->get_param('schedule_time_end') ?: null,
            'schedule_priority' => absint($request->get_param('schedule_priority') ?: 0),
            'branding' => wp_json_encode(is_array($branding) ? $branding : []),
            'background' => wp_json_encode(is_array($background) ? $background : []),
            'metadata' => wp_json_encode(is_array($metadata) ? $metadata : [])
        ]);

        $id = (int) $wpdb->insert_id;
        return CastConductor_REST_API::format_response(['id' => $id], true, 'Scene created');
    }

    public function get_item($request) {
        global $wpdb;
        $db = new CastConductor_Database();
        $table = $db->get_table_name('scenes');
        $id = absint($request['id']);
        $row = $wpdb->get_row($wpdb->prepare("SELECT * FROM {$table} WHERE id = %d", $id));
        if (!$row) {
            return CastConductor_REST_API::error_response('Scene not found', 'scene_not_found', 404);
        }
        return CastConductor_REST_API::format_response($row);
    }

    public function get_active_scene($request) {
        $db = new CastConductor_Database();
        $row = $db->get_active_scene();
        if (!$row) {
            $db->ensure_default_scene();
            $row = $db->get_active_scene();
        }
        if (!$row) {
            return CastConductor_REST_API::error_response('No active scene', 'no_active_scene', 404);
        }
        // If branding/background empty, try populate from current branding options
        $branding = is_string($row->branding) ? json_decode($row->branding, true) : (array) $row->branding;
        $background = is_string($row->background) ? json_decode($row->background, true) : (array) $row->background;
        if (empty($branding)) {
            // Prefer the horizontal/center logo for Scenes Stage branding
            $center_id = (int) get_option('castconductor_current_animated_center_logo_1280x250');
            $center_url = $center_id ? wp_get_attachment_image_url($center_id, 'full') : '';
            if ($center_url) {
                $branding = array('logo' => array('src' => $center_url));
            } else {
                // Fallback to square logo if center logo not set
                $logo_id = (int) get_option('castconductor_current_square_logo_600x600');
                $logo_url = $logo_id ? wp_get_attachment_image_url($logo_id, 'full') : '';
                if ($logo_url) { $branding = array('logo' => array('src' => $logo_url)); }
            }
        }
        if (empty($background)) {
            $bg_id = (int) get_option('castconductor_current_background_1920x1080');
            $bg_url = $bg_id ? wp_get_attachment_image_url($bg_id, 'full') : '';
            if ($bg_url) { $background = array('type' => 'image', 'sources' => array($bg_url), 'fit' => 'cover'); }
        }
        $row->branding = $branding;
        $row->background = $background;
        return CastConductor_REST_API::format_response($row);
    }

    public function update_item($request) {
        global $wpdb;
        $db = new CastConductor_Database();
        $table = $db->get_table_name('scenes');
        $id = absint($request['id']);

        $data = [];
        
        // Text fields
        if ($request->offsetExists('name')) {
            $new_name = sanitize_text_field($request->get_param('name'));
            // Check for duplicate names (excluding current scene)
            $existing = $wpdb->get_var($wpdb->prepare("SELECT id FROM {$table} WHERE name = %s AND id != %d LIMIT 1", $new_name, $id));
            if ($existing) {
                return CastConductor_REST_API::error_response('A scene with this name already exists. Please choose a unique name.', 'duplicate_scene_name', 400);
            }
            $data['name'] = $new_name;
        }
        if ($request->offsetExists('description')) {
            $data['description'] = sanitize_textarea_field($request->get_param('description'));
        }
        
        // Boolean fields
        foreach (['rotation_enabled', 'schedule_enabled'] as $boolField) {
            if ($request->offsetExists($boolField)) {
                $data[$boolField] = (int) ($request->get_param($boolField) ? 1 : 0);
            }
        }
        
        // Integer fields
        foreach (['rotation_interval', 'schedule_priority'] as $intField) {
            if ($request->offsetExists($intField)) {
                $data[$intField] = absint($request->get_param($intField));
            }
        }
        
        // Float fields (rotation_percentage)
        if ($request->offsetExists('rotation_percentage')) {
            $data['rotation_percentage'] = floatval($request->get_param('rotation_percentage'));
        }
        
        // Datetime fields (schedule_start, schedule_end)
        foreach (['schedule_start', 'schedule_end'] as $dtField) {
            if ($request->offsetExists($dtField)) {
                $val = $request->get_param($dtField);
                $data[$dtField] = $val ?: null;
            }
        }
        
        // Time fields (schedule_time_start, schedule_time_end)
        foreach (['schedule_time_start', 'schedule_time_end'] as $timeField) {
            if ($request->offsetExists($timeField)) {
                $val = $request->get_param($timeField);
                $data[$timeField] = $val ?: null;
            }
        }
        
        // Timezone
        if ($request->offsetExists('schedule_timezone')) {
            $data['schedule_timezone'] = sanitize_text_field($request->get_param('schedule_timezone') ?: 'America/Los_Angeles');
        }
        
        // JSON array field (schedule_days)
        if ($request->offsetExists('schedule_days')) {
            $val = $request->get_param('schedule_days');
            $data['schedule_days'] = $val ? wp_json_encode(is_array($val) ? $val : []) : null;
        }
        
        // JSON object fields (branding, background, metadata)
        foreach (['branding', 'background', 'metadata'] as $jsonF) {
            if ($request->offsetExists($jsonF)) {
                $val = $request->get_param($jsonF);
                $data[$jsonF] = wp_json_encode(is_array($val) ? $val : []);
            }
        }
        
        if (empty($data)) {
            return CastConductor_REST_API::error_response('No fields to update', 'no_update_fields', 400);
        }

        $updated = $wpdb->update($table, $data, ['id' => $id]);
        if ($updated === false) {
            return CastConductor_REST_API::error_response('Failed to update scene', 'scene_update_failed', 500);
        }
        return CastConductor_REST_API::format_response(['id' => $id], true, 'Scene updated');
    }

    public function delete_item($request) {
        global $wpdb;
        $db = new CastConductor_Database();
        $table = $db->get_table_name('scenes');
        $id = absint($request['id']);

        // Prevent deleting the only active scene
        $active = $wpdb->get_var("SELECT COUNT(*) FROM {$table} WHERE is_active = 1");
        $is_active = $wpdb->get_var($wpdb->prepare("SELECT is_active FROM {$table} WHERE id = %d", $id));
        if ((int)$is_active === 1 && (int)$active <= 1) {
            return CastConductor_REST_API::error_response('Cannot delete the only active scene', 'cannot_delete_active_scene', 400);
        }

        $deleted = $wpdb->delete($table, ['id' => $id]);
        if ($deleted === false) {
            return CastConductor_REST_API::error_response('Failed to delete scene', 'scene_delete_failed', 500);
        }
        return CastConductor_REST_API::format_response(['id' => $id], true, 'Scene deleted');
    }

    public function activate_scene($request) {
        global $wpdb;
        $db = new CastConductor_Database();
        $table = $db->get_table_name('scenes');
        $id = absint($request['id']);

        $exists = $wpdb->get_var($wpdb->prepare("SELECT COUNT(*) FROM {$table} WHERE id = %d", $id));
        if ((int)$exists === 0) {
            return CastConductor_REST_API::error_response('Scene not found', 'scene_not_found', 404);
        }

        // Activate selected scene exclusively
        $wpdb->query($wpdb->prepare("UPDATE {$table} SET is_active = CASE WHEN id = %d THEN 1 ELSE 0 END", $id));

        return CastConductor_REST_API::format_response(['id' => $id], true, 'Scene activated');
    }

    public function get_scene_containers($request) {
        global $wpdb;
        $db = new CastConductor_Database();
        $table = $db->get_table_name('scene_containers');
        $containers_table = $db->get_table_name('containers');
        $id = absint($request['id']);
        
        error_log(sprintf('[CC] GET scene_containers for scene_id=%d, table=%s', $id, $table));
        
        // Join with master containers to get names and position keys
        $rows = $wpdb->get_results($wpdb->prepare(
            "SELECT sc.*, c.name as container_name, c.position as container_key, c.width, c.height, c.x_position, c.y_position
             FROM {$table} sc
             LEFT JOIN {$containers_table} c ON sc.container_id = c.id
             WHERE sc.scene_id = %d",
            $id
        ));
        
        error_log(sprintf('[CC] Query returned %d rows', is_array($rows) ? count($rows) : 0));
        if ($wpdb->last_error) {
            error_log('[CC] DB Error: ' . $wpdb->last_error);
        }
        
        // Decode JSON columns and add computed fields
        foreach ($rows as &$row) {
            if (isset($row->overrides) && is_string($row->overrides)) {
                $row->overrides = json_decode($row->overrides, true);
            }
            if (isset($row->zones) && is_string($row->zones)) {
                $row->zones = json_decode($row->zones, true);
            }
            // Expose the key for frontend preset matching
            $row->key = $row->container_key ?? '';
            // Expose name for display
            $row->name = $row->container_name ?? '';
            // Build rect from master container if not in overrides
            if (empty($row->overrides['rect']) && isset($row->width)) {
                $row->master_rect = [
                    'x' => (int)$row->x_position,
                    'y' => (int)$row->y_position,
                    'width' => (int)$row->width,
                    'height' => (int)$row->height
                ];
            }
            error_log(sprintf('[CC] Row: container_id=%d, name=%s, zones=%s', $row->container_id ?? 'null', $row->name, json_encode($row->zones ?? null)));
        }
        
        error_log(sprintf('[CC] Returning %d scene containers', count($rows)));
        return CastConductor_REST_API::format_response($rows);
    }

    public function put_scene_containers($request) {
        global $wpdb;
        $db = new CastConductor_Database();
        $table = $db->get_table_name('scene_containers');
        $containers_table = $db->get_table_name('containers');
        $id = absint($request['id']);

        $payload = $request->get_param('containers');
        if (!is_array($payload)) {
            return CastConductor_REST_API::error_response('containers must be an array', 'invalid_payload', 400);
        }

        $errors = [];
        foreach ($payload as $row) {
            $container_id = isset($row['container_id']) ? absint($row['container_id']) : 0;
            $key = isset($row['key']) ? sanitize_text_field($row['key']) : '';
            $overrides = isset($row['overrides']) && is_array($row['overrides']) ? $row['overrides'] : [];
            $zones = isset($row['zones']) && is_array($row['zones']) ? $row['zones'] : [];
            $enabled = isset($row['enabled']) ? (int) (!!$row['enabled']) : 1;
            
            // If no container_id but we have a key and rect, create the master container
            if ($container_id <= 0 && !empty($key)) {
                // First check if a master container with this key already exists
                $existing_master = $wpdb->get_row($wpdb->prepare(
                    "SELECT id FROM {$containers_table} WHERE position = %s LIMIT 1",
                    $key
                ));
                
                if ($existing_master) {
                    $container_id = (int) $existing_master->id;
                    error_log(sprintf('[CC] Found existing master container %d for key "%s"', $container_id, $key));
                } else {
                    // Create new master container from preset
                    $rect = isset($overrides['rect']) ? $overrides['rect'] : [];
                    $name = isset($row['name']) ? sanitize_text_field($row['name']) : ucwords(str_replace('_', ' ', $key));
                    
                    $wpdb->insert($containers_table, [
                        'name' => $name,
                        'position' => $key,
                        'width' => isset($rect['width']) ? absint($rect['width']) : 1280,
                        'height' => isset($rect['height']) ? absint($rect['height']) : 720,
                        'x_position' => isset($rect['x']) ? absint($rect['x']) : 0,
                        'y_position' => isset($rect['y']) ? absint($rect['y']) : 0,
                        'z_index' => 100,
                        'rotation_enabled' => 1,
                        'rotation_interval' => 30,
                        'enabled' => 1
                    ]);
                    
                    $container_id = $wpdb->insert_id;
                    error_log(sprintf('[CC] Created new master container %d for key "%s" (%s)', $container_id, $key, $name));
                }
            }
            
            // Skip if still no valid container_id
            if ($container_id <= 0) { 
                error_log(sprintf('[CC] Skipping container - no valid container_id and no key provided'));
                continue; 
            }

            error_log(sprintf('[CC] Saving container %d to scene %d with zones: %s', $container_id, $id, json_encode($zones)));

            // Upsert via unique (scene_id, container_id)
            $existing_id = $wpdb->get_var($wpdb->prepare(
                "SELECT id FROM {$table} WHERE scene_id = %d AND container_id = %d",
                $id, $container_id
            ));
            
            $data = [
                'scene_id' => $id,
                'container_id' => $container_id,
                'overrides' => wp_json_encode($overrides),
                'zones' => wp_json_encode($zones),
                'enabled' => $enabled
            ];
            
            error_log(sprintf('[CC] Data to save: %s', json_encode($data)));
            
            $result = false;
            if ($existing_id) {
                error_log(sprintf('[CC] Updating existing row id=%d', $existing_id));
                $result = $wpdb->update($table, $data, ['id' => $existing_id]);
            } else {
                error_log('[CC] Inserting new row');
                $result = $wpdb->insert($table, $data);
            }
            
            if ($result === false) {
                $error_msg = sprintf('DB Error for container %d: %s', $container_id, $wpdb->last_error);
                error_log('[CC] ' . $error_msg);
                $errors[] = $error_msg;
            } else {
                error_log(sprintf('[CC] Save successful, affected rows: %d', $result));
            }
        }

        if (!empty($errors)) {
            return CastConductor_REST_API::error_response(
                'Database errors occurred: ' . implode('; ', $errors),
                'database_error',
                500
            );
        }

        return CastConductor_REST_API::format_response(['scene_id' => $id], true, 'Scene containers updated');
    }
    
    /**
     * Clear all containers for a scene.
     * Used when saving a new set of containers - first clear, then add.
     */
    public function clear_scene_containers($request) {
        global $wpdb;
        $db = new CastConductor_Database();
        $table = $db->get_table_name('scene_containers');
        $id = absint($request['id']);
        
        $deleted = $wpdb->delete($table, ['scene_id' => $id]);
        
        if ($deleted === false) {
            return CastConductor_REST_API::error_response(
                'Failed to clear scene containers: ' . $wpdb->last_error,
                'database_error',
                500
            );
        }
        
        return CastConductor_REST_API::format_response(
            ['scene_id' => $id, 'cleared' => $deleted], 
            true, 
            sprintf('Cleared %d containers from scene', $deleted)
        );
    }
}
