<?php

/*
Plugin Name: Sacconi iCal Sync – Advanced Calendars for Holiday Rentals
Description: Professional availability calendars with advanced iCal intersection (up to 15 links), manual dates management, and ultra-fast SQL engine.
Version: 1.1.8
Tested up to: 7.0
Author: Sacconi
Text Domain: sacconi-ical-sync
Domain Path: /languages
License: GPLv2 or later
License URI: https://www.gnu.org/licenses/gpl-2.0.html
*/
if ( !defined( 'ABSPATH' ) ) {
    exit;
}
if ( !function_exists( 'sis_fs' ) ) {
    // Create a helper function for easy SDK access.
    function sis_fs() {
        global $sis_fs;
        if ( !isset( $sis_fs ) ) {
            // Include Freemius SDK.
            require_once dirname( __FILE__ ) . '/vendor/freemius/start.php';
            $sis_fs = fs_dynamic_init( array(
                'id'               => '27928',
                'slug'             => 'sacconi-ical-sync',
                'type'             => 'plugin',
                'public_key'       => 'pk_503b8a913b5457d4ff6caa3094e56',
                'is_premium'       => false,
                'premium_suffix'   => 'Professional',
                'has_addons'       => false,
                'has_paid_plans'   => true,
                'is_org_compliant' => true,
                'menu'             => array(
                    'slug'       => 'sacconi-settings',
                    'first-path' => 'admin.php?page=sacconi-settings',
                    'support'    => false,
                ),
                'is_live'          => true,
            ) );
        }
        return $sis_fs;
    }

    // Inizializza Freemius
    sis_fs();
    // ⚡ AGGIUNGI QUESTA RIGA QUI SOTTO PER BLOCCARE IL PREZZO MENSILE ⚡
    sis_fs()->add_filter( 'pricing/show_annual_in_monthly', '__return_false' );
    // Segnala che l'SDK è stato caricato correttamente
    do_action( 'sis_fs_loaded' );
}
/**
 * CLASSE 1: GESTORE MATEMATICO INTERVALLI
 */
class SacconiPlugin_IntervalManager {
    private $intervals = [];

    public function addInterval( DateTime $start, DateTime $end ) {
        if ( $start > $end ) {
            return;
        }
        $this->intervals[] = [
            'start' => $start,
            'end'   => $end,
        ];
        $this->mergeOverlapping();
    }

    public function intersect( $otherManager ) {
        $intersected = [];
        foreach ( $this->intervals as $i1 ) {
            foreach ( $otherManager->getIntervals() as $i2 ) {
                $start = max( $i1['start'], $i2['start'] );
                $end = min( $i1['end'], $i2['end'] );
                if ( $start <= $end ) {
                    $intersected[] = [
                        'start' => $start,
                        'end'   => $end,
                    ];
                }
            }
        }
        $this->intervals = $intersected;
        $this->mergeOverlapping();
    }

    private function mergeOverlapping() {
        if ( empty( $this->intervals ) ) {
            return;
        }
        usort( $this->intervals, function ( $a, $b ) {
            return $a['start'] <=> $b['start'];
        } );
        $merged = [];
        $current = array_shift( $this->intervals );
        foreach ( $this->intervals as $next ) {
            $potential_end = ( clone $current['end'] )->modify( '+1 second' );
            if ( $next['start'] <= $potential_end ) {
                if ( $next['end'] > $current['end'] ) {
                    $current['end'] = $next['end'];
                }
            } else {
                $merged[] = $current;
                $current = $next;
            }
        }
        $merged[] = $current;
        $this->intervals = $merged;
    }

    public function getIntervals() {
        return $this->intervals;
    }

}

/**
 * CLASSE 2: PLUGIN PRINCIPALE
 */
class SacconiPlugin_Calendar {
    public function __construct() {
        register_activation_hook( __FILE__, [$this, 'sacconiplugin_create_db'] );
        add_action( 'rest_api_init', [$this, 'sacconiplugin_register_rest_routes'] );
        add_action( 'admin_enqueue_scripts', [$this, 'sacconiplugin_admin_assets'] );
        add_action( 'wp_enqueue_scripts', [$this, 'sacconiplugin_frontend_assets'] );
        add_action( 'add_meta_boxes', [$this, 'sacconiplugin_add_metabox'] );
        add_shortcode( 'sacconi_calendar', [$this, 'sacconiplugin_frontend_calendar'] );
        // --- ⚡ NUOVI NOMI AZIONI AJAX (Prefissati per WP.ORG) ⚡ ---
        // 1. Salvataggio manuale dal box del plugin
        add_action( 'wp_ajax_sacconi_sync_manual_save', [$this, 'sacconiplugin_ajax_manual_sync_handler'] );
        // 3. Navigazione AJAX del calendario (Frontend)
        add_action( 'wp_ajax_sacconi_sync_nav_cal', [$this, 'sacconi_nav_cal'] );
        add_action( 'wp_ajax_nopriv_sacconi_sync_nav_cal', [$this, 'sacconi_nav_cal'] );
        // --- IMPOSTAZIONI E LOGICHE DI SISTEMA ---
        add_action( 'admin_menu', [$this, 'sacconiplugin_add_settings_menu'] );
        add_action( 'admin_init', [$this, 'sacconiplugin_register_settings'] );
        add_action( 'send_headers', [$this, 'sacconiplugin_browser_headers'] );
        add_action( 'init', [$this, 'sacconiplugin_register_gutenberg_block'] );
        add_action(
            'transition_post_status',
            [$this, 'sacconi_clean_on_status_change'],
            10,
            3
        );
        add_action(
            'update_option_sacconi_settings',
            [$this, 'sacconiplugin_purge_all_cache'],
            10,
            2
        );
    }

    // =========================================================================
    // CONTROLLO LICENZA (SENTINELLA)
    // =========================================================================
    /**
     * CONTROLLO LICENZA (Sblocca automaticamente i tuoi siti personali)
     */
    public function is_pro() {
        // Recuperiamo l'indirizzo del sito attuale
        $host = ( isset( $_SERVER['HTTP_HOST'] ) ? sanitize_text_field( wp_unslash( $_SERVER['HTTP_HOST'] ) ) : '' );
        // ⚡ LISTA BIANCA: Sblocca SacconiCase e RitrattiAolio ⚡
        if ( strpos( $host, 'sacconicase.com' ) !== false || strpos( $host, 'ritrattiaolio.it' ) !== false ) {
            return true;
        }
        // Per tutti gli altri siti (clienti reali), usa il controllo di Freemius
        if ( !function_exists( 'sis_fs' ) ) {
            return false;
        }
        return sis_fs()->is_plan( 'professional' ) || sis_fs()->can_use_premium_code();
    }

    private function count_active_calendars() {
        global $wpdb;
        $table = $wpdb->prefix . 'sacconiplugin_availability';
        return (int) $wpdb->get_var( "SELECT COUNT(DISTINCT post_id) FROM {$wpdb->prefix}sacconiplugin_availability" );
        // phpcs:ignore WordPress.DB.DirectDatabaseQuery
    }

    /**
     * Invia header al browser per evitare che la cache locale diventi "stantia"
     * Questa versione è la più completa e sicura.
     */
    public function sacconiplugin_browser_headers() {
        if ( !is_admin() && is_singular( 'post' ) ) {
            header( "Cache-Control: no-cache, must-revalidate, max-age=0" );
            header( "Pragma: no-cache" );
            header( "Expires: Wed, 11 Jan 1984 05:00:00 GMT" );
        }
    }

    /**
     * Registra il blocco Gutenberg lato server
     */
    public function sacconiplugin_register_gutenberg_block() {
        register_block_type( 'sacconi-ical-sync/calendar-block', [
            'render_callback' => [$this, 'sacconiplugin_frontend_calendar'],
        ] );
    }

    // =========================================================================
    // SEZIONE 1: API PUBBLICHE (Per sviluppatori esterni)
    // =========================================================================
    /**
     * Ottiene l'elenco degli ID degli alloggi OCCUPATI in un certo periodo.
     * Metodo statico richiamabile con: SacconiPlugin_Calendar::get_occupied_ids($from, $to)
     */
    public static function get_occupied_ids( $from, $to ) {
        global $wpdb;
        $table = $wpdb->prefix . 'sacconiplugin_availability';
        // 1. Protezione contro valori NULL (evita il crash su PHP 8.1+)
        $from = ( $from !== null ? (string) $from : '' );
        $to = ( $to !== null ? (string) $to : '' );
        if ( empty( $from ) || empty( $to ) ) {
            return [];
        }
        try {
            $d1 = ( strpos( $from, '/' ) !== false ? DateTime::createFromFormat( 'd/m/Y', $from ) : new DateTime($from) );
            $d2 = ( strpos( $to, '/' ) !== false ? DateTime::createFromFormat( 'd/m/Y', $to ) : new DateTime($to) );
            if ( !$d1 || !$d2 ) {
                return [];
            }
            $start_sql = $d1->format( 'Y-m-d' );
            $end_sql = $d2->format( 'Y-m-d' );
            // 2. Query compatibile (senza %i) per massima sicurezza su ogni server
            return $wpdb->get_col( $wpdb->prepare( 
                // phpcs:ignore WordPress.DB.DirectDatabaseQuery
                "SELECT DISTINCT post_id FROM {$wpdb->prefix}sacconiplugin_availability WHERE start_date < %s AND end_date > %s",
                $end_sql,
                $start_sql
             ) );
        } catch ( Exception $e ) {
            return [];
        }
    }

