<?php

// Prevent direct access to the file
if (!defined('ABSPATH')) {
    exit;
}

// Include necessary files
require_once(ABSPATH . 'wp-admin/includes/image.php');
require_once(ABSPATH . 'wp-admin/includes/file.php');
require_once(ABSPATH . 'wp-admin/includes/media.php');

class ScrobbleHandler {
    private $auth_key;
    private $option_name = 'image_scrobble_auth_key';

    public function __construct() {
        $this->auth_key = get_option($this->option_name);
        
        add_action('rest_api_init', array($this, 'register_routes'));
        add_action('admin_menu', array($this, 'add_admin_menu'));
        add_action('admin_init', array($this, 'register_settings'));
        register_activation_hook(__FILE__, array($this, 'activate_plugin'));

        // Add action to handle form submission
        add_action('admin_post_image_scrobble_upload', array($this, 'handle_image_upload'));
    }

    public function activate_plugin() {
        // Set default AUTH_KEY if not exists
        if (!get_option($this->option_name)) {
            update_option($this->option_name, wp_generate_password(32, false));
        }
    }

    public function register_routes() {
        register_rest_route('image-scrobble/v1', '/create', array(
            'methods' => 'POST',
            'callback' => array($this, 'create_image'),
            'permission_callback' => array($this, 'check_auth')
        ));

        register_rest_route('image-scrobble/v1', '/scrobble', array(
            'methods' => 'POST',
            'callback' => array($this, 'scrobble'),
            'permission_callback' => array($this, 'check_auth')
        ));
    }

    public function check_auth($request) {
        $auth_header = $request->get_header('Authorization');
        return $auth_header && $auth_header === "Bearer {$this->auth_key}";
    }

    public function create_image($request) {
        // Retrieve the raw image data from the request body
        $image_data = $request->get_body();

        // Sanitize and retrieve parameters from the request
        $author = sanitize_text_field($request->get_param('author'));
        $album_title = sanitize_text_field($request->get_param('album_title'));
        $song_title = sanitize_text_field($request->get_param('song_title'));

        // Validate required parameters
        if (empty($author)) {
            return new WP_Error(
                'missing_author',
                'Author is required.',
                array('status' => 400)
            );
        }

        // Ensure that either album_title or song_title is provided
        if (empty($album_title) && empty($song_title)) {
            return new WP_Error(
                'missing_titles',
                'Either album title or song title is required.',
                array('status' => 400)
            );
        }

        // Determine which title to use: album_title takes precedence over song_title
        $title = !empty($album_title) ? $album_title : $song_title;

        // Generate a hash of the image data to check for duplicates
        $image_hash = md5($image_data);

        // Check if an image with this hash already exists to prevent duplicates
        $existing_attachment = $this->get_attachment_by_hash($image_hash);
        if ($existing_attachment) {
            return new WP_REST_Response(
                array('url' => wp_get_attachment_url($existing_attachment->ID)),
                200
            );
        }

        // Save the image and create attachment
        $result = $this->save_image_and_create_attachment($image_data, $author, $title, $image_hash, $album_title);

        if (is_wp_error($result)) {
            return $result;
        }

        // Return a successful response with the image URL
        return new WP_REST_Response(
            array('url' => $result),
            200
        );
    }

