<?php
/**
 * Cast Conductor Proprietary License v5
 * SPDX-License-Identifier: LicenseRef-CastConductor-Proprietary-v5
 * 
 * Copyright (c) 2025 CastConductor.com. All Rights Reserved.
 * 
 * Metadata Proxy Controller
 * 
 * Proxies metadata requests to AzuraCast (or other streaming providers)
 * and enhances responses with album artwork discovery.
 * 
 * This provides centralized control, caching, and manual override capability
 * for artwork associations.
 */

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

class CastConductor_Metadata_Proxy_Controller extends WP_REST_Controller {
    
    /**
     * API namespace
     */
    protected $namespace = 'castconductor/v5';
    
    /**
     * API base route
     */
    protected $rest_base = 'metadata';
    
    /**
     * Cache duration for metadata (30 seconds - matches typical refresh interval)
     */
    private $cache_duration = 30;
    
    /**
     * Register routes
     */
    public function register_routes() {
        // Proxy metadata requests with artwork enhancement
        register_rest_route($this->namespace, '/' . $this->rest_base . '/proxy', [
            [
                'methods' => 'GET',
                'callback' => [$this, 'proxy_metadata'],
                'permission_callback' => '__return_true', // Public endpoint for Roku
                'args' => [
                    'url' => [
                        'required' => true,
                        'type' => 'string',
                        'description' => 'Metadata endpoint URL to proxy',
                        'validate_callback' => function($param) {
                            return filter_var($param, FILTER_VALIDATE_URL) !== false;
                        }
                    ],
                    'format' => [
                        'required' => false,
                        'type' => 'string',
                        'default' => 'azuracast',
                        'description' => 'Metadata format (azuracast, icecast, generic)',
                        'enum' => ['azuracast', 'icecast', 'generic']
                    ],
                    'enhance_artwork' => [
                        'required' => false,
                        'type' => 'boolean',
                        'default' => true,
                        'description' => 'Enable enhanced artwork discovery'
                    ]
                ]
            ]
        ]);
        
        // Set manual artwork override for specific track
        register_rest_route($this->namespace, '/' . $this->rest_base . '/artwork-override', [
            [
                'methods' => 'POST',
                'callback' => [$this, 'set_artwork_override'],
                'permission_callback' => [$this, 'admin_permissions_check'],
                'args' => [
                    'artist' => [
                        'required' => true,
                        'type' => 'string'
                    ],
                    'title' => [
                        'required' => true,
                        'type' => 'string'
                    ],
                    'artwork_url' => [
                        'required' => true,
                        'type' => 'string',
                        'validate_callback' => function($param) {
                            return filter_var($param, FILTER_VALIDATE_URL) !== false;
                        }
                    ]
                ]
            ]
        ]);
        
        // Get artwork override for specific track
        register_rest_route($this->namespace, '/' . $this->rest_base . '/artwork-override', [
            [
                'methods' => 'GET',
                'callback' => [$this, 'get_artwork_override'],
                'permission_callback' => '__return_true',
                'args' => [
                    'artist' => [
                        'required' => true,
                        'type' => 'string'
                    ],
                    'title' => [
                        'required' => true,
                        'type' => 'string'
                    ]
                ]
            ]
        ]);
        
        // Delete artwork override
        register_rest_route($this->namespace, '/' . $this->rest_base . '/artwork-override', [
            [
                'methods' => 'DELETE',
                'callback' => [$this, 'delete_artwork_override'],
                'permission_callback' => [$this, 'admin_permissions_check'],
                'args' => [
                    'artist' => [
                        'required' => true,
                        'type' => 'string'
                    ],
                    'title' => [
                        'required' => true,
                        'type' => 'string'
                    ]
                ]
            ]
        ]);
        
        // List all artwork overrides
        register_rest_route($this->namespace, '/' . $this->rest_base . '/artwork-overrides', [
            [
                'methods' => 'GET',
                'callback' => [$this, 'list_artwork_overrides'],
                'permission_callback' => [$this, 'admin_permissions_check']
            ]
        ]);
        
        // External API proxy - allows Roku to fetch external APIs through WordPress with caching
        register_rest_route($this->namespace, '/external-api/proxy', [
            [
                'methods' => 'GET',
                'callback' => [$this, 'proxy_external_api'],
                'permission_callback' => '__return_true', // Public endpoint for Roku
                'args' => [
                    'url' => [
                        'required' => true,
                        'type' => 'string',
                        'description' => 'External API URL to proxy',
                        'validate_callback' => function($param) {
                            return filter_var($param, FILTER_VALIDATE_URL) !== false;
                        }
                    ],
                    'cache' => [
                        'required' => false,
                        'type' => 'integer',
                        'default' => 300,
                        'description' => 'Cache duration in seconds (default 5 min)'
                    ],
                    'path' => [
                        'required' => false,
                        'type' => 'string',
                        'default' => '',
                        'description' => 'JSON path to extract from response (e.g., "data.items")'
                    ],
                    'primary' => [
                        'required' => false,
                        'type' => 'string',
                        'description' => 'Field name for primary text'
                    ],
                    'secondary' => [
                        'required' => false,
                        'type' => 'string',
                        'description' => 'Field name for secondary text'
                    ],
                    'image' => [
                        'required' => false,
                        'type' => 'string',
                        'description' => 'Field name for image URL'
                    ],
                    'date' => [
                        'required' => false,
                        'type' => 'string',
                        'description' => 'Field name for date value'
                    ]
                ]
            ]
        ]);
    }
    
