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

/**
 * Enhanced Album Artwork Discovery Controller
 * 
 * Provides REST API endpoints for album artwork discovery
 * Integrates with iTunes, MusicBrainz, and Deezer APIs
 */

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

class CastConductor_Album_Artwork_Controller extends WP_REST_Controller {
    
    /**
     * API namespace
     */
    protected $namespace = 'castconductor/v5';
    
    /**
     * API base route
     */
    protected $rest_base = 'album-artwork';
    
    /**
     * Cache duration for artwork URLs (24 hours)
     */
    private $cache_duration = 86400;
    
    /**
     * Artwork discovery configuration
     */
    private $config = [
        'enabled' => true,
        'apis' => [
            'itunes' => [
                'enabled' => true,
                'base_url' => 'https://itunes.apple.com/search',
                'timeout' => 10,
                'rate_limit' => 100 // requests per minute
            ],
            'musicbrainz' => [
                'enabled' => true,
                'base_url' => 'https://musicbrainz.org/ws/2',
                'coverart_url' => 'https://coverartarchive.org',
                'timeout' => 10,
                'rate_limit' => 50 // requests per minute
            ],
            'deezer' => [
                'enabled' => true,
                'base_url' => 'https://api.deezer.com',
                'timeout' => 10,
                'rate_limit' => 100 // requests per minute
            ]
        ],
        'cache' => [
            'enabled' => true,
            'size_limit' => 1000,
            'duration' => 86400
        ],
        'fallback' => [
            'use_wordpress_branding' => true,
            'default_image' => ''
        ]
    ];
    
    /**
     * Register routes
     */
    public function register_routes() {
        // Search for album artwork
        register_rest_route($this->namespace, '/' . $this->rest_base . '/search', [
            [
                'methods' => 'POST',
                'callback' => [$this, 'search_artwork'],
                'permission_callback' => [$this, 'permissions_check'],
                'args' => $this->get_search_args_schema()
            ]
        ]);
        
        // Get cached artwork
        register_rest_route($this->namespace, '/' . $this->rest_base . '/cache/(?P<key>[a-f0-9]+)', [
            [
                'methods' => 'GET',
                'callback' => [$this, 'get_cached_artwork'],
                'permission_callback' => [$this, 'permissions_check']
            ]
        ]);
        
        // Clear artwork cache
        register_rest_route($this->namespace, '/' . $this->rest_base . '/cache', [
            [
                'methods' => 'DELETE',
                'callback' => [$this, 'clear_cache'],
                'permission_callback' => [$this, 'admin_permissions_check']
            ]
        ]);
        
        // Get artwork discovery statistics
        register_rest_route($this->namespace, '/' . $this->rest_base . '/stats', [
            [
                'methods' => 'GET',
                'callback' => [$this, 'get_stats'],
                'permission_callback' => [$this, 'admin_permissions_check']
            ]
        ]);
        
        // Test API connectivity
        register_rest_route($this->namespace, '/' . $this->rest_base . '/test', [
            [
                'methods' => 'POST',
                'callback' => [$this, 'test_apis'],
                'permission_callback' => [$this, 'admin_permissions_check']
            ]
        ]);
        
        // Search ALL sources (for override picker UI)
        register_rest_route($this->namespace, '/' . $this->rest_base . '/search-all', [
            [
                'methods' => 'POST',
                'callback' => [$this, 'search_all_sources'],
                'permission_callback' => [$this, 'admin_permissions_check'],
                'args' => $this->get_search_args_schema()
            ]
        ]);
        
        // Sideload external image to Media Library
        register_rest_route($this->namespace, '/' . $this->rest_base . '/sideload', [
            [
                'methods' => 'POST',
                'callback' => [$this, 'sideload_image'],
                'permission_callback' => [$this, 'admin_permissions_check'],
                'args' => [
                    'url' => [
                        'required' => true,
                        'type' => 'string',
                        'sanitize_callback' => 'esc_url_raw'
                    ],
                    'artist' => [
                        'required' => true,
                        'type' => 'string',
                        'sanitize_callback' => 'sanitize_text_field'
                    ],
                    'title' => [
                        'required' => true,
                        'type' => 'string',
                        'sanitize_callback' => 'sanitize_text_field'
                    ]
                ]
            ]
        ]);
    }
    