    /**
     * Verifica se un singolo alloggio è disponibile in un certo periodo.
     */
    public static function is_available( $post_id, $from, $to ) {
        $occupied = self::get_occupied_ids( $from, $to );
        return !in_array( (int) $post_id, $occupied );
    }

    // --- IMPOSTAZIONI ---
    public function sacconiplugin_add_settings_menu() {
        add_menu_page(
            'Sacconi Sync',
            'Sacconi Sync',
            'manage_options',
            'sacconi-settings',
            [$this, 'sacconiplugin_settings_page'],
            'dashicons-calendar-alt'
        );
    }

    public function sacconiplugin_register_settings() {
        register_setting( 'sacconi_settings_group', 'sacconi_settings', array(
            'sanitize_callback' => array($this, 'sacconi_sanitize_settings'),
        ) );
    }

    /**
     * Pulisce e convalida i dati (CON DEBUG VISIVO)
     */
    public function sacconi_sanitize_settings( $input ) {
        $new_input = array();
        if ( isset( $input['title'] ) ) {
            $new_input['title'] = sanitize_text_field( $input['title'] );
        }
        if ( isset( $input['title_type'] ) ) {
            $new_input['title_type'] = sanitize_text_field( $input['title_type'] );
        }
        if ( isset( $input['font'] ) ) {
            $new_input['font'] = sanitize_text_field( $input['font'] );
        }
        if ( isset( $input['color_title'] ) ) {
            $new_input['color_title'] = sanitize_hex_color( $input['color_title'] );
        }
        if ( isset( $input['color_free'] ) ) {
            $new_input['color_free'] = sanitize_hex_color( $input['color_free'] );
        }
        if ( isset( $input['color_occ'] ) ) {
            $new_input['color_occ'] = sanitize_hex_color( $input['color_occ'] );
        }
        if ( isset( $input['shape'] ) ) {
            $new_input['shape'] = sanitize_text_field( $input['shape'] );
        }
        if ( isset( $input['legend'] ) ) {
            $new_input['legend'] = sanitize_text_field( $input['legend'] );
        }
        if ( isset( $input['months'] ) ) {
            $new_input['months'] = absint( $input['months'] );
        }
        if ( isset( $input['back_month'] ) ) {
            $new_input['back_month'] = absint( $input['back_month'] );
        }
        if ( isset( $input['stay_days'] ) ) {
            $new_input['stay_days'] = absint( $input['stay_days'] );
        }
        if ( isset( $input['front_logic'] ) ) {
            $new_input['front_logic'] = sanitize_text_field( $input['front_logic'] );
        }
        $new_input['last_updated'] = time();
        $this->sacconiplugin_purge_all_cache();
        return $new_input;
    }

    public function sacconiplugin_settings_page() {
        $options = get_option( 'sacconi_settings' );
        $type = $options['title_type'] ?? 'availability';
        $legend = $options['legend'] ?? 'none';
        // ⚡ Recuperiamo l'opzione legenda
        // Usiamo filter_input invece di $_GET per massima sicurezza e compatibilità
        if ( null !== filter_input( INPUT_GET, 'settings-updated' ) ) {
            echo '<div class="updated notice is-dismissible"><p>✅ ' . esc_html__( 'Settings saved successfully!', 'sacconi-ical-sync' ) . '</p></div>';
        }
        ?>
        <div class="wrap">
            <h1><?php 
        esc_html_e( 'Sacconi Sync Settings', 'sacconi-ical-sync' );
        ?></h1>
            
            <!-- ⚡ NAVIGAZIONE DELLE TAB -->
            <h2 class="nav-tab-wrapper">
                <a href="#tab-grafica" class="nav-tab nav-tab-active sacconi-tab-link"><?php 
        esc_html_e( 'Graphics', 'sacconi-ical-sync' );
        ?></a>
                <a href="#tab-altre" class="nav-tab sacconi-tab-link"><?php 
        esc_html_e( 'Other Settings', 'sacconi-ical-sync' );
        ?></a>
            </h2>

            <form method="post" action="options.php">
                <?php 
        settings_fields( 'sacconi_settings_group' );
        ?>
                
                <!-- ⚡ TAB 1: GRAFICA -->
                <div id="tab-grafica" class="sacconi-tab-content">
                    <table class="form-table">
                        <tr>
                            <th><?php 
        esc_html_e( 'Calendar Title', 'sacconi-ical-sync' );
        ?></th>
                            <td>
                                <label><input type="radio" name="sacconi_settings[title_type]" value="availability" <?php 
        checked( $type, 'availability' );
        ?>> <?php 
        esc_html_e( 'Availability', 'sacconi-ical-sync' );
        ?></label><br>
                                <label><input type="radio" name="sacconi_settings[title_type]" value="calendars" <?php 
        checked( $type, 'calendars' );
        ?>> <?php 
        esc_html_e( 'Calendars', 'sacconi-ical-sync' );
        ?></label><br>
                                <label><input type="radio" name="sacconi_settings[title_type]" value="none" <?php 
        checked( $type, 'none' );
        ?>> <?php 
        echo esc_html_x( 'None', 'Calendar Title', 'sacconi-ical-sync' );
        ?></label>
                            </td>
                        </tr>
                        <tr>
                            <th><?php 
        esc_html_e( 'Font Family', 'sacconi-ical-sync' );
        ?></th>
                            <td><input type="text" name="sacconi_settings[font]" value="<?php 
        echo esc_attr( $options['font'] ?? "'Quicksand', sans-serif" );
        ?>" class="regular-text"></td>
                        </tr>
                        <tr>
                            <th><?php 
        esc_html_e( 'Title Color', 'sacconi-ical-sync' );
        ?></th>
                            <td><input type="text" name="sacconi_settings[color_title]" value="<?php 
        echo esc_attr( $options['color_title'] ?? '#006288' );
        ?>" class="sacconi-color-picker"></td>
                        </tr>			
                        <tr>
                            <th><?php 
        esc_html_e( 'Free Dates Color', 'sacconi-ical-sync' );
        ?></th>
                            <td><input type="text" name="sacconi_settings[color_free]" value="<?php 
        echo esc_attr( $options['color_free'] ?? '#b8f5b2' );
        ?>" class="sacconi-color-picker"></td>
                        </tr>
                        <tr>
                            <th><?php 
        esc_html_e( 'Occupied Dates Color', 'sacconi-ical-sync' );
        ?></th>
                            <td><input type="text" name="sacconi_settings[color_occ]" value="<?php 
        echo esc_attr( $options['color_occ'] ?? '#e65c00' );
        ?>" class="sacconi-color-picker"></td>
                        </tr>
                        <tr>
                            <th><?php 
        esc_html_e( 'Day Shape', 'sacconi-ical-sync' );
        ?></th>
                            <td>
                                <select name="sacconi_settings[shape]">
                                    <option value="circle" <?php 
        selected( $options['shape'] ?? 'circle', 'circle' );
        ?>><?php 
        esc_html_e( 'Circle', 'sacconi-ical-sync' );
        ?></option>
                                    <option value="large_circle" <?php 
        selected( $options['shape'] ?? 'circle', 'large_circle' );
        ?>><?php 
        esc_html_e( 'Large Circle', 'sacconi-ical-sync' );
        ?></option>
                                    <option value="square" <?php 
        selected( $options['shape'] ?? 'circle', 'square' );
        ?>><?php 
        esc_html_e( 'Square', 'sacconi-ical-sync' );
        ?></option>
                                </select>
                            </td>
                        </tr>
                        <!-- ⚡ LA NUOVA OPZIONE: LEGENDA -->
                        <tr>
                            <th><?php 
        esc_html_e( 'Legend', 'sacconi-ical-sync' );
        ?></th>
                            <td>
                                <label><input type="radio" name="sacconi_settings[legend]" value="none" <?php 
        checked( $legend, 'none' );
        ?>> <?php 
        echo esc_html_x( 'None', 'Legend Position', 'sacconi-ical-sync' );
        ?></label><br>
                                <label><input type="radio" name="sacconi_settings[legend]" value="sopra" <?php 
        checked( $legend, 'sopra' );
        ?>> <?php 
        esc_html_e( 'Above Calendar', 'sacconi-ical-sync' );
        ?></label><br>
                                <label><input type="radio" name="sacconi_settings[legend]" value="sotto" <?php 
        checked( $legend, 'sotto' );
        ?>> <?php 
        esc_html_e( 'Below Calendar', 'sacconi-ical-sync' );
        ?></label>
                            </td>
                        </tr>
                    </table>
                </div>

                <!-- ⚡ TAB 2: ALTRE IMPOSTAZIONI (Nascosta di default) -->
                <div id="tab-altre" class="sacconi-tab-content" style="display:none;">
                    <table class="form-table">
                        <tr>
                            <th><?php 
        esc_html_e( 'Months to show', 'sacconi-ical-sync' );
        ?></th>
                            <td><input type="number" name="sacconi_settings[months]" value="<?php 
        echo esc_attr( $options['months'] ?? '3' );
        ?>"></td>
                        </tr>
                        <tr>
                            <th><?php 
        esc_html_e( 'Backend Default Month', 'sacconi-ical-sync' );
        ?></th>
                            <td>
                                <select name="sacconi_settings[back_month]">
                                    <?php 
        $months = [
            99 => __( 'Current Month', 'sacconi-ical-sync' ),
            0  => __( 'January', 'sacconi-ical-sync' ),
            1  => __( 'February', 'sacconi-ical-sync' ),
            2  => __( 'March', 'sacconi-ical-sync' ),
            3  => __( 'April', 'sacconi-ical-sync' ),
            4  => __( 'May', 'sacconi-ical-sync' ),
            5  => __( 'June', 'sacconi-ical-sync' ),
            6  => __( 'July', 'sacconi-ical-sync' ),
            7  => __( 'August', 'sacconi-ical-sync' ),
            8  => __( 'September', 'sacconi-ical-sync' ),
            9  => __( 'October', 'sacconi-ical-sync' ),
            10 => __( 'November', 'sacconi-ical-sync' ),
            11 => __( 'December', 'sacconi-ical-sync' ),
        ];
        foreach ( $months as $num => $name ) {
            echo '<option value="' . esc_attr( $num ) . '" ' . selected( $options['back_month'] ?? 6, $num, false ) . '>' . esc_html( $name ) . '</option>';
        }
        ?>
                                </select>
                            </td>
                        </tr>
                        <tr>
                            <th><?php 
        esc_html_e( 'Frontend Starting Month', 'sacconi-ical-sync' );
        ?></th>
                            <td>
                                <label><input type="radio" name="sacconi_settings[front_logic]" value="default" <?php 
        checked( $options['front_logic'] ?? 'default', 'default' );
        ?>> <?php 
        esc_html_e( 'Default (Seasonal)', 'sacconi-ical-sync' );
        ?></label><br>
                                <label><input type="radio" name="sacconi_settings[front_logic]" value="match" <?php 
        checked( $options['front_logic'] ?? 'default', 'match' );
        ?>> <?php 
        esc_html_e( 'Align with Backend Setting', 'sacconi-ical-sync' );
        ?></label>
                                <p class="description"><?php 
        esc_html_e( 'Default: Automatically starts in June. If today is October or later, it jumps to June of next year. Align: Uses the month selected in "Backend Default Month".', 'sacconi-ical-sync' );
        ?></p>
                            </td>
                        </tr>
                        <tr>
                            <th><?php 
        esc_html_e( 'Default Stay (Days)', 'sacconi-ical-sync' );
        ?></th>
                            <td><input type="number" name="sacconi_settings[stay_days]" value="<?php 
        echo esc_attr( $options['stay_days'] ?? '7' );
        ?>" min="0" max="30"></td>
                        </tr>
                    </table>
                </div>

                <?php 
        submit_button();
        ?>
            </form>
        </div>
        <?php 
    }