    /**
     * Proxy metadata request with artwork enhancement
     */
    public function proxy_metadata($request) {
        $url = $request->get_param('url');
        $format = $request->get_param('format');
        $enhance_artwork = $request->get_param('enhance_artwork');
        
        // Create cache key
        $cache_key = 'cc_metadata_proxy_' . md5($url);
        $cached = get_transient($cache_key);
        
        if ($cached !== false) {
            return rest_ensure_response($cached);
        }
        
        // Fetch metadata from origin
        $response = wp_remote_get($url, [
            'timeout' => 10,
            'headers' => [
                'User-Agent' => 'CastConductor/5.0 WordPress Proxy'
            ]
        ]);
        
        if (is_wp_error($response)) {
            return new WP_Error(
                'proxy_failed',
                'Failed to fetch metadata: ' . $response->get_error_message(),
                ['status' => 502]
            );
        }
        
        $http_code = wp_remote_retrieve_response_code($response);
        if ($http_code !== 200) {
            return new WP_Error(
                'upstream_error',
                'Upstream metadata service returned HTTP ' . $http_code,
                ['status' => $http_code]
            );
        }
        
        $body = wp_remote_retrieve_body($response);
        $data = json_decode($body, true);
        
        if (!$data) {
            return new WP_Error(
                'invalid_json',
                'Upstream service returned invalid JSON',
                ['status' => 502]
            );
        }
        
        // Parse based on format
        $metadata = $this->parse_metadata($data, $format);
        
        if (!$metadata['success']) {
            return new WP_Error(
                'parse_failed',
                'Failed to parse metadata: ' . ($metadata['error'] ?? 'Unknown error'),
                ['status' => 500]
            );
        }
        
        // Enhance artwork if enabled
        if ($enhance_artwork && !empty($metadata['artist']) && !empty($metadata['title'])) {
            $enhanced_artwork = $this->get_enhanced_artwork(
                $metadata['artist'],
                $metadata['album'] ?? '',
                $metadata['title']
            );
            
            if ($enhanced_artwork) {
                $metadata['artwork_url'] = $enhanced_artwork;
                $metadata['artwork_source'] = 'enhanced';
            } else if (!empty($metadata['artwork_url'])) {
                $metadata['artwork_source'] = 'origin';
            }
        }
        
        // Resize oversized artwork for Roku compatibility (Build 32)
        if (!empty($metadata['artwork_url'])) {
            $metadata['artwork_url'] = $this->maybe_resize_artwork($metadata['artwork_url']);
        }
        
        // Wrap in AzuraCast format if requested
        if ($format === 'azuracast') {
            $metadata = $this->wrap_azuracast_format($metadata);
        }
        
        // Cache result
        set_transient($cache_key, $metadata, $this->cache_duration);
        
        return rest_ensure_response($metadata);
    }
    