    /**
     * Search for album artwork
     */
    public function search_artwork($request) {
        $artist = sanitize_text_field($request->get_param('artist'));
        $album = sanitize_text_field($request->get_param('album'));
        $title = sanitize_text_field($request->get_param('title'));
        $force_refresh = $request->get_param('force_refresh') === true;
        
        // Validate input
        if (empty($artist) && empty($album) && empty($title)) {
            return new WP_Error(
                'missing_search_terms',
                'At least one search term (artist, album, or title) is required',
                ['status' => 400]
            );
        }
        
        // Create cache key
        $search_terms = strtolower(trim($artist . '|' . $album . '|' . $title));
        $cache_key = 'cc_artwork_' . md5($search_terms);
        
        // Check cache unless force refresh
        if (!$force_refresh && $this->config['cache']['enabled']) {
            $cached = get_transient($cache_key);
            if ($cached !== false) {
                return rest_ensure_response([
                    'success' => true,
                    'artwork_url' => $cached === 'not_found' ? null : $cached,
                    'source' => 'cache',
                    'cache_key' => $cache_key,
                    'search_terms' => [
                        'artist' => $artist,
                        'album' => $album,
                        'title' => $title
                    ]
                ]);
            }
        }
        
        // Start search process
        $artwork_url = '';
        $search_source = '';
        $api_responses = [];
        
        // Search iTunes API (PRIORITY 1 - Most reliable)
        if ($this->config['apis']['itunes']['enabled'] && empty($artwork_url)) {
            $result = $this->search_itunes_api($artist, $album, $title);
            $api_responses['itunes'] = $result;
            if (!empty($result['artwork_url'])) {
                $artwork_url = $result['artwork_url'];
                $search_source = 'itunes';
            }
        }
        
        // Search Deezer API (PRIORITY 2 - Reliable URLs)
        if ($this->config['apis']['deezer']['enabled'] && empty($artwork_url)) {
            $result = $this->search_deezer_api($artist, $album, $title);
            $api_responses['deezer'] = $result;
            if (!empty($result['artwork_url'])) {
                $artwork_url = $result['artwork_url'];
                $search_source = 'deezer';
            }
        }
        
        // Search MusicBrainz + Cover Art Archive (PRIORITY 3 - Last resort)
        // Note: MusicBrainz returns archive.org URLs which may be oversized for Roku
        if ($this->config['apis']['musicbrainz']['enabled'] && empty($artwork_url)) {
            $result = $this->search_musicbrainz_api($artist, $album, $title);
            $api_responses['musicbrainz'] = $result;
            if (!empty($result['artwork_url'])) {
                // Resize archive.org images if needed (Roku limit: 2MB, 1920x1920)
                $artwork_url = $this->maybe_resize_image($result['artwork_url']);
                $search_source = 'musicbrainz';
            }
        }
        
        // Cache the result
        if ($this->config['cache']['enabled']) {
            $cache_value = empty($artwork_url) ? 'not_found' : $artwork_url;
            set_transient($cache_key, $cache_value, $this->cache_duration);
        }
        
        // Update statistics
        $this->update_search_stats($search_source, !empty($artwork_url));
        
        return rest_ensure_response([
            'success' => true,
            'artwork_url' => empty($artwork_url) ? null : $artwork_url,
            'source' => $search_source ?: 'none',
            'cache_key' => $cache_key,
            'search_terms' => [
                'artist' => $artist,
                'album' => $album,
                'title' => $title
            ],
            'api_responses' => $api_responses
        ]);
    }
    