    // --- ENDPOINT EXPORT REST API ---
    public function sacconiplugin_register_rest_routes() {
        register_rest_route( 'sacconi-ical-sync/v1', '/export/(?P<id>\\d+)', [
            'methods'             => 'GET',
            'callback'            => [$this, 'sacconiplugin_rest_export_ical'],
            'permission_callback' => '__return_true',
        ] );
    }

    public function sacconiplugin_rest_export_ical( $request ) {
        $post_id = intval( $request->get_param( 'id' ) );
        if ( get_post_type( $post_id ) !== 'post' ) {
            return new WP_Error('invalid_id', esc_html__( 'Invalid ID', 'sacconi-ical-sync' ), [
                'status' => 404,
            ]);
        }
        global $wpdb;
        $booked = $wpdb->get_results( $wpdb->prepare( "SELECT start_date, end_date FROM {$wpdb->prefix}sacconiplugin_availability WHERE post_id = %d", $post_id ) );
        // phpcs:ignore WordPress.DB.DirectDatabaseQuery
        if ( ob_get_length() ) {
            ob_clean();
        }
        header( 'Content-Type: text/calendar; charset=utf-8' );
        header( 'Content-Disposition: attachment; filename="calendar-' . $post_id . '.ics"' );
        echo "BEGIN:VCALENDAR\r\nVERSION:2.0\r\nPRODID:-//SacconiPlugin//IT\r\nMETHOD:PUBLISH\r\n";
        foreach ( $booked as $row ) {
            $start = gmdate( 'Ymd', strtotime( $row->start_date ) );
            $end = gmdate( 'Ymd', strtotime( $row->end_date ) );
            $uid = esc_attr( uniqid() ) . '@' . (( isset( $_SERVER['HTTP_HOST'] ) ? esc_attr( sanitize_text_field( wp_unslash( $_SERVER['HTTP_HOST'] ) ) ) : 'localhost' ));
            $summary = esc_html__( 'Occupied', 'sacconi-ical-sync' );
            echo "BEGIN:VEVENT\r\n";
            echo "UID:" . esc_html( $uid ) . "\r\n";
            echo "DTSTART;VALUE=DATE:" . esc_html( $start ) . "\r\n";
            echo "DTEND;VALUE=DATE:" . esc_html( $end ) . "\r\n";
            echo "SUMMARY:" . esc_html( $summary ) . "\r\n";
            echo "END:VEVENT\r\n";
        }
        echo "END:VCALENDAR";
        exit;
    }

    public function sacconiplugin_frontend_assets() {
        // --- ⚡ RECUPERO VERSIONE DINAMICA PER CACHE BUSTING ⚡ ---
        $options = get_option( 'sacconi_settings' );
        // Se non esiste ancora l'ultimo aggiornamento, usiamo la versione base del plugin
        $v = ( isset( $options['last_updated'] ) ? $options['last_updated'] : '1.0.4' );
        // 2. Carica il nuovo CSS esterno (USIAMO LA VERSIONE DINAMICA $v)
        wp_enqueue_style(
            'sacconi-main-style',
            plugin_dir_url( __FILE__ ) . 'assets/css/sacconi-style.css',
            array(),
            $v
        );
        // 3. Iniezione dinamica delle Variabili CSS (Bypass regole WP.org)
        $options = get_option( 'sacconi_settings' );
        $color_free = $options['color_free'] ?? '#b8f5b2';
        $color_occ = $options['color_occ'] ?? '#e65c00';
        $shape = $options['shape'] ?? 'circle';
        $radius = ( $shape === 'square' ? '0px' : '50%' );
        $custom_css = "\r\n            :root { \r\n                --s-free: " . esc_attr( $color_free ) . "; \r\n                --s-occ: " . esc_attr( $color_occ ) . "; \r\n                --s-radius: " . esc_attr( $radius ) . "; \r\n            }\r\n\t\t\t.sacconi-cal-ajax-container { position: relative; } /* Serve per centrare i pallini */\r\n            .sacconi-loader-overlay {\r\n                position: absolute; top: 50%; left: 50%; transform: translate(-50%, -50%);\r\n                display: flex; gap: 6px; z-index: 10;\r\n            }\r\n            .sacconi-loader-overlay .s-dot {\r\n                width: 10px; height: 10px; border-radius: 50%; background-color: #333; /* nero morbido */\r\n                animation: sacconi-bounce 1.4s infinite ease-in-out both;\r\n            }\r\n            .sacconi-loader-overlay .s-dot:nth-child(1) { animation-delay: -0.32s; }\r\n            .sacconi-loader-overlay .s-dot:nth-child(2) { animation-delay: -0.16s; }\r\n            @keyframes sacconi-bounce {\r\n                0%, 80%, 100% { transform: scale(0); }\r\n                40% { transform: scale(1); }\r\n            }\t\t\t\r\n\t\t\t";
        // ⚡ LA MAGIA DEL PALLINO GRANDE: Azzeriamo gli spazi della griglia!
        if ( $shape === 'large_circle' ) {
            $custom_css .= "\r\n            .s-custom-cal .s-d-grid { \r\n                /* 🧪 FAI QUI I TUOI ESPERIMENTI: Prova 1px, 2px o 3px per trovare la distanza perfetta! */\r\n                gap: 1px !important; \r\n            }\r\n            .s-custom-cal .s-day { \r\n                margin: 0 !important; \r\n                width: 100% !important; \r\n                border-radius: 50% !important;\r\n                /* (Abbiamo tolto il transform: scale così non si sormontano più) */\r\n            }\r\n            ";
        }
        wp_add_inline_style( 'sacconi-main-style', $custom_css );
        // 4. Carica il JS esterno (Navigazione + Swipe)
        wp_enqueue_script(
            'sacconi-main-js',
            plugin_dir_url( __FILE__ ) . 'assets/js/sacconi-frontend.js',
            array('jquery'),
            '1.0.4',
            true
        );
        wp_localize_script( 'sacconi-main-js', 'sacconi_data', array(
            'ajax_url' => admin_url( 'admin-ajax.php' ),
            'nonce'    => wp_create_nonce( 'sacconi_sync_nonce' ),
        ) );
    }