    private function save_image_and_create_attachment($image_data, $author, $title, $image_hash, $album_title = '') {
        // Get the upload directory information
        $upload_dir = wp_upload_dir();

        // Create a sanitized filename using the author and the determined title
        $filename = sanitize_file_name("{$author} - {$title}.jpg");

        // Ensure the filename is unique within the upload directory
        $unique_filename = wp_unique_filename($upload_dir['path'], $filename);

        // Construct the full file path
        $file_path = trailingslashit($upload_dir['path']) . $unique_filename;

        // Attempt to save the image data to the specified file path
        if (file_put_contents($file_path, $image_data) === false) {
            return new WP_Error(
                'file_save_failed',
                'Failed to save the image file.',
                array('status' => 500)
            );
        }

        // Prepare the attachment data
        $attachment = array(
            'post_mime_type' => 'image/jpeg',
            'post_title'     => "{$author} - {$title}",
            'post_content'   => '',
            'post_status'    => 'inherit'
        );

        // Insert the attachment into the WordPress media library
        $attach_id = wp_insert_attachment($attachment, $file_path);

        // Check for errors during attachment insertion
        if (is_wp_error($attach_id)) {
            @unlink($file_path); // Clean up the file if attachment creation failed
            return $attach_id;    // Return the WP_Error
        }

        // Generate and update attachment metadata
        require_once(ABSPATH . 'wp-admin/includes/image.php');
        $attach_data = wp_generate_attachment_metadata($attach_id, $file_path);
        wp_update_attachment_metadata($attach_id, $attach_data);

        // Save the image hash in the attachment's metadata for future duplicate checks
        update_post_meta($attach_id, 'image_hash', $image_hash);

        // Save the artist and album_title in attachment metadata for future lookups
        if (!empty($album_title)) {
            update_post_meta($attach_id, 'artist', strtolower($author));
            update_post_meta($attach_id, 'album_title', strtolower($album_title));
        }

        // Retrieve the URL of the newly created attachment
        $image_url = wp_get_attachment_url($attach_id);

        return $image_url;
    }

    private function get_attachment_by_hash($hash) {
        $args = array(
            'post_type' => 'attachment',
            'post_status' => 'inherit',
            'posts_per_page' => 1,
            'meta_query' => array(
                array(
                    'key' => 'image_hash',
                    'value' => $hash,
                    'compare' => '='
                )
            )
        );

        $query = new WP_Query($args);

        if ($query->have_posts()) {
            return $query->posts[0];
        }

        return null;
    }

    private function get_attachment_by_artist_album($artist, $album_title) {
        global $wpdb; // Access the global $wpdb object
        
        // Primary method: Query attachments based on artist and album title
        $args = array(
            'post_type'      => 'attachment',
            'post_status'    => 'inherit',
            'posts_per_page' => 1,
            'meta_query'     => array(
                'relation' => 'AND',
                array(
                    'key'     => 'artist',
                    'value'   => strtolower($artist),
                    'compare' => '='
                ),
                array(
                    'key'     => 'album_title',
                    'value'   => strtolower($album_title),
                    'compare' => '='
                )
            )
        );
    
        $query = new WP_Query($args);
    
        if ($query->have_posts()) {
            // Attachment found via primary method
            return wp_get_attachment_url($query->posts[0]->ID);
        }
    
        // Sanitize and prepare the variables
        $album_name_lower = strtolower($album_title);
        $author_lower     = strtolower($artist);
        $table_name       = 'song_scrobbles';
    
        // Prepare the SQL query using $wpdb->prepare to prevent SQL injection
        $prepared_query = $wpdb->prepare(
            "
            SELECT cover_url 
            FROM {$table_name}
            WHERE LOWER(album_name) = %s 
              AND LOWER(author) = %s 
            ORDER BY id DESC 
            LIMIT 1
            ",
            $album_name_lower, 
            $author_lower
        );
    
        // Execute the query and retrieve the cover URL
        $cover_url = $wpdb->get_var($prepared_query);
    
        if ($cover_url) {
            // Optional: Validate the URL or process it as needed
            return esc_url($cover_url);
        }

        // Try fetching from Apple iTunes
        $remote_album_url = $this->fetch_and_save_album_cover($artist, $album_title);
        if ($remote_album_url) {
            return esc_url($remote_album_url);
        }

        // If both methods fail, return null
        return null;
    }
    