    /**
     * Search iTunes API
     */
    private function search_itunes_api($artist, $album, $title) {
        $search_terms = array_filter([$artist, $album, $title]);
        if (empty($search_terms)) {
            return ['error' => 'No search terms provided'];
        }
        
        $query = urlencode(implode(' ', $search_terms));
        $url = $this->config['apis']['itunes']['base_url'] . "?term={$query}&entity=album&limit=10&media=music";
        
        $response = wp_remote_get($url, [
            'timeout' => $this->config['apis']['itunes']['timeout'],
            'user-agent' => 'CastConductor/' . CASTCONDUCTOR_VERSION
        ]);
        
        if (is_wp_error($response)) {
            return ['error' => $response->get_error_message()];
        }
        
        $body = wp_remote_retrieve_body($response);
        $data = json_decode($body, true);
        
        if (empty($data['results'])) {
            return ['error' => 'No results found'];
        }
        
        // Find best match
        foreach ($data['results'] as $result) {
            if (!empty($result['artworkUrl100'])) {
                // Score the match quality
                $score = $this->calculate_match_score([
                    'artist' => $result['artistName'] ?? '',
                    'album' => $result['collectionName'] ?? ''
                ], [
                    'artist' => $artist,
                    'album' => $album
                ]);
                
                if ($score > 0.5) { // 50% match threshold
                    $artwork_url = str_replace('100x100bb', '600x600bb', $result['artworkUrl100']);
                    return [
                        'artwork_url' => $artwork_url,
                        'match_score' => $score,
                        'matched_item' => [
                            'artist' => $result['artistName'],
                            'album' => $result['collectionName'],
                            'release_date' => $result['releaseDate'] ?? ''
                        ]
                    ];
                }
            }
        }
        
        return ['error' => 'No suitable matches found'];
    }
    
    /**
     * Search MusicBrainz API
     */
    private function search_musicbrainz_api($artist, $album, $title) {
        if (empty($artist)) {
            return ['error' => 'Artist name required for MusicBrainz search'];
        }
        
        $query_parts = ['artist:' . urlencode($artist)];
        
        if (!empty($album)) {
            $query_parts[] = 'release:' . urlencode($album);
        } elseif (!empty($title)) {
            $query_parts[] = 'release:' . urlencode($title);
        }
        
        $query = implode(' AND ', $query_parts);
        $url = $this->config['apis']['musicbrainz']['base_url'] . "/release?query={$query}&fmt=json&limit=10";
        
        $response = wp_remote_get($url, [
            'timeout' => $this->config['apis']['musicbrainz']['timeout'],
            'user-agent' => 'CastConductor/' . CASTCONDUCTOR_VERSION . ' (contact@castconductor.com)'
        ]);
        
        if (is_wp_error($response)) {
            return ['error' => $response->get_error_message()];
        }
        
        $body = wp_remote_retrieve_body($response);
        $data = json_decode($body, true);
        
        if (empty($data['releases'])) {
            return ['error' => 'No releases found'];
        }
        
        // Try to get artwork from Cover Art Archive
        foreach ($data['releases'] as $release) {
            if (!empty($release['id'])) {
                $artwork_url = $this->get_coverart_archive_image($release['id']);
                if (!empty($artwork_url)) {
                    $score = $this->calculate_match_score([
                        'artist' => $release['artist-credit'][0]['artist']['name'] ?? '',
                        'album' => $release['title'] ?? ''
                    ], [
                        'artist' => $artist,
                        'album' => $album ?: $title
                    ]);
                    
                    return [
                        'artwork_url' => $artwork_url,
                        'match_score' => $score,
                        'matched_item' => [
                            'artist' => $release['artist-credit'][0]['artist']['name'] ?? '',
                            'album' => $release['title'] ?? '',
                            'release_id' => $release['id'],
                            'release_date' => $release['date'] ?? ''
                        ]
                    ];
                }
            }
        }
        
        return ['error' => 'No artwork found in Cover Art Archive'];
    }
    
    /**
     * Get artwork from Cover Art Archive
     */
    private function get_coverart_archive_image($release_id) {
        $url = $this->config['apis']['musicbrainz']['coverart_url'] . "/release/{$release_id}/front";
        
        $response = wp_remote_head($url, [
            'timeout' => $this->config['apis']['musicbrainz']['timeout'],
            'redirection' => 0
        ]);
        
        if (!is_wp_error($response)) {
            $code = wp_remote_retrieve_response_code($response);
            if ($code === 302 || $code === 307) {
                $location = wp_remote_retrieve_header($response, 'location');
                if (!empty($location)) {
                    return $location;
                }
            }
        }
        
        return '';
    }
    