    public function sacconiplugin_admin_assets( $hook ) {
        // Assets per la pagina impostazioni del plugin
        if ( $hook === 'toplevel_page_sacconi-settings' ) {
            wp_enqueue_style( 'wp-color-picker' );
            wp_enqueue_script( 'wp-color-picker' );
            // ⚡ Iniettiamo sia l'avvio del Color Picker che la logica delle Tab!
            $settings_js = "\r\n                jQuery(document).ready(function(\$) {\r\n                    // Inizializza i colori\r\n                    \$('.sacconi-color-picker').wpColorPicker();\r\n                    \r\n                    // Logica delle Tab\r\n                    \$('.sacconi-tab-link').click(function(e) {\r\n                        e.preventDefault();\r\n                        \$('.sacconi-tab-link').removeClass('nav-tab-active');\r\n                        \$(this).addClass('nav-tab-active');\r\n                        \$('.sacconi-tab-content').hide();\r\n                        \$(\$(this).attr('href')).show();\r\n                    });\r\n                });\r\n            ";
            wp_add_inline_script( 'wp-color-picker', $settings_js );
        }
        // Carica il resto solo nelle pagine di modifica Post
        if ( !in_array( $hook, ['post.php', 'post-new.php'] ) ) {
            return;
        }
        // 1. Caricamento librerie LOCALI (Necessarie per approvazione WP.org)
        wp_enqueue_style(
            'sacconi-flatpickr-css',
            plugin_dir_url( __FILE__ ) . 'assets/flatpickr.min.css',
            array(),
            '4.6.13'
        );
        // ⚡ FIX WP 7.0: Blindatura per altezze, line-height (per lettere come j, g, p) e stili input
        $flatpickr_fix_wp7 = "\r\n            .flatpickr-calendar .flatpickr-months,\r\n            .flatpickr-calendar .flatpickr-month {\r\n                height: auto !important;\r\n                min-height: 40px !important;\r\n                overflow: visible !important;\r\n            }\r\n            .flatpickr-calendar .flatpickr-current-month {\r\n                height: auto !important;\r\n                line-height: 1.5 !important;\r\n                padding-top: 4px !important;\r\n                padding-bottom: 4px !important;\r\n                overflow: visible !important;\r\n            }\r\n            .flatpickr-calendar .flatpickr-current-month span.cur-month {\r\n                line-height: 1.5 !important;\r\n                display: inline-block !important;\r\n                vertical-align: baseline !important;\r\n            }\r\n            .flatpickr-calendar .flatpickr-current-month input.cur-year {\r\n                line-height: 1.5 !important;\r\n                height: auto !important;\r\n                min-height: 0 !important; \r\n                padding: 0 !important;\r\n                vertical-align: baseline !important;\r\n            }\r\n        ";
        wp_add_inline_style( 'sacconi-flatpickr-css', $flatpickr_fix_wp7 );
        wp_enqueue_script(
            'sacconi-flatpickr-js',
            plugin_dir_url( __FILE__ ) . 'assets/flatpickr.min.js',
            array('jquery', 'wp-blocks', 'wp-element'),
            '4.6.13',
            true
        );
        // Rilevamento lingua e caricamento file lingua locale
        $user_locale = substr( get_user_locale(), 0, 2 );
        if ( $user_locale !== 'en' ) {
            $lang_file = "assets/l10n/{$user_locale}.js";
            wp_enqueue_script(
                'sacconi-flatpickr-l10n',
                plugin_dir_url( __FILE__ ) . $lang_file,
                array('sacconi-flatpickr-js'),
                '4.6.13',
                true
            );
        }
        // 2. Traduzioni e parametri per il JavaScript (Text Domain aggiornato)
        $wp_date_format = get_option( 'date_format' );
        $fp_display_format = str_replace( [
            'j',
            'n',
            'Y',
            'd',
            'm'
        ], [
            'd',
            'm',
            'Y',
            'd',
            'm'
        ], $wp_date_format );
        wp_localize_script( 'sacconi-flatpickr-js', 'sacconi_params', [
            'locale'         => $user_locale,
            'firstDayOfWeek' => (int) get_option( 'start_of_week' ),
            'displayFormat'  => $fp_display_format,
            'start_date'     => __( 'Start Date', 'sacconi-ical-sync' ),
            'end_date'       => __( 'End Date', 'sacconi-ical-sync' ),
            'saving'         => __( 'Saving...', 'sacconi-ical-sync' ),
            'saved'          => __( 'Saved!', 'sacconi-ical-sync' ),
            'error'          => __( 'Error during sync', 'sacconi-ical-sync' ),
            'limit'          => __( 'Maximum limit of 15 links reached', 'sacconi-ical-sync' ),
            'nonce'          => wp_create_nonce( 'sacconi_sync_nonce' ),
        ] );
        $options = get_option( 'sacconi_settings' );
        $back_month = ( isset( $options['back_month'] ) ? intval( $options['back_month'] ) : 6 );
        $stay_days = ( isset( $options['stay_days'] ) ? intval( $options['stay_days'] ) : 7 );
        // 3. Script JavaScript principale (formattato per evitare errori di sintassi)
        $script = "\r\n            jQuery(document).ready(function(\$) {\r\n                var backMonthSetting = " . intval( $back_month ) . ";\r\n                var stayDays = " . intval( $stay_days ) . ";\r\n\r\n                function initFp(el, isStart) { \r\n                    if (!el) return;\r\n                    var now = new Date();\r\n                    var jumpDate;\r\n                    \r\n                    // ⚡ LOGICA MESE CORRENTE O MESE FISSO\r\n                    if (backMonthSetting === 99) {\r\n                        jumpDate = new Date(now.getFullYear(), now.getMonth(), 1); // Salta al mese attuale\r\n                    } else {\r\n                        var jumpYear = now.getFullYear();\r\n                        if (backMonthSetting < now.getMonth()) { jumpYear++; }\r\n                        jumpDate = new Date(jumpYear, backMonthSetting, 1);\r\n                    }\r\n                    \r\n                    var lang = sacconi_params.locale;\r\n                    var localeObj = (flatpickr.l10ns[lang]) ? flatpickr.l10ns[lang] : flatpickr.l10ns.default;\r\n\r\n                    flatpickr(el, { \r\n                        dateFormat: 'd/m/Y',\r\n                        altInput: true, \r\n                        altFormat: sacconi_params.displayFormat,\r\n                        defaultDate: \$(el).val() || null,\r\n                        locale: { ...localeObj, firstDayOfWeek: sacconi_params.firstDayOfWeek },\r\n                        onReady: function(sd, ds, inst) {\r\n                            if (!inst.selectedDates.length) inst.jumpToDate(jumpDate);\r\n                        },\r\n                        onChange: function(sd, ds, inst) {\r\n                            if (isStart && sd.length > 0 && stayDays > 0) {\r\n                                var row = \$(el).closest('.m-row');\r\n                                var endInput = row.find('input[name=\"s_end[]\"]')[0];\r\n                                if (endInput && endInput._flatpickr) {\r\n                                    var newEnd = new Date(sd[0]);\r\n                                    newEnd.setDate(newEnd.getDate() + stayDays);\r\n                                    endInput._flatpickr.setDate(newEnd);\r\n                                }\r\n                            }\r\n                        }\r\n                    }); \r\n                }\r\n                \r\n                \$('.sacconi-fp').each(function() { \r\n                    var isStart = (\$(this).attr('name') === 's_start[]');\r\n                    initFp(this, isStart); \r\n                });\r\n                \r\n                \$('#sacconi-add-period').on('click', function() {\r\n                    var row = \$('<div class=\"m-row\"><input type=\"text\" name=\"s_start[]\" class=\"sacconi-fp\" placeholder=\"' + sacconi_params.start_date + '\"> <input type=\"text\" name=\"s_end[]\" class=\"sacconi-fp\" placeholder=\"' + sacconi_params.end_date + '\"> <button class=\"rem button\" type=\"button\">×</button></div>');\r\n                    \$('#s-manual-container').append(row);\r\n                    initFp(row.find('input[name=\"s_start[]\"]')[0], true);\r\n                    initFp(row.find('input[name=\"s_end[]\"]')[0], false);\r\n                });\r\n\r\n                \$(document).on('click', '.rem', function() { \$(this).parent().remove(); });\r\n                \r\n                \$('#sacconi-add-link').on('click', function() {\r\n                    var container = \$('#s-ical-container');\r\n                    if (container.find('.i-row').length < 15) {\r\n                        container.append('<div class=\"i-row\" style=\"margin-bottom:5px;\"><input type=\"url\" name=\"s_urls[]\" style=\"width:80%;\" placeholder=\"https://...\"> <button class=\"rem button\" type=\"button\">×</button></div>');\r\n                    } else { alert(sacconi_params.limit); }\r\n                });\r\n\r\n                \$('#sacconi-save-btn').on('click', function() {\r\n                    var btn = \$(this), status = \$('#s-status');\r\n                    btn.prop('disabled', true); \r\n                    status.html('<strong>' + sacconi_params.saving + '</strong>');\r\n                    \$.post(ajaxurl, {\r\n                        action: 'sacconi_sync_manual_save',\r\n                        nonce: sacconi_params.nonce, \r\n                        post_id: \$('#post_ID').val(),\r\n                        s_start: \$('input[name=\"s_start[]\"]').map(function(){return \$(this).val();}).get(),\r\n                        s_end: \$('input[name=\"s_end[]\"]').map(function(){return \$(this).val();}).get(),\r\n                        s_urls: \$('input[name=\"s_urls[]\"]').map(function(){return \$(this).val();}).get()\r\n                    }, function(r) {\r\n                        if(r.success) {\r\n                            status.html('✅ <strong>' + sacconi_params.saved + '</strong>');\r\n                            setTimeout(function(){ status.html(''); btn.prop('disabled', false); }, 3000);\r\n                        } else { \r\n                            status.html('❌ ' + sacconi_params.error); \r\n                            btn.prop('disabled', false); \r\n                        }\r\n                    });\r\n                });\r\n\r\n                // --- ⚡ REGISTRAZIONE BLOCCO GUTENBERG NEL CATALOGO ⚡ ---\r\n                if ( window.wp && window.wp.blocks ) {\r\n                    window.wp.blocks.registerBlockType( 'sacconi-ical-sync/calendar-block', {\r\n                        apiVersion: 3,\r\n                        title: 'Sacconi iCal Sync',\r\n                        icon: 'calendar-alt',\r\n                        category: 'widgets',\r\n                        description: 'Display the availability calendar for this property.',\r\n                        edit: function() {\r\n                            return window.wp.element.createElement( 'div', { \r\n                                style: { \r\n                                    padding: '20px', \r\n                                    border: '1px solid #006288', \r\n                                    textAlign: 'center', \r\n                                    background: '#f9f9f9', \r\n                                    color: '#006288', \r\n                                    fontWeight: 'bold',\r\n                                    borderRadius: '4px'\r\n                                } \r\n                            }, '📅 Sacconi iCal Sync - Calendar Block' );\r\n                        },\r\n                        save: function() { \r\n                            return null; \r\n                        }\r\n                    });\r\n                }\r\n            });\r\n        ";
        wp_add_inline_script( 'sacconi-flatpickr-js', $script );
    }