    function normalize_names(string $author, string $songName, string $albumName): array
    {
        // Define the album name mapping
        $albumNameMapping = [
            'Nirvana' => [
                'In Utero (20th Anniversary Remaster)' => 'In Utero',
                'Nevermind (Deluxe Edition)' => 'Nevermind',
                'Bleach: Deluxe Edition' => 'Bleach'
            ]
        ];
    
        // Apply the album name mapping if applicable
        if ($albumName && isset($albumNameMapping[$author])) {
            foreach ($albumNameMapping[$author] as $variant => $standardized) {
                // Use case-insensitive comparison
                if (strcasecmp($albumName, $variant) === 0) {
                    $albumName = $standardized;
                    break;
                }
            }
        }
    
        // Define a regex pattern to remove remaster annotations like "(2022 Remaster)"
        $remasterPattern = '/\(\d+\s+Remaster\)/i';
    
        // Remove remaster annotations from song and album names
        $songName = preg_replace($remasterPattern, '', $songName);
        $albumName = preg_replace($remasterPattern, '', $albumName);
    
        // Define a regex pattern to remove "(Deluxe Edition)" and "(Deluxe Version)" (case-insensitive)
        $deluxePattern = '/\(deluxe\s+(edition|version)\)/i';
    
        // Remove "(Deluxe Edition)" and "(Deluxe Version)" from album names
        $albumName = preg_replace($deluxePattern, '', $albumName);
    
        // === Added Normalization for "COLLECTORS EDITION." ===
        
        // Define a regex pattern to remove "COLLECTORS EDITION." at the end of album names
        $collectorsPattern = '/\bCollectors\s+Edition\.?$/i';
        
        // Remove "COLLECTORS EDITION." from album names
        $albumName = preg_replace($collectorsPattern, '', $albumName);
    
        // ====================================================
    
        // Trim any extra whitespace that might have resulted from the removal
        $songName = trim($songName);
        $albumName = trim($albumName);
    
        return [
            'song_name'  => $songName,
            'album_name' => $albumName,
        ];
    }    
    
    public function scrobble($request) {
        global $wpdb;
        $data = $request->get_json_params();
        
        // Updated required fields: 'album_name' is no longer required
        $required_fields = ['song_name', 'author'];

        // Check for missing required fields
        foreach ($required_fields as $field) {
            if (!isset($data[$field])) {
                return new WP_Error('missing_field', 'Missing required field: ' . $field, array('status' => 400));
            }
        }

        $table_name = 'song_scrobbles';
        $author = isset($data['author']) ? $data['author'] : null;
        $cover_url = isset($data['cover_url']) ? $data['cover_url'] : null;

        $normalized = $this->normalize_names($data['author'], $data['song_name'], $data['album_name']);
        $song_name  = $normalized['song_name'];
        $album_name = $normalized['album_name'];

        // If 'album_name' is not provided, 'cover_url' must be present
        if (empty($album_name)) {
            if (empty($cover_url)) {
                return new WP_Error(
                    'missing_cover_url', 
                    'Cover URL must be provided if album name is not present', 
                    array('status' => 400)
                );
            }
        } else {
            // If 'album_name' is provided but 'cover_url' is not, attempt to retrieve it from existing entries
            if (empty($cover_url)) {
                // Prepare case-insensitive search
                $author_lower = strtolower($data['author']);
                $album_name_lower = strtolower($album_name);

                // Try to find attachment
                $cover_url = $this->get_attachment_by_artist_album($data['author'], $album_name);
                if (!isset($cover_url)) {
                    // If no matching entry is found, return an error
                    return new WP_Error(
                        'missing_cover_url', 
                        $cover_url, 
                        array('status' => 400)
                    );
                }
            }
        }

        // Insert the scrobble data into the database
        $result = $wpdb->insert(
            $table_name,
            array(
                'song_name'      => $song_name,
                'album_name'     => $album_name, // This can be null
                'cover_url'      => $cover_url,
                'author'         => $data['author'],
                'length_seconds' => isset($data['length_seconds']) ? intval($data['length_seconds']) : null
            ),
            array('%s', '%s', '%s', '%s', '%d')
        );

        // Handle potential database insertion errors
        if ($result === false) {
            return new WP_Error('db_error', 'Error saving scrobble', array('status' => 500));
        }

        // Return a successful response
        return new WP_REST_Response(array('message' => 'Scrobble saved successfully'), 201);
    }

    public function add_admin_menu() {
        add_options_page(
            'Image Scrobble Settings',
            'Image Scrobble',
            'manage_options',
            'image-scrobble-settings',
            array($this, 'settings_page')
        );

        // Add submenu page for uploading attachments
        add_options_page(
            'Upload Album Cover',
            'Upload Album Cover',
            'manage_options',
            'image-scrobble-upload',
            array($this, 'upload_form_page')
        );
    }