    /**
     * Search Deezer API
     */
    private function search_deezer_api($artist, $album, $title) {
        $search_terms = array_filter([$artist, $album, $title]);
        if (empty($search_terms)) {
            return ['error' => 'No search terms provided'];
        }
        
        $query = urlencode(implode(' ', $search_terms));
        $url = $this->config['apis']['deezer']['base_url'] . "/search/album?q={$query}&limit=10";
        
        $response = wp_remote_get($url, [
            'timeout' => $this->config['apis']['deezer']['timeout'],
            'user-agent' => 'CastConductor/' . CASTCONDUCTOR_VERSION
        ]);
        
        if (is_wp_error($response)) {
            return ['error' => $response->get_error_message()];
        }
        
        $body = wp_remote_retrieve_body($response);
        $data = json_decode($body, true);
        
        if (empty($data['data'])) {
            return ['error' => 'No results found'];
        }
        
        // Find best match
        foreach ($data['data'] as $album_data) {
            $artwork_url = $album_data['cover_xl'] ?? $album_data['cover_big'] ?? '';
            if (!empty($artwork_url)) {
                $score = $this->calculate_match_score([
                    'artist' => $album_data['artist']['name'] ?? '',
                    'album' => $album_data['title'] ?? ''
                ], [
                    'artist' => $artist,
                    'album' => $album ?: $title
                ]);
                
                if ($score > 0.5) {
                    return [
                        'artwork_url' => $artwork_url,
                        'match_score' => $score,
                        'matched_item' => [
                            'artist' => $album_data['artist']['name'] ?? '',
                            'album' => $album_data['title'] ?? '',
                            'deezer_id' => $album_data['id'] ?? ''
                        ]
                    ];
                }
            }
        }
        
        return ['error' => 'No suitable matches found'];
    }
    
    /**
     * Calculate match score between search terms and API result
     */
    private function calculate_match_score($result, $search) {
        $score = 0;
        $max_score = 0;
        
        // Artist match (weighted higher)
        if (!empty($search['artist']) && !empty($result['artist'])) {
            $max_score += 2;
            $similarity = 0;
            similar_text(
                strtolower($search['artist']), 
                strtolower($result['artist']), 
                $similarity
            );
            $score += ($similarity / 100) * 2;
        }
        
        // Album match
        if (!empty($search['album']) && !empty($result['album'])) {
            $max_score += 1;
            $similarity = 0;
            similar_text(
                strtolower($search['album']), 
                strtolower($result['album']), 
                $similarity
            );
            $score += ($similarity / 100);
        }
        
        return $max_score > 0 ? $score / $max_score : 0;
    }
    
    /**
     * Get cached artwork
     */
    public function get_cached_artwork($request) {
        $cache_key = 'cc_artwork_' . $request->get_param('key');
        $cached = get_transient($cache_key);
        
        if ($cached === false) {
            return new WP_Error(
                'cache_miss',
                'Artwork not found in cache',
                ['status' => 404]
            );
        }
        
        return rest_ensure_response([
            'success' => true,
            'artwork_url' => $cached === 'not_found' ? null : $cached,
            'cache_key' => $cache_key
        ]);
    }
    
    /**
     * Clear artwork cache
     */
    public function clear_cache($request) {
        global $wpdb;
        
        $deleted = $wpdb->query(
            "DELETE FROM {$wpdb->options} 
             WHERE option_name LIKE '_transient_cc_artwork_%' 
             OR option_name LIKE '_transient_timeout_cc_artwork_%'"
        );
        
        return rest_ensure_response([
            'success' => true,
            'message' => "Cleared {$deleted} cached artwork entries"
        ]);
    }
    
