<?php

namespace PhotoSphereViewer\Includes;

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

/**
 * Shortcode Manager Class
 *
 * Handles database operations for shortcode management
 */
class ShortcodeManager {

    /**
     * Bump this when the table schema changes so maybe_create_table() reruns.
     */
    const DB_VERSION = '1.0';

    private $table_name;

    public function __construct() {
        global $wpdb;
        $this->table_name = $wpdb->prefix . 'psv_shortcodes';

        // Only run dbDelta when the stored version doesn't match.
        add_action('init', array($this, 'maybe_create_table'));

        // AJAX handlers
        add_action('wp_ajax_psv_get_shortcodes', array($this, 'get_shortcodes'));
        add_action('wp_ajax_psv_save_shortcode', array($this, 'save_shortcode'));
        add_action('wp_ajax_psv_delete_shortcode', array($this, 'delete_shortcode'));
        add_action('wp_ajax_psv_get_media_images', array($this, 'get_media_images'));
        add_action('wp_ajax_psv_get_original_image_url', array($this, 'get_original_image_url'));
    }

    /**
     * Create (or upgrade) the custom table, but only when the stored DB
     * version is out of date. Skips the dbDelta() call — and the underlying
     * SHOW TABLES query — on every normal page load.
     */
    public function maybe_create_table() {
        if (get_option('psv_db_version') === self::DB_VERSION) {
            return;
        }

        global $wpdb;

        $charset_collate = $wpdb->get_charset_collate();

        $sql = "CREATE TABLE IF NOT EXISTS {$this->table_name} (
            id int(11) NOT NULL AUTO_INCREMENT,
            name varchar(255) NOT NULL,
            image_id int(11) NOT NULL,
            image_url varchar(500) NOT NULL,
            settings longtext NOT NULL,
            shortcode text NOT NULL,
            created_at datetime DEFAULT CURRENT_TIMESTAMP,
            updated_at datetime DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
            PRIMARY KEY (id),
            KEY updated_at (updated_at)
        ) $charset_collate;";

        require_once ABSPATH . 'wp-admin/includes/upgrade.php';
        dbDelta($sql);