    public function upload_form_page() {
        ?>
        <div class="wrap">
            <h1>Upload Album Cover</h1>
            <?php
            if (isset($_GET['message'])) {
                $message = sanitize_text_field($_GET['message']);
                if ($message == 'upload_success') {
                    echo '<div class="updated"><p>Album cover uploaded successfully.</p></div>';
                } elseif ($message == 'duplicate_image') {
                    echo '<div class="error"><p>An image with this content already exists.</p></div>';
                } elseif ($message == 'upload_error') {
                    echo '<div class="error"><p>Error during file upload.</p></div>';
                } elseif ($message == 'not_an_image') {
                    echo '<div class="error"><p>The uploaded file is not a valid image.</p></div>';
                } elseif ($message == 'missing_fields') {
                    echo '<div class="error"><p>Please fill in all required fields.</p></div>';
                } elseif ($message == 'upload_failed') {
                    echo '<div class="error"><p>Failed to upload image.</p></div>';
                } else {
                    echo '<div class="error"><p>An unknown error occurred.</p></div>';
                }
            }
            ?>
            <form method="post" action="<?php echo admin_url('admin-post.php'); ?>" enctype="multipart/form-data">
                <?php wp_nonce_field('image_scrobble_upload', 'image_scrobble_upload_nonce'); ?>
                <input type="hidden" name="action" value="image_scrobble_upload">
                <table class="form-table">
                    <tr valign="top">
                        <th scope="row">Artist</th>
                        <td>
                            <input type="text" name="artist" value="" required />
                        </td>
                    </tr>
                    <tr valign="top">
                        <th scope="row">Album Title</th>
                        <td>
                            <input type="text" name="album_title" value="" required />
                        </td>
                    </tr>
                    <tr valign="top">
                        <th scope="row">Album Cover Image</th>
                        <td>
                            <input type="file" name="album_cover" accept="image/*" required />
                        </td>
                    </tr>
                </table>
                <?php submit_button('Upload Album Cover'); ?>
            </form>
        </div>
        <?php
    }

    public function fetch_and_save_album_cover($artist_name, $album_name) {
        $album_exceptions = [
            'Dios' => [
                'Gasoline EP' => 'Gasoline EP - EP'
            ]
        ];
    
        // Check if there's an exception for this artist and album
        if (isset($album_exceptions[$artist_name]) && 
            isset($album_exceptions[$artist_name][$album_name])) {
            $album_name = $album_exceptions[$artist_name][$album_name];
        }

        // Encode the artist and album name for the URL
        $search_term = urlencode($artist_name . ' ' . $album_name);
    
        // Construct the iTunes Search API URL
        $api_url = "https://itunes.apple.com/search?term={$search_term}&entity=album&limit=10";
    
        // Fetch the API response
        $response = wp_remote_get($api_url);
    
        // Check for errors in the API request
        if (is_wp_error($response)) {
            return new WP_Error(
                'api_request_failed',
                'Failed to connect to the iTunes API.',
                array('status' => 500)
            );
        }
    
        // Decode the JSON response
        $body = wp_remote_retrieve_body($response);
        $data = json_decode($body, true);
    
        // Check if the API returned any results
        if (empty($data['results'])) {
            return new WP_Error(
                'no_results',
                'No album found matching the provided artist and album name.',
                array('status' => 404)
            );
        }
    
        // Initialize a variable to store the matched album
        $matched_album = null;
    
        // Iterate through the results to find an exact match
        foreach ($data['results'] as $album) {
            $api_artist_name = strtolower($album['artistName']);
            $api_album_name = strtolower($album['collectionName']); // or 'collectionCensoredName'
    
            // Normalize the input and API data for comparison
            $input_artist_name = strtolower($artist_name);
            $input_album_name = strtolower($album_name);
    
            // Remove any extra whitespace and special characters
            $api_artist_name = preg_replace('/\s+/', ' ', trim($api_artist_name));
            $api_album_name = preg_replace('/\s+/', ' ', trim($api_album_name));
            $input_artist_name = preg_replace('/\s+/', ' ', trim($input_artist_name));
            $input_album_name = preg_replace('/\s+/', ' ', trim($input_album_name));
    
            // Compare the artist and album names
            if ($api_artist_name === $input_artist_name && $api_album_name === $input_album_name) {
                $matched_album = $album;
                break;
            }
        }
    
        // Check if a matched album was found
        if (!$matched_album) {
            return new WP_Error(
                'no_exact_match',
                'No exact match found for the provided artist and album name.',
                array('status' => 404)
            );
        }
    
        // Get the artwork URL and modify it
        $artwork_url = $matched_album['artworkUrl100'];
        $artwork_url_500 = str_replace('100x100bb', '1000x1000bb', $artwork_url);
    
        // Fetch the image data
        $image_response = wp_remote_get($artwork_url_500);
    
        // Check for errors in the image request
        if (is_wp_error($image_response)) {
            return new WP_Error(
                'image_request_failed',
                'Failed to fetch the album cover image.',
                array('status' => 500)
            );
        }
    
        // Retrieve the image data
        $image_data = wp_remote_retrieve_body($image_response);
    
        // Check if image data was retrieved
        if (empty($image_data)) {
            return new WP_Error(
                'empty_image_data',
                'No image data was returned from the iTunes API.',
                array('status' => 500)
            );
        }
    
        // Generate a hash of the image data to check for duplicates
        $image_hash = md5($image_data);
    
        // Call the provided method to save the image and create an attachment
        return $this->save_image_and_create_attachment(
            $image_data,
            $artist_name,
            $album_name,
            $image_hash,
            $album_name // Passing the album title
        );
    }    