    /**
     * Get discovery statistics
     */
    public function get_stats($request) {
        $stats = get_option('castconductor_artwork_stats', [
            'total_searches' => 0,
            'successful_searches' => 0,
            'api_usage' => [
                'itunes' => 0,
                'musicbrainz' => 0,
                'deezer' => 0
            ],
            'cache_hits' => 0,
            'last_reset' => time()
        ]);
        
        // Calculate success rate
        $success_rate = $stats['total_searches'] > 0 
            ? ($stats['successful_searches'] / $stats['total_searches']) * 100 
            : 0;
        
        return rest_ensure_response([
            'success' => true,
            'stats' => array_merge($stats, [
                'success_rate' => round($success_rate, 2)
            ])
        ]);
    }
    
    /**
     * Test API connectivity
     */
    public function test_apis($request) {
        $results = [];
        
        // Test iTunes API
        if ($this->config['apis']['itunes']['enabled']) {
            $test_result = $this->search_itunes_api('Pink Floyd', 'Dark Side of the Moon', '');
            $results['itunes'] = [
                'status' => empty($test_result['error']) ? 'success' : 'error',
                'response' => $test_result
            ];
        }
        
        // Test MusicBrainz API
        if ($this->config['apis']['musicbrainz']['enabled']) {
            $test_result = $this->search_musicbrainz_api('Pink Floyd', 'Dark Side of the Moon', '');
            $results['musicbrainz'] = [
                'status' => empty($test_result['error']) ? 'success' : 'error',
                'response' => $test_result
            ];
        }
        
        // Test Deezer API
        if ($this->config['apis']['deezer']['enabled']) {
            $test_result = $this->search_deezer_api('Pink Floyd', 'Dark Side of the Moon', '');
            $results['deezer'] = [
                'status' => empty($test_result['error']) ? 'success' : 'error',
                'response' => $test_result
            ];
        }
        
        return rest_ensure_response([
            'success' => true,
            'test_results' => $results
        ]);
    }
    
    /**
     * Update search statistics
     */
    private function update_search_stats($source, $success) {
        $stats = get_option('castconductor_artwork_stats', [
            'total_searches' => 0,
            'successful_searches' => 0,
            'api_usage' => [
                'itunes' => 0,
                'musicbrainz' => 0,
                'deezer' => 0
            ],
            'cache_hits' => 0,
            'last_reset' => time()
        ]);
        
        $stats['total_searches']++;
        if ($success) {
            $stats['successful_searches']++;
        }
        if (!empty($source) && isset($stats['api_usage'][$source])) {
            $stats['api_usage'][$source]++;
        }
        
        update_option('castconductor_artwork_stats', $stats);
    }
    
    /**
     * Get search arguments schema
     */
    private function get_search_args_schema() {
        return [
            'artist' => [
                'description' => 'Artist name',
                'type' => 'string',
                'sanitize_callback' => 'sanitize_text_field'
            ],
            'album' => [
                'description' => 'Album name',
                'type' => 'string',
                'sanitize_callback' => 'sanitize_text_field'
            ],
            'title' => [
                'description' => 'Track title',
                'type' => 'string',
                'sanitize_callback' => 'sanitize_text_field'
            ],
            'force_refresh' => [
                'description' => 'Force refresh cache',
                'type' => 'boolean',
                'default' => false
            ]
        ];
    }
    