    /**
     * Parse metadata based on format
     */
    private function parse_metadata($data, $format) {
        switch ($format) {
            case 'azuracast':
                return $this->parse_azuracast($data);
            
            case 'icecast':
                return $this->parse_icecast($data);
            
            case 'generic':
            default:
                return $this->parse_generic($data);
        }
    }
    
    /**
     * Parse AzuraCast format
     */
    private function parse_azuracast($data) {
        if (!isset($data['now_playing']['song'])) {
            return [
                'success' => false,
                'error' => 'Missing now_playing.song structure'
            ];
        }
        
        $song = $data['now_playing']['song'];
        
        return [
            'success' => true,
            'artist' => $song['artist'] ?? 'Unknown Artist',
            'title' => $song['title'] ?? 'Unknown Title',
            'album' => $song['album'] ?? '',
            'artwork_url' => $song['art'] ?? '',
            'elapsed' => $data['now_playing']['elapsed'] ?? 0,
            'duration' => $data['now_playing']['duration'] ?? 0,
            'is_live' => $data['live']['is_live'] ?? false,
            'listeners' => $data['listeners']['current'] ?? 0,
            'station_name' => $data['station']['name'] ?? ''
        ];
    }
    
    /**
     * Parse Icecast format
     */
    private function parse_icecast($data) {
        // Icecast typically returns simpler structure
        return [
            'success' => true,
            'artist' => $data['artist'] ?? 'Unknown Artist',
            'title' => $data['title'] ?? 'Unknown Title',
            'album' => $data['album'] ?? '',
            'artwork_url' => $data['artwork'] ?? '',
            'listeners' => $data['listeners'] ?? 0
        ];
    }
    
    /**
     * Parse generic format
     */
    private function parse_generic($data) {
        if (empty($data['artist']) && empty($data['title'])) {
            return [
                'success' => false,
                'error' => 'Missing artist or title fields'
            ];
        }
        
        return [
            'success' => true,
            'artist' => $data['artist'] ?? 'Unknown Artist',
            'title' => $data['title'] ?? 'Unknown Title',
            'album' => $data['album'] ?? '',
            'artwork_url' => $data['artwork_url'] ?? $data['artwork'] ?? $data['image'] ?? ''
        ];
    }
    
    /**
     * Wrap parsed metadata in AzuraCast format
     */
    private function wrap_azuracast_format($metadata) {
        return [
            'station' => [
                'name' => $metadata['station_name'] ?? ''
            ],
            'listeners' => [
                'current' => $metadata['listeners'] ?? 0
            ],
            'live' => [
                'is_live' => $metadata['is_live'] ?? false
            ],
            'now_playing' => [
                'elapsed' => $metadata['elapsed'] ?? 0,
                'duration' => $metadata['duration'] ?? 0,
                'song' => [
                    'artist' => $metadata['artist'] ?? 'Unknown Artist',
                    'title' => $metadata['title'] ?? 'Unknown Title',
                    'album' => $metadata['album'] ?? '',
                    'art' => $metadata['artwork_url'] ?? ''
                ]
            ]
        ];
    }
    