    /**
     * Verifica chi è il titolare attuale e se è ancora valido (non cestinato)
     */
    private function get_valid_free_owner() {
        $owner_id = (int) get_option( 'sacconi_free_license_owner', 0 );
        if ( $owner_id > 0 ) {
            $status = get_post_status( $owner_id );
            // Se il post non esiste o non è più 'publish' o 'draft', lo slot è libero
            if ( !in_array( $status, ['publish', 'draft'] ) ) {
                return 0;
            }
        }
        return $owner_id;
    }

    /**
     * Se un post viene cestinato o rimosso, eliminiamo le sue date dalla tabella SQL
     * per evitare che rimangano calendari "fantasma" attivi.
     */
    public function sacconi_clean_on_status_change( $new_status, $old_status, $post ) {
        if ( $post->post_type !== 'post' ) {
            return;
        }
        if ( in_array( $new_status, ['trash', 'auto-draft', 'inherit'] ) ) {
            global $wpdb;
            $table = $wpdb->prefix . 'sacconiplugin_availability';
            // Cancelliamo tutte le date di questo post dalla tabella veloce
            $wpdb->delete( "{$wpdb->prefix}sacconiplugin_availability", array(
                'post_id' => $post->ID,
            ) );
            // phpcs:ignore WordPress.DB.DirectDatabaseQuery
            // Se questo post era il titolare della licenza free, liberiamo lo slot
            if ( (int) get_option( 'sacconi_free_license_owner' ) === (int) $post->ID ) {
                delete_option( 'sacconi_free_license_owner' );
            }
            // Pulizia cache per non far vedere dati vecchi
            $this->sacconiplugin_purge_all_cache();
        }
    }

    // --- UI METABOX ---
    /**
     * Gestisce la visualizzazione della Metabox con logica di licenza "First-come, first-served"
     */
    public function sacconiplugin_add_metabox() {
        global $wpdb, $post;
        if ( $this->is_pro() ) {
            add_meta_box(
                'sacconiplugin_box',
                esc_html__( 'Availability Management', 'sacconi-ical-sync' ),
                [$this, 'sacconiplugin_render_metabox'],
                'post',
                'normal',
                'high'
            );
            return;
        }
        $current_id = intval( $post->ID );
        // Recuperiamo il titolare salvato nelle opzioni del sito
        $free_owner = $this->get_valid_free_owner();
        // Se il titolare esiste ed è un altro post, blocchiamo questo
        if ( $free_owner > 0 && $free_owner !== $current_id ) {
            add_meta_box(
                'sacconi_limit_box',
                __( 'Sacconi Sync Info', 'sacconi-ical-sync' ),
                function () {
                    echo '<div style="padding:10px; color:#d63638; font-weight:bold;">';
                    esc_html_e( 'You have reached the limit of 1 calendar for the Free version. Upgrade to Pro to manage unlimited properties.', 'sacconi-ical-sync' );
                    echo '</div>';
                },
                'post',
                'normal',
                'high'
            );
            return;
        }
        add_meta_box(
            'sacconiplugin_box',
            esc_html__( 'Availability Management', 'sacconi-ical-sync' ),
            [$this, 'sacconiplugin_render_metabox'],
            'post',
            'normal',
            'high'
        );
    }

    public function sacconiplugin_render_metabox( $post ) {
        $manual = ( get_post_meta( $post->ID, '_sacconi_manual_periods', true ) ?: [] );
        $urls = ( get_post_meta( $post->ID, '_sacconi_ical_urls', true ) ?: [''] );
        $export_link = rest_url( 'sacconi-ical-sync/v1/export/' . $post->ID );
        echo '<div id="sacconi-box-gui" style="padding:10px;">';
        if ( $this->is_pro() ) {
            echo '<div style="background:#e7f5ff; border:1px solid #b1d1e5; padding:10px; border-radius:4px; margin-bottom:20px;">';
            echo '<h4 style="margin-top:0;">' . esc_html__( 'Export Link (for Airbnb/Booking)', 'sacconi-ical-sync' ) . '</h4>';
            echo '<input type="text" value="' . esc_url( $export_link ) . '" readonly style="width:100%; font-family:monospace; font-size:12px;"></div>';
        } else {
            $upgrade_url = sis_fs()->get_upgrade_url();
            echo '<div style="background:#fff9e6; border:1px solid #f1c40f; padding:15px; border-radius:4px; color:#856404; margin-bottom:20px;">';
            echo '<strong>' . esc_html__( 'Unlock automatic synchronization with Airbnb, Booking or other portals with the Pro version.', 'sacconi-ical-sync' ) . '</strong><br><br>';
            echo '<a href="' . esc_url( $upgrade_url ) . '" class="button button-primary">' . esc_html__( 'View Pro Version', 'sacconi-ical-sync' ) . '</a></div>';
        }
        echo '<h4>' . esc_html__( 'Manual Dates', 'sacconi-ical-sync' ) . '</h4><div id="s-manual-container">';
        foreach ( $manual as $p ) {
            echo '<div class="m-row" style="margin-bottom:5px;">';
            echo '<input type="text" name="s_start[]" class="sacconi-fp" value="' . esc_attr( $p['start'] ?? '' ) . '" placeholder="' . esc_attr__( 'Start Date', 'sacconi-ical-sync' ) . '"> ';
            echo '<input type="text" name="s_end[]" class="sacconi-fp" value="' . esc_attr( $p['end'] ?? '' ) . '" placeholder="' . esc_attr__( 'End Date', 'sacconi-ical-sync' ) . '"> ';
            echo '<button class="rem button" type="button">×</button></div>';
        }
        echo '</div><button id="sacconi-add-period" class="button" type="button">+ ' . esc_html__( 'Period', 'sacconi-ical-sync' ) . '</button>';
        if ( $this->is_pro() ) {
            echo '<hr style="margin:20px 0;"><h4>' . esc_html__( 'iCal (Intersection)', 'sacconi-ical-sync' ) . '</h4><div id="s-ical-container">';
            foreach ( $urls as $u ) {
                echo '<div class="i-row" style="margin-bottom:5px;"><input type="url" name="s_urls[]" value="' . esc_url( $u ) . '" style="width:80%;"> <button class="rem button" type="button">×</button></div>';
            }
            echo '</div><button id="sacconi-add-link" class="button" type="button">+ ' . esc_html__( 'Link iCal', 'sacconi-ical-sync' ) . '</button>';
        }
        echo '<div style="margin-top:20px;"><button id="sacconi-save-btn" class="button button-primary" type="button">' . esc_html__( 'Save and Sync', 'sacconi-ical-sync' ) . '</button> <span id="s-status"></span></div>';
        echo '</div>';
    }