    public function handle_image_upload() {
        // Check if current user has permission
        if (!current_user_can('manage_options')) {
            wp_die('Unauthorized user');
        }

        // Verify nonce
        check_admin_referer('image_scrobble_upload', 'image_scrobble_upload_nonce');

        // Get the posted data
        $artist = sanitize_text_field($_POST['artist']);
        $album_title = sanitize_text_field($_POST['album_title']);

        // Check if required fields are present
        if (empty($artist) || empty($album_title) || empty($_FILES['album_cover'])) {
            wp_redirect(add_query_arg('message', 'missing_fields', wp_get_referer()));
            exit;
        }

        // Handle the uploaded file
        $file = $_FILES['album_cover'];

        // Check for upload errors
        if ($file['error'] !== UPLOAD_ERR_OK) {
            wp_redirect(add_query_arg('message', 'upload_error', wp_get_referer()));
            exit;
        }

        // Check if the file is an image
        $check = getimagesize($file['tmp_name']);
        if ($check === false) {
            wp_redirect(add_query_arg('message', 'not_an_image', wp_get_referer()));
            exit;
        }

        // Read the image data
        $image_data = file_get_contents($file['tmp_name']);

        // Generate a hash of the image data to check for duplicates
        $image_hash = md5($image_data);

        // Check if an image with this hash already exists to prevent duplicates
        $existing_attachment = $this->get_attachment_by_hash($image_hash);
        if ($existing_attachment) {
            wp_redirect(add_query_arg('message', 'duplicate_image', wp_get_referer()));
            exit;
        }

        // Save the image and create attachment (using same logic as create_image)
        $image_url = $this->save_image_and_create_attachment($image_data, $artist, $album_title, $image_hash, $album_title);

        if (is_wp_error($image_url)) {
            wp_redirect(add_query_arg('message', 'upload_failed', wp_get_referer()));
            exit;
        }

        // Redirect back with success message
        wp_redirect(add_query_arg('message', 'upload_success', wp_get_referer()));
        exit;
    }

    public function register_settings() {
        register_setting('image_scrobble_settings', $this->option_name);
    }

    public function settings_page() {
        ?>
        <div class="wrap">
            <h1>Image Scrobble Settings</h1>
            <form method="post" action="options.php">
                <?php
                settings_fields('image_scrobble_settings');
                do_settings_sections('image_scrobble_settings');
                ?>
                <table class="form-table">
                    <tr valign="top">
                        <th scope="row">AUTH_KEY</th>
                        <td>
                            <input type="text" name="<?php echo $this->option_name; ?>" value="<?php echo esc_attr(get_option($this->option_name)); ?>" />
                        </td>
                    </tr>
                </table>
                <?php submit_button(); ?>
            </form>
        </div>
        <?php
    }
}