    /**
     * Get enhanced artwork with manual override support
     */
    private function get_enhanced_artwork($artist, $album, $title) {
        // Check for manual override first (exact match)
        $override_key = $this->get_override_key($artist, $title);
        $override = get_option('cc_artwork_override_' . $override_key);
        
        if ($override && !empty($override['artwork_url'])) {
            error_log("CastConductor Artwork: Exact override match for {$artist} - {$title}");
            return $override['artwork_url'];
        }
        
        // Try fuzzy matching against all overrides
        $fuzzy_result = $this->fuzzy_match_override($artist, $title);
        if ($fuzzy_result) {
            error_log("CastConductor Artwork: Fuzzy override match for {$artist} - {$title}");
            return $fuzzy_result;
        }
        
        // Use automated enhanced discovery
        if (!class_exists('CastConductor_Album_Artwork_Controller')) {
            return null;
        }
        
        $artwork_controller = new CastConductor_Album_Artwork_Controller();
        $request = new WP_REST_Request('POST', '/castconductor/v5/album-artwork/search');
        $request->set_param('artist', $artist);
        $request->set_param('album', $album);
        $request->set_param('title', $title);
        $request->set_param('force_refresh', false);
        
        try {
            $response = $artwork_controller->search_artwork($request);
            
            if (is_wp_error($response)) {
                return null;
            }
            
            $response_data = $response->get_data();
            
            if (isset($response_data['success']) && $response_data['success'] && !empty($response_data['artwork_url'])) {
                return $response_data['artwork_url'];
            }
            
            return null;
            
        } catch (Exception $e) {
            error_log('CastConductor Metadata Proxy: Artwork search exception: ' . $e->getMessage());
            return null;
        }
    }
    
    /**
     * Set manual artwork override
     */
    public function set_artwork_override($request) {
        $artist = sanitize_text_field($request->get_param('artist'));
        $title = sanitize_text_field($request->get_param('title'));
        $artwork_url = esc_url_raw($request->get_param('artwork_url'));
        
        $override_key = $this->get_override_key($artist, $title);
        
        $override_data = [
            'artist' => $artist,
            'title' => $title,
            'artwork_url' => $artwork_url,
            'created_at' => current_time('mysql'),
            'created_by' => get_current_user_id()
        ];
        
        update_option('cc_artwork_override_' . $override_key, $override_data);
        
        // Clear metadata cache to force refresh
        $this->clear_metadata_cache();
        
        return rest_ensure_response([
            'success' => true,
            'message' => 'Artwork override saved successfully',
            'override' => $override_data
        ]);
    }
    
    /**
     * Get artwork override
     */
    public function get_artwork_override($request) {
        $artist = sanitize_text_field($request->get_param('artist'));
        $title = sanitize_text_field($request->get_param('title'));
        
        $override_key = $this->get_override_key($artist, $title);
        $override = get_option('cc_artwork_override_' . $override_key);
        
        if (!$override) {
            return new WP_Error(
                'not_found',
                'No artwork override found for this track',
                ['status' => 404]
            );
        }
        
        return rest_ensure_response($override);
    }
    
    /**
     * Delete artwork override
     */
    public function delete_artwork_override($request) {
        $artist = sanitize_text_field($request->get_param('artist'));
        $title = sanitize_text_field($request->get_param('title'));
        
        $override_key = $this->get_override_key($artist, $title);
        $deleted = delete_option('cc_artwork_override_' . $override_key);
        
        if (!$deleted) {
            return new WP_Error(
                'not_found',
                'No artwork override found for this track',
                ['status' => 404]
            );
        }
        
        // Clear metadata cache
        $this->clear_metadata_cache();
        
        return rest_ensure_response([
            'success' => true,
            'message' => 'Artwork override deleted successfully'
        ]);
    }
    
    /**
     * List all artwork overrides
     */
    public function list_artwork_overrides($request) {
        global $wpdb;
        
        $overrides = [];
        $results = $wpdb->get_results(
            "SELECT option_name, option_value FROM {$wpdb->options} WHERE option_name LIKE 'cc_artwork_override_%'",
            ARRAY_A
        );
        
        foreach ($results as $row) {
            $override = maybe_unserialize($row['option_value']);
            if ($override && isset($override['artist']) && isset($override['title'])) {
                $overrides[] = $override;
            }
        }
        
        return rest_ensure_response([
            'success' => true,
            'count' => count($overrides),
            'overrides' => $overrides
        ]);
    }
    
    /**
     * Generate consistent override key
     */
    private function get_override_key($artist, $title) {
        return md5(strtolower(trim($artist)) . '|' . strtolower(trim($title)));
    }
    