    /**
     * Resize oversized images for Roku compatibility
     * Roku limits: ~2MB file size, recommended max 1920x1920px
     * 
     * @param string $image_url Original image URL
     * @return string Resized image URL or original if resize fails/unnecessary
     */
    private function maybe_resize_image($image_url) {
        // Only process archive.org URLs (known to have oversized images)
        if (strpos($image_url, 'archive.org') === false) {
            return $image_url;
        }

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

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

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

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

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

        // Check if GD library is available
        if (!function_exists('imagecreatefromstring')) {
            error_log('CastConductor: GD library not available, cannot resize');
            set_transient($cache_key, $image_url, 3600);
            return $image_url;
        }

        // Load image to check dimensions
        $image = @imagecreatefromstring($body);
        if (!$image) {
            error_log('CastConductor: Failed to load image for resizing');
            set_transient($cache_key, $image_url, 3600);
            return $image_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: Image OK ({$orig_width}x{$orig_height}, {$size} bytes), no resize needed");
            imagedestroy($image);
            set_transient($cache_key, $image_url, 86400);
            return $image_url;
        }

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

        // Calculate new dimensions (maintain aspect ratio)
        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 WordPress uploads
        $upload_dir = wp_upload_dir();
        $filename = 'cc-artwork-' . md5($image_url) . '.jpg';
        $filepath = $upload_dir['path'] . '/' . $filename;
        $fileurl = $upload_dir['url'] . '/' . $filename;

        // Save as JPEG with quality 85
        $saved = @imagejpeg($resized, $filepath, 85);
        imagedestroy($image);
        imagedestroy($resized);

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

        $new_size = filesize($filepath);
        error_log("CastConductor: Resized {$orig_width}x{$orig_height} → {$new_width}x{$new_height}, {$size} bytes → {$new_size} bytes");
        error_log("CastConductor: Saved to {$fileurl}");
        
        // Cache the resized URL for 7 days
        set_transient($cache_key, $fileurl, 604800);
        return $fileurl;
    }

    /**
     * Check permissions for reading
     */
    public function permissions_check($request) {
        return true; // Allow all users to search for artwork
    }
    
    /**
     * Check admin permissions
     * Allows: Administrators, castconductor_admin role, and Editors (edit_posts)
     */
    public function admin_permissions_check($request) {
        return current_user_can('manage_options') || current_user_can('edit_posts');
    }
    
    /**
     * Search ALL sources and return all results for picker UI
     */
    public function search_all_sources($request) {
        $artist = sanitize_text_field($request->get_param('artist'));
        $album = sanitize_text_field($request->get_param('album'));
        $title = sanitize_text_field($request->get_param('title'));
        
        if (empty($artist) && empty($album) && empty($title)) {
            return new WP_Error(
                'missing_search_terms',
                'At least one search term is required',
                ['status' => 400]
            );
        }
        
        $results = [];
        
        // Search iTunes
        if ($this->config['apis']['itunes']['enabled']) {
            $itunes_results = $this->search_itunes_all($artist, $album, $title);
            foreach ($itunes_results as $r) {
                $r['source'] = 'itunes';
                $results[] = $r;
            }
        }
        
        // Search Deezer
        if ($this->config['apis']['deezer']['enabled']) {
            $deezer_results = $this->search_deezer_all($artist, $album, $title);
            foreach ($deezer_results as $r) {
                $r['source'] = 'deezer';
                $results[] = $r;
            }
        }
        
        // Search MusicBrainz (slower, do last)
        if ($this->config['apis']['musicbrainz']['enabled']) {
            $mb_results = $this->search_musicbrainz_all($artist, $album, $title);
            foreach ($mb_results as $r) {
                $r['source'] = 'musicbrainz';
                $results[] = $r;
            }
        }
        
        return rest_ensure_response([
            'success' => true,
            'count' => count($results),
            'results' => $results,
            'search_terms' => [
                'artist' => $artist,
                'album' => $album,
                'title' => $title
            ]
        ]);
    }
    
    /**
     * Search iTunes and return ALL matches (not just best)
     */
    private function search_itunes_all($artist, $album, $title) {
        $results = [];
        $search_terms = array_filter([$artist, $album, $title]);
        if (empty($search_terms)) return $results;
        
        $query = urlencode(implode(' ', $search_terms));
        $url = $this->config['apis']['itunes']['base_url'] . "?term={$query}&entity=album&limit=10&media=music";
        
        $response = wp_remote_get($url, [
            'timeout' => $this->config['apis']['itunes']['timeout'],
            'user-agent' => 'CastConductor/' . CASTCONDUCTOR_VERSION
        ]);
        
        if (is_wp_error($response)) return $results;
        
        $body = wp_remote_retrieve_body($response);
        $data = json_decode($body, true);
        
        if (empty($data['results'])) return $results;
        
        foreach ($data['results'] as $item) {
            if (!empty($item['artworkUrl100'])) {
                $artwork_url = str_replace('100x100bb', '600x600bb', $item['artworkUrl100']);
                $results[] = [
                    'artwork_url' => $artwork_url,
                    'artist' => $item['artistName'] ?? '',
                    'album' => $item['collectionName'] ?? '',
                    'release_date' => $item['releaseDate'] ?? ''
                ];
            }
        }
        
        return array_slice($results, 0, 5); // Limit to 5 per source
    }
    