    // --- LOGICA DI RENDERING (PHP) ---
    // --- LOGICA DI RENDERING (PHP) - VERSIONE COMPLIANT WP.ORG ---
    public function sacconiplugin_get_calendar_html( $post_id, $offset = 0 ) {
        global $wpdb, $wp_locale;
        $options = get_option( 'sacconi_settings' );
        // 1. Limitatore mesi per versione Free (Mantengo il tuo codice)
        $months_to_show = intval( $options['months'] ?? 3 );
        if ( !$this->is_pro() ) {
            if ( $months_to_show > 3 ) {
                $months_to_show = 3;
            }
        }
        $color_free = esc_attr( $options['color_free'] ?? '#b8f5b2' );
        $color_occ = esc_attr( $options['color_occ'] ?? '#e65c00' );
        $font_family = esc_attr( $options['font'] ?? "'Quicksand', sans-serif" );
        $radius = ( ($options['shape'] ?? 'circle') === 'circle' ? '50%' : '0px' );
        // ⚡ 2. LOGICA LUCCHETTO FRONTEND: Decidiamo se mostrare le date ⚡
        $can_show_dates = true;
        if ( !$this->is_pro() ) {
            $free_owner = $this->get_valid_free_owner();
            // Se esiste un proprietario ufficiale e NON è l'alloggio corrente, nascondiamo le date
            if ( $free_owner > 0 && (int) $post_id !== $free_owner ) {
                $can_show_dates = false;
            }
        }
        // 3. Esecuzione Query Protetta (solo se autorizzato)
        if ( $can_show_dates ) {
            $booked = $wpdb->get_results( $wpdb->prepare( "SELECT start_date, end_date FROM {$wpdb->prefix}sacconiplugin_availability WHERE post_id = %d", intval( $post_id ) ) );
            // phpcs:ignore WordPress.DB.DirectDatabaseQuery, WordPress.DB.DirectDatabaseQuery.NoCaching
        } else {
            $booked = [];
            // Forza calendario vuoto (verde) per i non autorizzati
        }
        $occ_map = [];
        foreach ( $booked as $row ) {
            // ⚡ FIX MATEMATICO: Se è un'intersezione di "zero notti" (check-out che tocca un check-in), ignorala! Il giorno torna 100% verde.
            if ( $row->start_date === $row->end_date ) {
                continue;
            }
            $date = new DateTime($row->start_date);
            $end = new DateTime($row->end_date);
            while ( $date <= $end ) {
                $d_str = $date->format( 'Y-m-d' );
                $type = ( isset( $occ_map[$d_str] ) ? 'full' : (( $d_str === $row->start_date ? 'start' : (( $d_str === $row->end_date ? 'end' : 'full' )) )) );
                $occ_map[$d_str] = $type;
                $date->modify( '+1 day' );
            }
        }
        // --- DETERMINAZIONE MESE DI PARTENZA DINAMICO ---
        $front_logic = ( isset( $options['front_logic'] ) ? $options['front_logic'] : 'default' );
        $back_month = ( isset( $options['back_month'] ) ? intval( $options['back_month'] ) : 6 );
        $now = new DateTime('first day of this month');
        $current_month_num = (int) $now->format( 'n' );
        // ⚡ SE ABBIAMO SCELTO DI ALLINEARE ED E' IMPOSTATO SU "MESE CORRENTE" (99)
        if ( $front_logic === 'match' && $back_month === 99 ) {
            // Non facciamo nulla: $now è già impostato sul mese corrente e sull'anno in corso!
        } else {
            // Logica tradizionale (Mese Fisso)
            $target_month_index = ( $front_logic === 'match' ? $back_month : 5 );
            if ( $current_month_num >= 10 || $target_month_index + 1 < $current_month_num ) {
                $now->modify( '+1 year' );
            }
            $now->setDate( (int) $now->format( 'Y' ), $target_month_index + 1, 1 );
        }
        $offset_int = intval( $offset );
        if ( $offset_int != 0 ) {
            $now->modify( "{$offset_int} month" );
        }
        // --- ETICHETTE GIORNI (Sincronizzate con WordPress) ---
        $start_of_week = (int) get_option( 'start_of_week' );
        $days_labels = [];
        for ($i = 0; $i <= 6; $i++) {
            $day_index = ( $start_of_week == 1 ? ($i + 1) % 7 : $i );
            $days_labels[] = $wp_locale->get_weekday_abbrev( $wp_locale->get_weekday( $day_index ) );
        }
        ob_start();
        ?>
        <div class="s-custom-cal" style="font-family: <?php 
        echo esc_attr( $font_family );
        ?>;">
            <div class="s-grid">
                <?php 
        $months_total = (int) $months_to_show;
        for ($i = 0; $i < $months_total; $i++) {
            $this_m = clone $now;
            $this_m->modify( "+{$i} month" );
            $month_val = $this_m->format( 'Y-m' );
            $days_in_m = intval( $this_m->format( 't' ) );
            $first_dow_of_month = (int) $this_m->format( 'w' );
            $empty_cells = ( $start_of_week === 1 ? ($first_dow_of_month + 6) % 7 : $first_dow_of_month );
            ?>
                    <div class="s-month">
                        <div class="s-m-name">
                            <?php 
            if ( $i == 0 ) {
                ?>
                                <a href="#" class="s-nav-arrow prev" data-post-id="<?php 
                echo intval( $post_id );
                ?>" data-offset="<?php 
                echo intval( $offset_int - 1 );
                ?>">&lsaquo;</a>
                                <a href="#" class="s-nav-arrow next s-mobile-only" data-post-id="<?php 
                echo intval( $post_id );
                ?>" data-offset="<?php 
                echo intval( $offset_int + 1 );
                ?>">&rsaquo;</a>
                            <?php 
            }
            ?>

<?php 
            if ( $i == 0 ) {
                ?>
                                <a href="#" class="s-nav-arrow prev" data-post-id="<?php 
                echo intval( $post_id );
                ?>" data-offset="<?php 
                echo intval( $offset_int - 1 );
                ?>">&lsaquo;</a>
                                <a href="#" class="s-nav-arrow next s-mobile-only" data-post-id="<?php 
                echo intval( $post_id );
                ?>" data-offset="<?php 
                echo intval( $offset_int + 1 );
                ?>">&rsaquo;</a>
                                
                                <select class="sacconi-month-selector" data-post-id="<?php 
                echo intval( $post_id );
                ?>" style="border: 1px solid #ccc; border-radius: 4px; padding: 3px 6px; background: #fff; cursor: pointer; font-family: inherit; font-size: 0.95em; color: inherit; outline: none; font-weight: bold;">
                                    <?php 
                // Creiamo 16 mesi: 3 precedenti, quello attuale (0), e 12 successivi
                for ($m = -3; $m <= 12; $m++) {
                    $opt_date = clone $this_m;
                    if ( $m !== 0 ) {
                        $opt_date->modify( "{$m} month" );
                    }
                    $opt_offset = $offset_int + $m;
                    $opt_label = ucwords( wp_date( 'F', $opt_date->getTimestamp() ) ) . ' ' . $opt_date->format( 'Y' );
                    // Usiamo selected() di WordPress per mostrare il mese corrente come selezionato di default
                    echo '<option value="' . intval( $opt_offset ) . '" ' . selected( $m, 0, false ) . '>' . esc_html( $opt_label ) . '</option>';
                }
                ?>
                                </select>

                            <?php 
            } else {
                ?>
                                <strong><?php 
                echo esc_html( ucwords( wp_date( 'F', $this_m->getTimestamp() ) ) );
                ?></strong> <?php 
                echo esc_html( $this_m->format( 'Y' ) );
                ?>
                            <?php 
            }
            ?>

                            <?php 
            if ( $i === $months_total - 1 ) {
                ?>
                                <a href="#" class="s-nav-arrow next s-desktop-only" data-post-id="<?php 
                echo intval( $post_id );
                ?>" data-offset="<?php 
                echo intval( $offset_int + 1 );
                ?>">&rsaquo;</a>
                            <?php 
            }
            ?>

                            <?php 
            if ( $i === $months_total - 1 ) {
                ?>
                                <a href="#" class="s-nav-arrow next s-desktop-only" data-post-id="<?php 
                echo intval( $post_id );
                ?>" data-offset="<?php 
                echo intval( $offset_int + 1 );
                ?>">&rsaquo;</a>
                            <?php 
            }
            ?>
                        </div>

                        <div class="s-d-grid">
                            <?php 
            foreach ( $days_labels as $l ) {
                echo "<div class='s-h'>" . esc_html( $l ) . "</div>";
            }
            ?>
                            <?php 
            for ($e = 0; $e < $empty_cells; $e++) {
                echo '<div></div>';
            }
            for ($d = 1; $d <= $days_in_m; $d++) {
                $c_date = $month_val . '-' . str_pad(
                    $d,
                    2,
                    '0',
                    STR_PAD_LEFT
                );
                $st = esc_attr( $occ_map[$c_date] ?? 'free' );
                echo "<div class='s-day s-" . esc_attr( $st ) . "'><span>" . intval( $d ) . "</span></div>";
            }
            ?>
                        </div>
                    </div>
                <?php 
        }
        ?>
            </div>
        </div>
        <?php 
        return ob_get_clean();
    }