    /**
     * Fuzzy match against stored overrides
     * Uses similarity percentage to find near-matches
     * 
     * @param string $artist Artist name from metadata
     * @param string $title Track title from metadata
     * @return string|null Artwork URL if fuzzy match found, null otherwise
     */
    private function fuzzy_match_override($artist, $title) {
        global $wpdb;
        
        // Get all overrides (cached for performance)
        $cache_key = 'cc_all_overrides_fuzzy';
        $overrides = wp_cache_get($cache_key);
        
        if ($overrides === false) {
            $results = $wpdb->get_results(
                "SELECT option_value FROM {$wpdb->options} WHERE option_name LIKE 'cc_artwork_override_%'",
                ARRAY_A
            );
            
            $overrides = [];
            foreach ($results as $row) {
                $override = maybe_unserialize($row['option_value']);
                if ($override && !empty($override['artwork_url'])) {
                    $overrides[] = $override;
                }
            }
            wp_cache_set($cache_key, $overrides, '', 300); // Cache for 5 minutes
        }
        
        if (empty($overrides)) {
            return null;
        }
        
        $best_match = null;
        $best_score = 0;
        $threshold = 75; // Require 75% similarity
        
        $search_combined = strtolower(trim($artist) . ' ' . trim($title));
        
        foreach ($overrides as $override) {
            // Skip alias records
            if (!empty($override['is_alias_of'])) {
                continue;
            }
            
            // Check all match keys for this override
            $match_keys = $override['match_keys'] ?? [
                ['artist' => $override['artist'], 'title' => $override['title']]
            ];
            
            foreach ($match_keys as $match_key) {
                $override_combined = strtolower(trim($match_key['artist']) . ' ' . trim($match_key['title']));
                
                // Calculate similarity
                similar_text($search_combined, $override_combined, $percent);
                
                // Also try matching just artist or just title for partial matches
                $artist_percent = 0;
                $title_percent = 0;
                similar_text(strtolower(trim($artist)), strtolower(trim($match_key['artist'])), $artist_percent);
                similar_text(strtolower(trim($title)), strtolower(trim($match_key['title'])), $title_percent);
                
                // Use weighted score: full match weighted higher, but artist+title partial match also counts
                $weighted_score = max($percent, ($artist_percent * 0.5 + $title_percent * 0.5));
                
                if ($weighted_score > $best_score && $weighted_score >= $threshold) {
                    $best_score = $weighted_score;
                    $best_match = $override['artwork_url'];
                }
            }
        }
        
        return $best_match;
    }
    