    /**
     * Search Deezer and return ALL matches
     */
    private function search_deezer_all($artist, $album, $title) {
        $results = [];
        $search_terms = array_filter([$artist, $album, $title]);
        if (empty($search_terms)) return $results;
        
        $query = urlencode(implode(' ', $search_terms));
        $url = $this->config['apis']['deezer']['base_url'] . "/search/album?q={$query}&limit=10";
        
        $response = wp_remote_get($url, [
            'timeout' => $this->config['apis']['deezer']['timeout'],
            'user-agent' => 'CastConductor/' . CASTCONDUCTOR_VERSION
        ]);
        
        if (is_wp_error($response)) return $results;
        
        $body = wp_remote_retrieve_body($response);
        $data = json_decode($body, true);
        
        if (empty($data['data'])) return $results;
        
        foreach ($data['data'] as $item) {
            $artwork_url = $item['cover_xl'] ?? $item['cover_big'] ?? '';
            if (!empty($artwork_url)) {
                $results[] = [
                    'artwork_url' => $artwork_url,
                    'artist' => $item['artist']['name'] ?? '',
                    'album' => $item['title'] ?? ''
                ];
            }
        }
        
        return array_slice($results, 0, 5);
    }
    
    /**
     * Search MusicBrainz and return ALL matches with artwork
     */
    private function search_musicbrainz_all($artist, $album, $title) {
        $results = [];
        if (empty($artist)) return $results;
        
        $query_parts = ['artist:' . urlencode($artist)];
        if (!empty($album)) {
            $query_parts[] = 'release:' . urlencode($album);
        } elseif (!empty($title)) {
            $query_parts[] = 'release:' . urlencode($title);
        }
        
        $query = implode(' AND ', $query_parts);
        $url = $this->config['apis']['musicbrainz']['base_url'] . "/release?query={$query}&fmt=json&limit=5";
        
        $response = wp_remote_get($url, [
            'timeout' => $this->config['apis']['musicbrainz']['timeout'],
            'user-agent' => 'CastConductor/' . CASTCONDUCTOR_VERSION . ' (contact@castconductor.com)'
        ]);
        
        if (is_wp_error($response)) return $results;
        
        $body = wp_remote_retrieve_body($response);
        $data = json_decode($body, true);
        
        if (empty($data['releases'])) return $results;
        
        foreach ($data['releases'] as $release) {
            if (!empty($release['id'])) {
                $artwork_url = $this->get_coverart_archive_image($release['id']);
                if (!empty($artwork_url)) {
                    $results[] = [
                        'artwork_url' => $artwork_url,
                        'artist' => $release['artist-credit'][0]['artist']['name'] ?? '',
                        'album' => $release['title'] ?? '',
                        'release_date' => $release['date'] ?? ''
                    ];
                }
            }
            if (count($results) >= 3) break; // MusicBrainz is slow, limit more
        }
        
        return $results;
    }
    