        update_option('psv_db_version', self::DB_VERSION);
    }

    /**
     * Get all shortcodes via AJAX
     */
    public function get_shortcodes() {
        // Verify nonce
        if (! isset($_POST['nonce']) || ! wp_verify_nonce(sanitize_text_field(wp_unslash($_POST['nonce'])), 'psv_admin_nonce')) {
            wp_die('Security check failed');
        }

        global $wpdb;

        $cache_key  = 'psv_all_shortcodes';
        $shortcodes = wp_cache_get($cache_key, 'photo_sphere_viewer');

        if (false === $shortcodes) {
            // phpcs:ignore WordPress.DB.DirectDatabaseQuery.DirectQuery
            $shortcodes = $wpdb->get_results(
                // phpcs:ignore WordPress.DB.PreparedSQL.InterpolatedNotPrepared -- table name is safe
                "SELECT * FROM {$this->table_name} ORDER BY created_at DESC",
                ARRAY_A
            );
            wp_cache_set($cache_key, $shortcodes, 'photo_sphere_viewer');
        }

        // Decode settings for each shortcode
        foreach ($shortcodes as &$shortcode) {
            // Handle escaped JSON data
            $settings_json = $shortcode['settings'];

            // Remove WordPress slashes if present
            if (is_string($settings_json)) {
                $settings_json = wp_unslash($settings_json);
            }

            // Decode JSON
            $decoded_settings = json_decode($settings_json, true);

            // Fallback to empty array if decoding fails
            $shortcode['settings'] = $decoded_settings ?: array();

            // Also clean up the shortcode field
            if (isset($shortcode['shortcode'])) {
                $shortcode['shortcode'] = wp_unslash($shortcode['shortcode']);
            }
        }

        wp_send_json_success($shortcodes);
    }

    /**
     * Save shortcode via AJAX
     */
    public function save_shortcode() {
        // Verify nonce
        if (! isset($_POST['nonce']) || ! wp_verify_nonce(sanitize_text_field(wp_unslash($_POST['nonce'])), 'psv_admin_nonce')) {
            wp_die('Security check failed');
        }

        global $wpdb;

        $id        = isset($_POST['id']) ? intval($_POST['id']) : 0;
        $is_update = ($id > 0);
        $name      = isset($_POST['name']) ? sanitize_text_field(wp_unslash($_POST['name'])) : '';
        $image_id  = isset($_POST['image_id']) ? intval($_POST['image_id']) : 0;
        $image_url = isset($_POST['image_url']) ? esc_url_raw(wp_unslash($_POST['image_url'])) : '';

        // Defensive: when an attachment ID is supplied, prefer the original
        // (un-scaled) URL so the persisted shortcode never references a
        // *-scaled.jpg variant. Honors the per-shortcode opt-out.
        $use_original = true;
        if (isset($_POST['use_original'])) {
            $use_original = sanitize_text_field(wp_unslash($_POST['use_original'])) !== 'no';
        }
        if ($image_id > 0 && $use_original) {
            $resolved = ImageResolver::resolve($image_id, true, 'admin-save');
            if (! empty($resolved)) {
                $image_url = esc_url_raw($resolved);
            }
        }

        // Normalise settings so we always encode a clean associative array
        $raw_settings = isset($_POST['settings']) ? wp_unslash($_POST['settings']) : array(); // phpcs:ignore WordPress.Security.ValidatedSanitizedInput.InputNotSanitized -- JSON decoded and re-encoded below

        // If settings came in as a JSON string, decode it first
        if (is_string($raw_settings)) {
            $decoded = json_decode($raw_settings, true);
            if (is_array($decoded)) {
                $raw_settings = $decoded;
            }
        }

        // Ensure we always encode an array structure for consistent usage in JS
        if (!is_array($raw_settings)) {
            $raw_settings = array();
        }

        $settings  = wp_json_encode($raw_settings, JSON_UNESCAPED_SLASHES);
        $shortcode = isset($_POST['shortcode']) ? sanitize_text_field(wp_unslash($_POST['shortcode'])) : '';

        if ($is_update) {
            // phpcs:ignore WordPress.DB.DirectDatabaseQuery.DirectQuery, WordPress.DB.DirectDatabaseQuery.NoCaching -- custom table write
            $result = $wpdb->update(
                $this->table_name,
                array(
                    'name'      => $name,
                    'image_id'  => $image_id,
                    'image_url' => $image_url,
                    'settings'  => $settings,
                    'shortcode' => $shortcode,
                ),
                array('id' => $id),
                array('%s', '%d', '%s', '%s', '%s'),
                array('%d')
            );
        } else {
            // phpcs:ignore WordPress.DB.DirectDatabaseQuery.DirectQuery -- custom table write
            $result = $wpdb->insert(
                $this->table_name,
                array(
                    'name'      => $name,
                    'image_id'  => $image_id,
                    'image_url' => $image_url,
                    'settings'  => $settings,
                    'shortcode' => $shortcode,
                ),
                array('%s', '%d', '%s', '%s', '%s')
            );
            $id = $wpdb->insert_id;
        }

        // Invalidate cache after save
        wp_cache_delete('psv_all_shortcodes', 'photo_sphere_viewer');
        wp_cache_delete('psv_shortcode_' . $id, 'photo_sphere_viewer');

        if ($result !== false) {
            wp_send_json_success(array(
                'id'      => $id,
                'message' => $is_update
                    ? __('Shortcode updated successfully', 'photo-sphere-viewer')
                    : __('Shortcode created successfully', 'photo-sphere-viewer'),
            ));
        } else {
            wp_send_json_error(__('Failed to save shortcode', 'photo-sphere-viewer'));
        }
    }

    /**
     * Delete shortcode via AJAX
     */
    public function delete_shortcode() {
        // Verify nonce
        if (! isset($_POST['nonce']) || ! wp_verify_nonce(sanitize_text_field(wp_unslash($_POST['nonce'])), 'psv_admin_nonce')) {
            wp_die('Security check failed');
        }

        global $wpdb;

        $id = isset($_POST['id']) ? intval($_POST['id']) : 0;

        // phpcs:ignore WordPress.DB.DirectDatabaseQuery.DirectQuery, WordPress.DB.DirectDatabaseQuery.NoCaching -- custom table delete
        $result = $wpdb->delete(
            $this->table_name,
            array('id' => $id),
            array('%d')
        );

        // Invalidate cache after delete
        wp_cache_delete('psv_all_shortcodes', 'photo_sphere_viewer');
        wp_cache_delete('psv_shortcode_' . $id, 'photo_sphere_viewer');

        if ($result !== false) {
            wp_send_json_success(__('Shortcode deleted successfully', 'photo-sphere-viewer'));
        } else {
            wp_send_json_error(__('Failed to delete shortcode', 'photo-sphere-viewer'));
        }
    }

    /**
     * Get media library images via AJAX
     */
    public function get_media_images() {
        // Verify nonce
        if (! isset($_POST['nonce']) || ! wp_verify_nonce(sanitize_text_field(wp_unslash($_POST['nonce'])), 'psv_admin_nonce')) {
            wp_die('Security check failed');
        }

        $args = array(
            'post_type' => 'attachment',
            'post_mime_type' => 'image',
            'post_status' => 'inherit',
            'posts_per_page' => 50,
            'orderby' => 'date',
            'order' => 'DESC'
        );

        $images = get_posts($args);
        $formatted_images = array();

        foreach ($images as $image) {
            $image_data = wp_get_attachment_image_src($image->ID, 'medium');
            $full_image = wp_get_attachment_image_src($image->ID, 'full');
            // Prefer the un-scaled original so the picker grid hands the
            // viewer a true-resolution panorama by default.
            $original_url = ImageResolver::resolve($image->ID, true, 'media-list');

            if ($image_data) {
                $formatted_images[] = array(
                    'id' => $image->ID,
                    'title' => $image->post_title,
                    'url' => $original_url ?: $full_image[0],
                    'scaled_url' => $full_image[0],
                    'original_url' => $original_url ?: $full_image[0],
                    'thumbnail' => $image_data[0],
                    'filename' => basename($image->guid),
                    'alt' => get_post_meta($image->ID, '_wp_attachment_image_alt', true),
                );
            }
        }

        wp_send_json_success($formatted_images);
    }

    /**
     * Resolve a single attachment ID to its original (un-scaled) URL.
     *
     * Used by the admin React UI immediately after a wp.media selection so
     * the form/shortcode is populated with the full-resolution panorama URL
     * (not the *-scaled.jpg copy WordPress generates for big uploads).
     */
    public function get_original_image_url() {
        if (! isset($_POST['nonce']) || ! wp_verify_nonce(sanitize_text_field(wp_unslash($_POST['nonce'])), 'psv_admin_nonce')) {
            wp_send_json_error('Security check failed', 403);
        }
        if (! current_user_can('upload_files')) {
            wp_send_json_error('Insufficient permissions', 403);
        }

        $attachment_id = isset($_POST['attachment_id']) ? intval($_POST['attachment_id']) : 0;
        if ($attachment_id <= 0) {
            wp_send_json_error('Invalid attachment id');
        }

        $original = ImageResolver::resolve($attachment_id, true, 'ajax-resolve');
        $scaled   = wp_get_attachment_image_src($attachment_id, 'full');
        $thumb    = wp_get_attachment_image_src($attachment_id, 'medium');
        $meta     = wp_get_attachment_metadata($attachment_id);

        wp_send_json_success(array(
            'id'            => $attachment_id,
            'url'           => $original ?: (is_array($scaled) ? $scaled[0] : ''),
            'original_url'  => $original ?: (is_array($scaled) ? $scaled[0] : ''),
            'scaled_url'    => is_array($scaled) ? $scaled[0] : '',
            'thumbnail'     => is_array($thumb) ? $thumb[0] : '',
            'filename'      => basename(get_attached_file($attachment_id) ?: ''),
            'width'         => isset($meta['width']) ? (int) $meta['width'] : 0,
            'height'        => isset($meta['height']) ? (int) $meta['height'] : 0,
        ));
    }

    /**
     * Get shortcode by ID
     */
    public function get_shortcode_by_id($id) {
        global $wpdb;

        $cache_key  = 'psv_shortcode_' . intval($id);
        $shortcode  = wp_cache_get($cache_key, 'photo_sphere_viewer');

        if (false === $shortcode) {
            // phpcs:ignore WordPress.DB.DirectDatabaseQuery.DirectQuery
            $shortcode = $wpdb->get_row(
                $wpdb->prepare(
                    // phpcs:ignore WordPress.DB.PreparedSQL.InterpolatedNotPrepared -- table name is safe
                    "SELECT * FROM {$this->table_name} WHERE id = %d",
                    $id
                ),
                ARRAY_A
            );
            wp_cache_set($cache_key, $shortcode, 'photo_sphere_viewer');
        }

        if ($shortcode) {
            $shortcode['settings'] = json_decode($shortcode['settings'], true);
        }

        return $shortcode;
    }

    /**
     * Get all shortcodes (for admin display)
     */
    public function get_all_shortcodes() {
        global $wpdb;

        $cache_key  = 'psv_all_shortcodes';
        $shortcodes = wp_cache_get($cache_key, 'photo_sphere_viewer');

        if (false === $shortcodes) {
            // phpcs:ignore WordPress.DB.DirectDatabaseQuery.DirectQuery
            $shortcodes = $wpdb->get_results(
                // phpcs:ignore WordPress.DB.PreparedSQL.InterpolatedNotPrepared -- table name is safe
                "SELECT * FROM {$this->table_name} ORDER BY created_at DESC",
                ARRAY_A
            );
            wp_cache_set($cache_key, $shortcodes, 'photo_sphere_viewer');
        }

        foreach ($shortcodes as &$shortcode) {
            $shortcode['settings'] = json_decode($shortcode['settings'], true);
        }

        return $shortcodes;
    }
}