    /**
     * Resize oversized artwork for Roku compatibility
     * Delegates to Album Artwork Controller's resize logic
     * 
     * @param string $artwork_url Original artwork URL
     * @return string Resized URL or original if resize unnecessary/fails
     */
    private function maybe_resize_artwork($artwork_url) {
        // Only process archive.org URLs (known to have oversized images)
        if (strpos($artwork_url, 'archive.org') === false) {
            return $artwork_url;
        }

        // Check if already processed
        $cache_key = 'cc_resized_' . md5($artwork_url);
        $cached = get_transient($cache_key);
        if ($cached !== false) {
            return $cached;
        }

        error_log("CastConductor Metadata: Checking image size for Roku: {$artwork_url}");

        // Download and check image size
        $response = wp_remote_get($artwork_url, array(
            'timeout' => 10,
            'headers' => array(
                'User-Agent' => 'CastConductor/5.0 (Roku Image Optimizer)'
            )
        ));

        if (is_wp_error($response)) {
            error_log('CastConductor Metadata: Failed to fetch image: ' . $response->get_error_message());
            set_transient($cache_key, $artwork_url, 3600);
            return $artwork_url;
        }

        $body = wp_remote_retrieve_body($response);
        $size = strlen($body);

        // Check GD library availability
        if (!function_exists('imagecreatefromstring')) {
            error_log('CastConductor Metadata: GD library not available');
            set_transient($cache_key, $artwork_url, 3600);
            return $artwork_url;
        }

        // Load image to check dimensions
        $image = @imagecreatefromstring($body);
        if (!$image) {
            error_log('CastConductor Metadata: Failed to load image');
            set_transient($cache_key, $artwork_url, 3600);
            return $artwork_url;
        }

        $orig_width = imagesx($image);
        $orig_height = imagesy($image);

        // Roku UI is 1280x720, album art displays at ~240px
        // 1000x1000 provides 4x resolution for sharpness while keeping file size small
        $max_dimension = 1000;
        
        // Check if resize needed (dimensions OR file size)
        $needs_resize = ($orig_width > $max_dimension || $orig_height > $max_dimension || $size > 2097152);
        
        if (!$needs_resize) {
            error_log("CastConductor Metadata: Image OK ({$orig_width}x{$orig_height}, {$size} bytes)");
            imagedestroy($image);
            set_transient($cache_key, $artwork_url, 86400);
            return $artwork_url;
        }

        error_log("CastConductor Metadata: Image needs resize ({$orig_width}x{$orig_height}, {$size} bytes)");

        // Calculate new dimensions
        if ($orig_width > $max_dimension || $orig_height > $max_dimension) {
            $scale = min($max_dimension / $orig_width, $max_dimension / $orig_height);
            $new_width = (int)($orig_width * $scale);
            $new_height = (int)($orig_height * $scale);
        } else {
            $new_width = $orig_width;
            $new_height = $orig_height;
        }

        // Create resized image
        $resized = imagecreatetruecolor($new_width, $new_height);
        imagecopyresampled($resized, $image, 0, 0, 0, 0, $new_width, $new_height, $orig_width, $orig_height);

        // Save to uploads
        $upload_dir = wp_upload_dir();
        $filename = 'cc-artwork-' . md5($artwork_url) . '.jpg';
        $filepath = $upload_dir['path'] . '/' . $filename;
        $fileurl = $upload_dir['url'] . '/' . $filename;

        $saved = @imagejpeg($resized, $filepath, 85);
        imagedestroy($image);
        imagedestroy($resized);

        if (!$saved) {
            error_log('CastConductor Metadata: Failed to save resized image');
            set_transient($cache_key, $artwork_url, 3600);
            return $artwork_url;
        }

        $new_size = filesize($filepath);
        error_log("CastConductor Metadata: Resized {$orig_width}x{$orig_height} → {$new_width}x{$new_height}, {$size} → {$new_size} bytes");
        error_log("CastConductor Metadata: Saved to {$fileurl}");
        
        // Cache for 7 days
        set_transient($cache_key, $fileurl, 604800);
        return $fileurl;
    }
    
    /**
     * Clear all metadata cache
     */
    private function clear_metadata_cache() {
        global $wpdb;
        $wpdb->query("DELETE FROM {$wpdb->options} WHERE option_name LIKE '_transient_cc_metadata_proxy_%'");
        $wpdb->query("DELETE FROM {$wpdb->options} WHERE option_name LIKE '_transient_timeout_cc_metadata_proxy_%'");
    }
    
    /**
     * Check admin permissions
     * Allows: Administrators and Editors (edit_posts)
     */
    public function admin_permissions_check() {
        return current_user_can('manage_options') || current_user_can('edit_posts');
    }
    