    /**
     * Sideload external image to WordPress Media Library and set as override
     */
    public function sideload_image($request) {
        $url = $request->get_param('url');
        $artist = $request->get_param('artist');
        $title = $request->get_param('title');
        $match_artist = $request->get_param('match_artist');
        $match_title = $request->get_param('match_title');
        
        if (empty($url)) {
            return new WP_Error('missing_url', 'URL is required', ['status' => 400]);
        }
        
        // Download the image
        $response = wp_remote_get($url, [
            'timeout' => 30,
            'user-agent' => 'CastConductor/' . CASTCONDUCTOR_VERSION
        ]);
        
        if (is_wp_error($response)) {
            return new WP_Error('download_failed', 'Failed to download image: ' . $response->get_error_message(), ['status' => 500]);
        }
        
        $body = wp_remote_retrieve_body($response);
        $content_type = wp_remote_retrieve_header($response, 'content-type');
        
        if (empty($body)) {
            return new WP_Error('empty_response', 'Image download returned empty response', ['status' => 500]);
        }
        
        // Determine extension from content type
        $ext = 'jpg';
        if (strpos($content_type, 'png') !== false) {
            $ext = 'png';
        } elseif (strpos($content_type, 'webp') !== false) {
            $ext = 'webp';
        } elseif (strpos($content_type, 'gif') !== false) {
            $ext = 'gif';
        }
        
        // Create filename from artist + title
        $safe_name = sanitize_file_name(strtolower(trim($artist . '-' . $title)));
        $safe_name = preg_replace('/[^a-z0-9\-]/', '', $safe_name);
        $filename = 'cc-override-' . $safe_name . '.' . $ext;
        
        // Upload to WordPress
        $upload = wp_upload_bits($filename, null, $body);
        
        if (!empty($upload['error'])) {
            return new WP_Error('upload_failed', 'Failed to save image: ' . $upload['error'], ['status' => 500]);
        }
        
        // Create attachment
        $attachment = [
            'post_mime_type' => $content_type ?: 'image/jpeg',
            'post_title' => "Album Art: {$artist} - {$title}",
            'post_content' => '',
            'post_status' => 'inherit'
        ];
        
        $attach_id = wp_insert_attachment($attachment, $upload['file']);
        
        if (is_wp_error($attach_id)) {
            return new WP_Error('attachment_failed', 'Failed to create media attachment', ['status' => 500]);
        }
        
        // Generate metadata
        require_once(ABSPATH . 'wp-admin/includes/image.php');
        $attach_data = wp_generate_attachment_metadata($attach_id, $upload['file']);
        wp_update_attachment_metadata($attach_id, $attach_data);
        
        $local_url = $upload['url'];
        
        // Also set as override if artist and title provided
        if (!empty($artist) && !empty($title)) {
            // Primary key (canonical name used for search)
            $override_key = md5(strtolower(trim($artist)) . '|' . strtolower(trim($title)));
            
            // Build match keys array (primary + any additional match key)
            $match_keys = [
                ['artist' => $artist, 'title' => $title]
            ];
            
            // Add alternate match key if provided and different from primary
            if (!empty($match_artist) || !empty($match_title)) {
                $alt_artist = !empty($match_artist) ? $match_artist : $artist;
                $alt_title = !empty($match_title) ? $match_title : $title;
                
                // Only add if different from primary
                if (strtolower(trim($alt_artist)) !== strtolower(trim($artist)) || 
                    strtolower(trim($alt_title)) !== strtolower(trim($title))) {
                    $match_keys[] = ['artist' => $alt_artist, 'title' => $alt_title];
                    
                    // Also create a separate option for the alternate key for fast lookup
                    $alt_key = md5(strtolower(trim($alt_artist)) . '|' . strtolower(trim($alt_title)));
                    update_option('cc_artwork_override_' . $alt_key, [
                        'artist' => $alt_artist,
                        'title' => $alt_title,
                        'artwork_url' => $local_url,
                        'attachment_id' => $attach_id,
                        'created_at' => current_time('mysql'),
                        'created_by' => get_current_user_id(),
                        'is_alias_of' => $override_key // Reference to primary
                    ]);
                }
            }
            
            $override_data = [
                'artist' => $artist,
                'title' => $title,
                'artwork_url' => $local_url,
                'attachment_id' => $attach_id,
                'created_at' => current_time('mysql'),
                'created_by' => get_current_user_id(),
                'match_keys' => $match_keys
            ];
            update_option('cc_artwork_override_' . $override_key, $override_data);
        }
        
        return rest_ensure_response([
            'success' => true,
            'local_url' => $local_url,
            'attachment_id' => $attach_id,
            'message' => 'Image uploaded and override saved'
        ]);
    }
}