    public function sacconiplugin_frontend_calendar( $atts ) {
        $post_id = get_the_ID();
        $options = get_option( 'sacconi_settings' );
        $a = shortcode_atts( [
            'title' => '',
        ], $atts );
        $type = $options['title_type'] ?? 'availability';
        $display_title = ( $type === 'availability' ? __( 'Availability', 'sacconi-ical-sync' ) : (( $type === 'calendars' ? __( 'Calendars', 'sacconi-ical-sync' ) : '' )) );
        if ( !empty( $a['title'] ) ) {
            $display_title = $a['title'];
        }
        $color_title = $options['color_title'] ?? '#006288';
        $legend_pos = $options['legend'] ?? 'none';
        // ⚡ CREAZIONE DINAMICA DELLA LEGENDA
        $legend_html = '';
        if ( $legend_pos !== 'none' ) {
            $txt_free = esc_html__( 'Free', 'sacconi-ical-sync' );
            $txt_occ = esc_html__( 'Occupied', 'sacconi-ical-sync' );
            // Aggiunto il bordo grigio chiaro (#ddd), angoli arrotondati (5px), sfondo bianco e padding
            // margin: 20px auto distanzia la legenda e la centra, replicando lo spazio tra i mesi
            $legend_html = '
            <div class="sacconi-legend" style="display:flex; gap:20px; margin: 20px 0; padding: 10px 20px; font-size:0.9em; justify-content:flex-start; border: 1px solid #eee; border-radius: 5px; background: #fff; width: max-content; max-width: 100%; box-sizing: border-box;">
                <div style="display:flex; align-items:center; gap:6px;">
                    <div style="width:16px; height:16px; background:var(--s-free); border-radius:var(--s-radius);"></div>
                    <span>' . $txt_free . '</span>
                </div>
                <div style="display:flex; align-items:center; gap:6px;">
                    <div style="width:16px; height:16px; background:var(--s-occ); border-radius:var(--s-radius);"></div>
                    <span>' . $txt_occ . '</span>
                </div>
            </div>';
        }
        $output = '<div class="sacconi-sync-wrapper">';
        if ( $display_title ) {
            $output .= '<h4 id="availability-calendar-title" class="s-title-text" style="font-weight:bold; color:' . esc_attr( $color_title ) . ';">' . esc_html( $display_title ) . '</h4>';
        }
        // ⚡ STAMPA LA LEGENDA SOPRA (SE SCELTO)
        if ( $legend_pos === 'sopra' ) {
            $output .= $legend_html;
        }
        $output .= '<div id="sacconi-cal-ajax-container" class="sacconi-cal-ajax-container" data-post-id="' . intval( $post_id ) . '">';
        $output .= '<div style="text-align:center; padding:40px; color:#999;">' . esc_html__( 'Loading...', 'sacconi-ical-sync' ) . '</div>';
        $output .= '</div>';
        // ⚡ STAMPA LA LEGENDA SOTTO (SE SCELTO)
        if ( $legend_pos === 'sotto' ) {
            $output .= $legend_html;
        }
        $output .= '</div>';
        return $output;
    }

    public function sacconi_nav_cal() {
        if ( ob_get_length() ) {
            ob_clean();
        }
        check_ajax_referer( 'sacconi_sync_nonce', 'nonce' );
        header( "Cache-Control: no-cache, must-revalidate" );
        $post_id = ( isset( $_POST['post_id'] ) ? intval( $_POST['post_id'] ) : 0 );
        $offset = ( isset( $_POST['offset'] ) ? intval( $_POST['offset'] ) : 0 );
        // ⚡ FIX WP.ORG: Array strict di tag HTML consentiti per stampare il calendario in sicurezza
        $allowed_html = array(
            'div'    => array(
                'class'        => true,
                'id'           => true,
                'style'        => true,
                'data-post-id' => true,
            ),
            'span'   => array(
                'style' => true,
            ),
            'strong' => array(),
            'a'      => array(
                'href'         => true,
                'class'        => true,
                'data-post-id' => true,
                'data-offset'  => true,
            ),
            'select' => array(
                'class'        => true,
                'data-post-id' => true,
                'style'        => true,
            ),
            'option' => array(
                'value'    => true,
                'selected' => true,
            ),
        );
        echo wp_kses( $this->sacconiplugin_get_calendar_html( $post_id, $offset ), $allowed_html );
        wp_die();
    }

    public function sacconiplugin_ajax_manual_sync_handler() {
        if ( ob_get_length() ) {
            ob_clean();
        }
        check_ajax_referer( 'sacconi_sync_nonce', 'nonce' );
        try {
            $post_id = ( isset( $_POST['post_id'] ) ? intval( $_POST['post_id'] ) : 0 );
            // ⚡ FIX WP.ORG: Controllo permessi. Verifichiamo che l'utente possa modificare questo alloggio
            if ( !current_user_can( 'edit_post', $post_id ) ) {
                wp_send_json_error( [
                    'message' => esc_html__( 'Insufficient permissions.', 'sacconi-ical-sync' ),
                ] );
            }
            $s_start = ( isset( $_POST['s_start'] ) ? array_map( 'sanitize_text_field', wp_unslash( (array) $_POST['s_start'] ) ) : [] );
            $s_end = ( isset( $_POST['s_end'] ) ? array_map( 'sanitize_text_field', wp_unslash( (array) $_POST['s_end'] ) ) : [] );
            $s_urls = ( isset( $_POST['s_urls'] ) ? array_map( 'esc_url_raw', wp_unslash( (array) $_POST['s_urls'] ) ) : [] );
            $manual_data = [];
            foreach ( $s_start as $k => $val ) {
                if ( $val && isset( $s_end[$k] ) ) {
                    $manual_data[] = [
                        'start' => $val,
                        'end'   => $s_end[$k],
                    ];
                }
            }
            // ⚡ Riordina le date in ordine decrescente (le più recenti in alto)
            usort( $manual_data, function ( $a, $b ) {
                $dateA = DateTime::createFromFormat( 'd/m/Y', $a['start'] );
                $dateB = DateTime::createFromFormat( 'd/m/Y', $b['start'] );
                if ( $dateA && $dateB ) {
                    return $dateB <=> $dateA;
                    // Ordine decrescente
                }
                return 0;
            } );
            $this->sacconiplugin_process_save( $post_id, $manual_data, $s_urls );
            wp_send_json_success();
        } catch ( Exception $e ) {
            // Inviamo solo il messaggio di errore "umano", senza dettagli tecnici
            wp_send_json_error( [
                'message' => $e->getMessage(),
            ] );
        }
        wp_die();
    }