    /**
     * Proxy external API requests with caching
     * Enables Roku to fetch external APIs through WordPress to avoid CORS and rate limits
     * 
     * @param WP_REST_Request $request
     * @return WP_REST_Response|WP_Error
     */
    public function proxy_external_api($request) {
        $url = $request->get_param('url');
        $cache_duration = (int) $request->get_param('cache') ?: 300;
        $json_path = $request->get_param('path') ?: '';
        
        // Field mapping params
        $field_mapping = [
            'primary_text' => $request->get_param('primary') ?: '',
            'secondary_text' => $request->get_param('secondary') ?: '',
            'image_url' => $request->get_param('image') ?: '',
            'date_value' => $request->get_param('date') ?: '',
        ];
        
        // Create cache key from URL and mapping
        $cache_key = 'cc_external_api_' . md5($url . serialize($field_mapping));
        $cached = get_transient($cache_key);
        
        if ($cached !== false) {
            $cached['cached'] = true;
            return rest_ensure_response($cached);
        }
        
        // Fetch from external API
        $response = wp_remote_get($url, [
            'timeout' => 15,
            'headers' => [
                'User-Agent' => 'CastConductor/5.0 External API Proxy',
                'Accept' => 'application/json'
            ]
        ]);
        
        if (is_wp_error($response)) {
            return new WP_Error(
                'external_api_failed',
                'Failed to fetch external API: ' . $response->get_error_message(),
                ['status' => 502]
            );
        }
        
        $status_code = wp_remote_retrieve_response_code($response);
        if ($status_code < 200 || $status_code >= 300) {
            $body = wp_remote_retrieve_body($response);
            $error_data = json_decode($body, true);
            $error_msg = 'HTTP ' . $status_code;
            
            // Try to extract error message from response
            if (is_array($error_data)) {
                if (isset($error_data['error']['message'])) {
                    $error_msg = $error_data['error']['message'];
                } elseif (isset($error_data['message'])) {
                    $error_msg = $error_data['message'];
                } elseif (isset($error_data['error']) && is_string($error_data['error'])) {
                    $error_msg = $error_data['error'];
                }
            }
            
            return new WP_Error(
                'external_api_error',
                $error_msg,
                ['status' => $status_code]
            );
        }
        
        $body = wp_remote_retrieve_body($response);
        $data = json_decode($body, true);
        
        if ($data === null) {
            return new WP_Error(
                'invalid_json',
                'External API returned invalid JSON',
                ['status' => 502]
            );
        }
        
        // Extract from JSON path if specified (e.g., "data.items")
        if (!empty($json_path)) {
            $data = $this->extract_json_path($data, $json_path);
        }
        
        // If data is an array, take first item for single-item display
        if (is_array($data) && isset($data[0])) {
            $data = $data[0];
        }
        
        // Apply field mapping if specified
        $mapped_data = $this->apply_external_field_mapping($data, $field_mapping);
        
        $result = [
            'success' => true,
            'data' => $mapped_data,
            'raw' => $data,
            'cached' => false,
            'cache_duration' => $cache_duration,
            'timestamp' => current_time('c')
        ];
        
        // Cache the result
        set_transient($cache_key, $result, $cache_duration);
        
        return rest_ensure_response($result);
    }
    
    /**
     * Extract value from nested array using dot notation path
     * 
     * @param array $data Source data
     * @param string $path Dot notation path (e.g., "data.items.0")
     * @return mixed
     */
    private function extract_json_path($data, $path) {
        if (empty($path) || !is_array($data)) {
            return $data;
        }
        
        $parts = explode('.', $path);
        $current = $data;
        
        foreach ($parts as $part) {
            if (is_array($current) && isset($current[$part])) {
                $current = $current[$part];
            } elseif (is_array($current) && is_numeric($part) && isset($current[(int)$part])) {
                $current = $current[(int)$part];
            } else {
                return null;
            }
        }
        
        return $current;
    }
    
    /**
     * Apply field mapping to external API data
     * 
     * @param array $data Source data
     * @param array $mapping Field mapping configuration
     * @return array Mapped data with standard field names
     */
    private function apply_external_field_mapping($data, $mapping) {
        if (!is_array($data)) {
            return [
                'primary_text' => '',
                'secondary_text' => '',
                'image_url' => '',
                'date_value' => ''
            ];
        }
        
        $result = [];
        
        foreach ($mapping as $target_field => $source_field) {
            if (empty($source_field)) {
                $result[$target_field] = '';
                continue;
            }
            
            // Support dot notation for nested fields
            if (strpos($source_field, '.') !== false) {
                $result[$target_field] = $this->extract_json_path($data, $source_field) ?? '';
            } else {
                $result[$target_field] = $data[$source_field] ?? '';
            }
        }
        
        return $result;
    }
}