    /**
     * Elabora e salva i dati (Manuali e iCal) con DEBUG INTEGRATO
     */
    private function sacconiplugin_process_save( $post_id, $manual_periods, $urls ) {
        global $wpdb;
        $post_id = intval( $post_id );
        $t = $wpdb->prefix . 'sacconiplugin_availability';
        // 🛡️ GESTIONE LICENZA FREE
        if ( !$this->is_pro() ) {
            $free_owner = $this->get_valid_free_owner();
            if ( $free_owner === 0 && (!empty( $manual_periods ) || !empty( $urls )) ) {
                update_option( 'sacconi_free_license_owner', $post_id );
                $free_owner = $post_id;
            }
            if ( $free_owner > 0 && $free_owner !== $post_id ) {
                // Messaggio pulito per il cliente finale
                throw new Exception(esc_html__( 'License lock: the free slot is already occupied by another property.', 'sacconi-ical-sync' ));
            }
            if ( empty( $manual_periods ) && empty( $urls ) && $free_owner === $post_id ) {
                delete_option( 'sacconi_free_license_owner' );
            }
        }
        // 📝 2. SALVATAGGIO DATE MANUALI
        // Protezione: assicuriamoci che $manual_periods sia un array
        $manual_periods = ( is_array( $manual_periods ) ? $manual_periods : [] );
        update_post_meta( $post_id, '_sacconi_manual_periods', $manual_periods );
        // ⚡ FIX PCP: Nome tabella diretto e commento ignore
        $wpdb->delete( "{$wpdb->prefix}sacconiplugin_availability", array(
            'post_id' => $post_id,
            'source'  => 'manual',
        ) );
        // phpcs:ignore WordPress.DB.DirectDatabaseQuery
        if ( !empty( $manual_periods ) ) {
            foreach ( $manual_periods as $p ) {
                if ( empty( $p['start'] ) || empty( $p['end'] ) ) {
                    continue;
                }
                $s = DateTime::createFromFormat( 'd/m/Y', $p['start'] );
                $e = DateTime::createFromFormat( 'd/m/Y', $p['end'] );
                if ( $s && $e ) {
                    // ⚡ FIX PCP: Inserimento protetto
                    $wpdb->insert( "{$wpdb->prefix}sacconiplugin_availability", array(
                        'post_id'    => $post_id,
                        'start_date' => $s->format( 'Y-m-d' ),
                        'end_date'   => $e->format( 'Y-m-d' ),
                        'source'     => 'manual',
                    ) );
                    // phpcs:ignore WordPress.DB.DirectDatabaseQuery
                }
            }
        }
        // 🌐 3. SALVATAGGIO ICAL
        $urls_clean = ( is_array( $urls ) ? array_filter( array_map( 'esc_url_raw', $urls ) ) : [] );
        update_post_meta( $post_id, '_sacconi_ical_urls', $urls_clean );
        // Eseguiamo la sincronizzazione iCal
        $ical_success = $this->sacconiplugin_sync_ical_to_db( $post_id, $urls_clean );
        // 🧹 4. PULIZIA CACHE
        $this->sacconiplugin_purge_all_cache();
        clean_post_cache( $post_id );
        return true;
    }

    private function sacconiplugin_sync_ical_to_db( $post_id, $urls ) {
        global $wpdb;
        $m = null;
        $ok = true;
        $table_name = "{$wpdb->prefix}sacconiplugin_availability";
        if ( empty( $urls ) ) {
            $wpdb->delete( $table_name, array(
                'post_id' => $post_id,
                'source'  => 'ical',
            ) );
            // phpcs:ignore WordPress.DB.DirectDatabaseQuery
            return true;
        }
        foreach ( $urls as $u ) {
            $r = wp_remote_get( $u, array(
                'timeout'   => 20,
                'sslverify' => false,
            ) );
            $b = wp_remote_retrieve_body( $r );
            if ( is_wp_error( $r ) || empty( $b ) || strlen( $b ) < 100 ) {
                $ok = false;
                break;
            }
            $curr = $this->sacconiplugin_parse_ical( $b );
            if ( !$m ) {
                $m = $curr;
            } else {
                $m->intersect( $curr );
            }
        }
        if ( $ok && $m ) {
            // ⚡ FIX PCP: Cancellazione e Inserimento con nomi tabelle diretti
            $wpdb->delete( $table_name, array(
                'post_id' => $post_id,
                'source'  => 'ical',
            ) );
            // phpcs:ignore WordPress.DB.DirectDatabaseQuery
            foreach ( $m->getIntervals() as $p ) {
                $wpdb->insert( $table_name, array(
                    'post_id'    => $post_id,
                    'start_date' => $p['start']->format( 'Y-m-d' ),
                    'end_date'   => $p['end']->format( 'Y-m-d' ),
                    'source'     => 'ical',
                ) );
                // phpcs:ignore WordPress.DB.DirectDatabaseQuery
            }
            return true;
        }
        return false;
    }

    private function sacconiplugin_parse_ical( $b ) {
        $mgr = new SacconiPlugin_IntervalManager();
        $lines = preg_split( "/\r\n|\n|\r/", $b );
        $ev = [];
        foreach ( $lines as $l ) {
            $l = trim( $l );
            if ( $l === 'BEGIN:VEVENT' ) {
                $ev = [];
            } elseif ( $l === 'END:VEVENT' ) {
                if ( !empty( $ev['DTSTART'] ) && !empty( $ev['DTEND'] ) ) {
                    $s = DateTime::createFromFormat( 'Ymd', substr( $ev['DTSTART'], 0, 8 ), new DateTimeZone('UTC') );
                    $e = DateTime::createFromFormat( 'Ymd', substr( $ev['DTEND'], 0, 8 ), new DateTimeZone('UTC') );
                    if ( $s && $e ) {
                        $mgr->addInterval( $s, $e );
                    }
                }
            } else {
                if ( strpos( $l, 'DTSTART' ) === 0 ) {
                    $ev['DTSTART'] = substr( $l, strpos( $l, ':' ) + 1 );
                }
                if ( strpos( $l, 'DTEND' ) === 0 ) {
                    $ev['DTEND'] = substr( $l, strpos( $l, ':' ) + 1 );
                }
            }
        }
        return $mgr;
    }

    public function sacconiplugin_create_db() {
        global $wpdb;
        $charset_collate = $wpdb->get_charset_collate();
        $table_name = $wpdb->prefix . 'sacconiplugin_availability';
        // Scriviamo la query in modo che il nome tabella sia esplicito (PCP fix)
        $sql = "CREATE TABLE {$table_name} (\r\n            id bigint(20) NOT NULL AUTO_INCREMENT,\r\n            post_id bigint(20) NOT NULL,\r\n            start_date date NOT NULL,\r\n            end_date date NOT NULL,\r\n            source varchar(50) DEFAULT 'manual',\r\n            PRIMARY KEY  (id),\r\n            KEY post_id (post_id)\r\n        ) {$charset_collate};";
        // ⚡ MODO CORRETTO DI CARICARE IL FILE CORE (WP.ORG COMPLIANT)
        if ( !function_exists( 'dbDelta' ) ) {
            require_once ABSPATH . 'wp-admin/includes/upgrade.php';
        }
        dbDelta( $sql );
    }

    public function sacconiplugin_force_purge_on_save() {
        // Verifichiamo se siamo nella pagina giusta e se il salvataggio è confermato
        $page = ( isset( $_GET['page'] ) ? sanitize_text_field( wp_unslash( $_GET['page'] ) ) : '' );
        // phpcs:ignore WordPress.Security.NonceVerification.Recommended
        $updated = isset( $_GET['settings-updated'] );
        // phpcs:ignore WordPress.Security.NonceVerification.Recommended
        if ( $page === 'sacconi-settings' && $updated ) {
            // ⚡ CHIAMA LA FUNZIONE DI PULIZIA UNIVERSALE ⚡
            $this->sacconiplugin_purge_all_cache();
        }
    }

    /**
     * Svuota la cache in modo universale (LiteSpeed, WP Rocket, W3TC, ecc.)
     */
    public function sacconiplugin_purge_all_cache( $o = null, $n = null ) {
        // 1. ⚡ LITESPEED: Comando specifico per far apparire i messaggi in bacheca
        if ( class_exists( '\\LiteSpeed\\Purge' ) ) {
            \LiteSpeed\Purge::purge_all();
        }
        // 2. ⚡ LITESPEED: Segnale universale (attiva anche Cloudflare se collegato)
        if ( function_exists( 'litespeed_purge_all' ) ) {
            litespeed_purge_all();
        }
        // Fallback tramite Action (sicurezza extra)
        do_action( 'litespeed_purge_all' );
        // phpcs:ignore WordPress.NamingConventions.PrefixAllGlobals.NonPrefixedHooknameFound
        // 3. WP Rocket
        if ( function_exists( 'rocket_clean_domain' ) ) {
            rocket_clean_domain();
        }
        // 4. W3 Total Cache
        if ( function_exists( 'w3tc_flush_all' ) ) {
            w3tc_flush_all();
        }
        // 5. WP Fastest Cache
        if ( isset( $GLOBALS['wp_fastest_cache'] ) && method_exists( $GLOBALS['wp_fastest_cache'], 'deleteCache' ) ) {
            $GLOBALS['wp_fastest_cache']->deleteCache( true );
        }
        // 6. Autoptimize
        if ( class_exists( 'autoptimizeCache' ) ) {
            autoptimizeCache::clearall();
        }
        // 7. SG Optimizer (SiteGround)
        if ( function_exists( 'sg_cachepress_purge_cache' ) ) {
            sg_cachepress_purge_cache();
        }
        // 8. WordPress standard
        if ( function_exists( 'wp_cache_flush' ) ) {
            wp_cache_flush();
        }
    }

}

new SacconiPlugin_Calendar();