<?php
/**
 * Plugin Name: MOVA Event Manager
 * Description: Gestione iscrizioni evento da form Elementor con dashboard, export XLSX, QR code e check-in.
 * Version: 1.4.2
 * Author: MCI / Luca
 */

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

class MOVA_Event_Manager {
    const VERSION = '1.4.2';
    const TABLE = 'mova_event_registrations';
    const FORM_NAME = 'Prenotazione Evento';
    const STATUS_REGISTERED = 'registered';
    const STATUS_WAITING_LIST = 'waiting_list';
    const STATUS_CONFIRMED = 'confirmed';
    const EVENT_TITLE = 'The Master & The Team';
    const EVENT_DATE = 'Mercoledì 6 Maggio 2026';
    const EVENT_LOCATION = 'Teatro Alcione Milano';
    const HEADER_IMAGE = 'https://mcidemo.it/mova/wp-content/uploads/2026/04/MOVA_Modric_Banner_2560x768.jpg';
    const OPTION_LAST_MAIL_STATUS = 'mova_event_manager_last_mail_status';
    const OPTION_AUTO_CLOSED = 'mova_event_manager_auto_closed';

    private $current_mail_context = '';

    public function __construct() {
        register_activation_hook(__FILE__, [$this, 'activate']);
        register_deactivation_hook(__FILE__, [$this, 'deactivate']);

        add_action('init', [$this, 'add_rewrite_rules']);
        add_action('init', [$this, 'maybe_upgrade_schema']);
        add_filter('query_vars', [$this, 'add_query_vars']);
        add_action('template_redirect', [$this, 'handle_public_routes']);
        add_action('wp_enqueue_scripts', [$this, 'enqueue_public_assets']);

        add_action('admin_menu', [$this, 'admin_menu']);
        add_action('admin_enqueue_scripts', [$this, 'enqueue_admin_assets']);
        add_action('admin_post_mova_export_registrations', [$this, 'handle_export']);
        add_action('admin_post_mova_save_registration', [$this, 'handle_save_registration']);
        add_action('admin_post_mova_delete_registration', [$this, 'handle_delete_registration']);
        add_action('admin_post_mova_checkin_registration', [$this, 'handle_checkin_registration']);
        add_action('admin_post_mova_undo_checkin_registration', [$this, 'handle_undo_checkin_registration']);
        add_action('admin_post_mova_regenerate_qr_registration', [$this, 'handle_regenerate_qr_registration']);
        add_action('admin_post_mova_confirm_registration', [$this, 'handle_confirm_registration']);

        add_action('wp_mail_failed', [$this, 'capture_mail_error']);

        add_action('elementor_pro/forms/validation', [$this, 'validate_elementor_submission'], 10, 2);
        add_action('elementor_pro/forms/new_record', [$this, 'capture_elementor_submission'], 10, 2);
    }

    public function activate() {
        $this->create_table();
        update_option('mova_event_manager_db_version', self::VERSION, false);
        $this->add_rewrite_rules();
        flush_rewrite_rules();
    }

    public function deactivate() {
        flush_rewrite_rules();
    }

    private function table_name() {
        global $wpdb;
        return $wpdb->prefix . self::TABLE;
    }

    private function create_table() {
        global $wpdb;
        require_once ABSPATH . 'wp-admin/includes/upgrade.php';
        $charset = $wpdb->get_charset_collate();
        $sql = "CREATE TABLE {$this->table_name()} (
            id BIGINT UNSIGNED NOT NULL AUTO_INCREMENT,
            page_id BIGINT UNSIGNED NOT NULL,
            first_name VARCHAR(190) NOT NULL,
            last_name VARCHAR(190) NOT NULL,
            email VARCHAR(190) NOT NULL,
            phone VARCHAR(50) DEFAULT '',
            age VARCHAR(20) DEFAULT '',
            registration_status VARCHAR(40) NOT NULL DEFAULT 'registered',
            privacy_policy VARCHAR(20) DEFAULT '',
            marketing_policy VARCHAR(20) DEFAULT '',
            token VARCHAR(64) NOT NULL,
            qr_file VARCHAR(255) DEFAULT '',
            checked_in_at DATETIME NULL,
            checkin_source VARCHAR(50) DEFAULT '',
            notes TEXT NULL,
            confirmed_at DATETIME NULL,
            created_at DATETIME NOT NULL,
            updated_at DATETIME NOT NULL,
            PRIMARY KEY (id),
            UNIQUE KEY token (token),
            UNIQUE KEY page_email (page_id, email),
            KEY page_id (page_id),
            KEY email (email)
        ) $charset;";
        dbDelta($sql);
    }


    public function maybe_upgrade_schema() {
        $version = get_option('mova_event_manager_db_version');
        if ($version !== self::VERSION) {
            $this->create_table();
            $this->migrate_legacy_consents($version);
            $this->add_rewrite_rules();
            flush_rewrite_rules(false);
            update_option('mova_event_manager_db_version', self::VERSION, false);
        }
    }

    private function migrate_legacy_consents($previous_version = '') {
        global $wpdb;

        if ($previous_version && version_compare((string) $previous_version, '1.3.3', '>=')) {
            return;
        }

        // Legacy versions never persisted these fields even when the checkboxes
        // were selected in Elementor, so existing empty values are backfilled.
        $wpdb->query(
            "UPDATE {$this->table_name()}
            SET privacy_policy = 'Accettato'
            WHERE privacy_policy = '' OR privacy_policy IS NULL"
        );

        $wpdb->query(
            "UPDATE {$this->table_name()}
            SET marketing_policy = 'Accettato'
            WHERE marketing_policy = '' OR marketing_policy IS NULL"
        );
    }

    private function get_checkin_count($page_id = null) {
        global $wpdb;
        $page_id = $page_id ?: $this->get_event_page_id();
        return (int) $wpdb->get_var($wpdb->prepare("SELECT COUNT(*) FROM {$this->table_name()} WHERE page_id = %d AND checked_in_at IS NOT NULL", $page_id));
    }
    public function add_rewrite_rules() {
        add_rewrite_rule('^mova-ticket/([A-Za-z0-9]+)/?$', 'index.php?mova_registration=$matches[1]', 'top');
        add_rewrite_rule('^mova-checkin/([A-Za-z0-9]+)/?$', 'index.php?mova_registration=$matches[1]&mova_checkin=1', 'top');
        add_rewrite_rule('^mova-dashboard/?$', 'index.php?mova_dashboard=1', 'top');
    }

    public function add_query_vars($vars) {
        $vars[] = 'mova_registration';
        $vars[] = 'mova_checkin';
        $vars[] = 'mova_dashboard';
        return $vars;
    }

    public function enqueue_public_assets() {
        if (!get_query_var('mova_registration') && !get_query_var('mova_dashboard')) {
            return;
        }

        wp_enqueue_style(
            'mova-event-manager-public',
            plugin_dir_url(__FILE__) . 'assets/css/mova-event-public.css',
            [],
            self::VERSION
        );

        if (get_query_var('mova_dashboard')) {
            wp_enqueue_style(
                'mova-event-manager-admin-public',
                plugin_dir_url(__FILE__) . 'assets/css/mova-event-admin.css',
                [],
                self::VERSION
            );

            wp_enqueue_script(
                'mova-event-manager-admin-public',
                plugin_dir_url(__FILE__) . 'assets/js/mova-event-admin.js',
                [],
                self::VERSION,
                true
            );
        }
    }

    public function enqueue_admin_assets($hook) {
        if ($hook !== 'toplevel_page_mova-event') {
            return;
        }

        wp_enqueue_style(
            'mova-event-manager-admin',
            plugin_dir_url(__FILE__) . 'assets/css/mova-event-admin.css',
            [],
            self::VERSION
        );

        wp_enqueue_script(
            'mova-event-manager-admin',
            plugin_dir_url(__FILE__) . 'assets/js/mova-event-admin.js',
            [],
            self::VERSION,
            true
        );
    }

    private function get_event_page_id() {
        return (int) get_option('page_on_front');
    }

    private function can_access_dashboard() {
        return current_user_can('edit_pages') || current_user_can('manage_options');
    }

    private function get_dashboard_url() {
        return home_url('/mova-dashboard/');
    }

    private function get_dashboard_base_url($public_context = null) {
        $public_context = $public_context === null ? (bool) get_query_var('mova_dashboard') : (bool) $public_context;
        return $public_context ? $this->get_dashboard_url() : admin_url('admin.php?page=mova-event');
    }

    private function get_safe_redirect_target($fallback = '') {
        $fallback = $fallback ?: $this->get_dashboard_base_url();
        $redirect_to = '';

        if (isset($_REQUEST['redirect_to'])) {
            $redirect_to = wp_unslash($_REQUEST['redirect_to']);
        }

        return wp_validate_redirect($redirect_to, $fallback);
    }

    private function normalize_meta_value($value, $default = '') {
        if (is_array($value)) {
            if (isset($value['value']) && $value['value'] !== '') {
                return $value['value'];
            }
            if (isset($value['label']) && $value['label'] !== '') {
                return $value['label'];
            }
            $first = reset($value);
            return ($first !== false && $first !== null && $first !== '') ? $first : $default;
        }

        if ($value === null || $value === '') {
            return $default;
        }

        return $value;
    }

    private function get_event_field($key, $default = '') {
        $page_id = $this->get_event_page_id();
        if (!$page_id) {
            return $default;
        }

        if (function_exists('get_field')) {
            $value = get_field($key, $page_id);
        } else {
            $value = get_post_meta($page_id, $key, true);
        }

        return $this->normalize_meta_value($value, $default);
    }

    private function update_event_field($key, $value) {
        $page_id = $this->get_event_page_id();
        if (!$page_id) {
            return;
        }

        if (function_exists('update_field')) {
            @update_field($key, $value, $page_id);
        }

        update_post_meta($page_id, $key, $value);
    }

    private function get_event_status() {
        $status = (string) $this->get_event_field('status', 'Aperto');
        return trim($status) ?: 'Aperto';
    }

    private function get_event_capacity() {
        return max(0, (int) $this->get_event_field('capienza', 0));
    }

    private function get_event_address() {
        return (string) $this->get_event_field('indirizzo_evento', self::EVENT_LOCATION);
    }

    private function get_registration_redirect_url($status) {
        $field_key = $status === self::STATUS_WAITING_LIST
            ? 'redirect_waiting_list'
            : 'redirect_iscritto';

        $url = trim((string) $this->get_event_field($field_key, ''));
        return $url ? esc_url_raw($url) : '';
    }

    private function maybe_add_submission_redirect($handler, $status) {
        $redirect_url = $this->get_registration_redirect_url($status);
        if (!$redirect_url || !is_object($handler) || !method_exists($handler, 'add_response_data')) {
            return;
        }

        $handler->add_response_data('redirect_url', $redirect_url);
        $handler->add_response_data('redirect', $redirect_url);
    }

    private function normalize_phone_for_lookup($phone) {
        return preg_replace('/\D+/', '', (string) $phone);
    }

    private function get_registration_count($page_id = null) {
        global $wpdb;
        $page_id = $page_id ?: $this->get_event_page_id();
        return (int) $wpdb->get_var($wpdb->prepare("SELECT COUNT(*) FROM {$this->table_name()} WHERE page_id = %d", $page_id));
    }

    private function get_priority_registration_count($page_id = null) {
        global $wpdb;
        $page_id = $page_id ?: $this->get_event_page_id();

        return (int) $wpdb->get_var($wpdb->prepare(
            "SELECT COUNT(*) FROM {$this->table_name()} WHERE page_id = %d AND registration_status IN (%s, %s)",
            $page_id,
            self::STATUS_REGISTERED,
            self::STATUS_CONFIRMED
        ));
    }

    private function get_primary_registration_limit() {
        return $this->get_event_capacity();
    }

    private function determine_registration_status($page_id = null) {
        $page_id = $page_id ?: $this->get_event_page_id();
        $limit = $this->get_primary_registration_limit();

        if ($limit <= 0) {
            return self::STATUS_REGISTERED;
        }

        return $this->get_priority_registration_count($page_id) < $limit
            ? self::STATUS_REGISTERED
            : self::STATUS_WAITING_LIST;
    }

    private function get_registration_status_label($status) {
        if ($status === self::STATUS_WAITING_LIST) {
            return 'Waiting List';
        }
        if ($status === self::STATUS_CONFIRMED) {
            return 'Iscrizione Confermata';
        }

        return 'Iscritto';
    }

    private function is_event_closed() {
        return function_exists('mb_strtolower')
            ? mb_strtolower($this->get_event_status()) === 'chiuso'
            : strtolower($this->get_event_status()) === 'chiuso';
    }

    private function is_event_full() {
        $capacity = $this->get_event_capacity();
        if ($capacity <= 0) {
            return false;
        }
        return $this->get_registration_count() >= $capacity;
    }

    private function is_event_auto_closed() {
        return get_option(self::OPTION_AUTO_CLOSED, '0') === '1';
    }

    private function set_event_auto_closed($value) {
        update_option(self::OPTION_AUTO_CLOSED, $value ? '1' : '0', false);
    }

    private function sync_event_status_with_capacity() {
        if ($this->is_event_full()) {
            $this->set_event_auto_closed(true);
            $this->update_event_field('status', 'Chiuso');
        } elseif ($this->is_event_auto_closed()) {
            $this->set_event_auto_closed(false);
            $this->update_event_field('status', 'Aperto');
        }
    }

    private function acquire_event_lock($page_id, $timeout = 10) {
        global $wpdb;
        $lock_name = sprintf('%s_%d', $this->table_name(), (int) $page_id);
        return (int) $wpdb->get_var(
            $wpdb->prepare('SELECT GET_LOCK(%s, %d)', $lock_name, max(0, (int) $timeout))
        ) === 1;
    }

    private function release_event_lock($page_id) {
        global $wpdb;
        $lock_name = sprintf('%s_%d', $this->table_name(), (int) $page_id);
        $wpdb->get_var($wpdb->prepare('SELECT RELEASE_LOCK(%s)', $lock_name));
    }

    public function validate_elementor_submission($record, $ajax_handler) {
        $form_name = $record->get_form_settings('form_name');
        if ($form_name !== self::FORM_NAME) {
            return;
        }

        if ($this->is_event_closed()) {
            $ajax_handler->add_error_message('Le iscrizioni sono chiuse: evento non disponibile.');
            return;
        }

        $fields = $this->normalize_fields($record->get('fields'));
        $email = sanitize_email($fields['email'] ?? '');
        $phone = sanitize_text_field($fields['phone'] ?? '');
        if ($email && $this->email_exists_for_event($email)) {
            $this->add_form_error($ajax_handler, ['mail', 'email'], 'Esiste già una registrazione con questa email per l’evento.');
        }
        if ($phone && $this->phone_exists_for_event($phone)) {
            $this->add_form_error($ajax_handler, ['telefono', 'phone', 'tel'], 'Esiste già una registrazione con questo numero di telefono per l’evento.');
        }
    }

    public function capture_elementor_submission($record, $handler) {
        $form_name = $record->get_form_settings('form_name');
        if ($form_name !== self::FORM_NAME) {
            return;
        }

        if ($this->is_event_closed()) {
            return;
        }

        $fields = $this->normalize_fields($record->get('fields'));
        $data = [
            'page_id'    => $this->get_event_page_id(),
            'first_name' => sanitize_text_field($fields['first_name'] ?? ''),
            'last_name'  => sanitize_text_field($fields['last_name'] ?? ''),
            'email'      => sanitize_email($fields['email'] ?? ''),
            'phone'      => sanitize_text_field($fields['phone'] ?? ''),
            'age'        => sanitize_text_field($fields['age'] ?? ''),
            'registration_status' => $this->determine_registration_status($this->get_event_page_id()),
            'privacy_policy' => $fields['privacy_policy'] ?? 'Non accettato',
            'marketing_policy' => $fields['marketing_policy'] ?? 'Non accettato',
        ];

        if (empty($data['first_name']) || empty($data['last_name']) || empty($data['email'])) {
            return;
        }

        $insert_result = $this->insert_registration($data);
        if (empty($insert_result['id'])) {
            return;
        }

        $registration_id = (int) $insert_result['id'];
        $registration = $this->get_registration($registration_id);
        if (!$registration) {
            return;
        }

        $this->send_admin_notification($registration);

        if (($registration->registration_status ?? self::STATUS_REGISTERED) === self::STATUS_WAITING_LIST) {
            $this->send_waiting_list_email($registration);
        } else {
            $this->send_registered_email($registration);
        }

        $this->maybe_add_submission_redirect($handler, $registration->registration_status ?? self::STATUS_REGISTERED);
    }

    private function normalize_fields($fields) {
        $map = [];
        foreach ((array) $fields as $field) {
            $id = isset($field['id']) ? sanitize_key($field['id']) : '';
            $title = isset($field['title']) ? sanitize_key(remove_accents($field['title'])) : '';
            $value = isset($field['value']) ? $field['value'] : '';
            if ($id) {
                $map[$id] = $value;
            }
            if ($title) {
                $map[$title] = $value;
            }
        }

        return [
            'first_name' => $map['nome'] ?? $map['first_name'] ?? $map['firstname'] ?? '',
            'last_name'  => $map['cognome'] ?? $map['last_name'] ?? $map['lastname'] ?? '',
            'email'      => $map['mail'] ?? $map['email'] ?? '',
            'phone'      => $map['telefono'] ?? $map['phone'] ?? $map['tel'] ?? '',
            'age'        => $map['eta'] ?? $map['età'] ?? $map['age'] ?? '',
            'privacy_policy' => $this->normalize_consent_value($map['privacy_policy'] ?? $map['privacy'] ?? ''),
            'marketing_policy' => $this->normalize_consent_value($map['marketing_policy'] ?? $map['marketing'] ?? ''),
        ];
    }

    private function normalize_consent_value($value) {
        if (is_array($value)) {
            $value = implode(', ', array_filter(array_map('strval', $value)));
        }

        $value = strtolower(trim((string) $value));
        if ($value === '') {
            return 'Non accettato';
        }

        $rejected_values = ['0', 'false', 'no', 'off', 'non accettato', 'not accepted', 'unchecked'];
        if (in_array($value, $rejected_values, true)) {
            return 'Non accettato';
        }

        $accepted_values = ['1', 'true', 'yes', 'si', 'sì', 'on', 'accepted', 'accettato', 'consent', 'consenso'];
        if (in_array($value, $accepted_values, true)) {
            return 'Accettato';
        }

        return 'Accettato';
    }

    private function format_consent_for_export($value, $accepted_at = '') {
        $normalized = strtolower(trim((string) $value));
        if ($normalized === 'accettato') {
            $date = $accepted_at ? mysql2date('d/m/Y H:i', $accepted_at) : '';
            return $date ? ('Accettata - ' . $date) : 'Accettata';
        }

        return 'Non accettata';
    }

    private function add_form_error($ajax_handler, $field_keys, $message) {
        foreach ((array) $field_keys as $field_key) {
            $ajax_handler->add_error($field_key, $message);
        }
    }

    private function email_exists_for_event($email, $exclude_id = 0, $page_id = null) {
        global $wpdb;
        $page_id = $page_id ?: $this->get_event_page_id();
        $sql = "SELECT id FROM {$this->table_name()} WHERE page_id = %d AND email = %s";
        $args = [$page_id, $email];
        if ($exclude_id) {
            $sql .= ' AND id != %d';
            $args[] = $exclude_id;
        }
        return (bool) $wpdb->get_var($wpdb->prepare($sql, ...$args));
    }

    private function phone_exists_for_event($phone, $exclude_id = 0, $page_id = null) {
        global $wpdb;
        $page_id = $page_id ?: $this->get_event_page_id();
        $normalized_phone = $this->normalize_phone_for_lookup($phone);
        if ($normalized_phone === '') {
            return false;
        }

        $normalized_column = "REPLACE(REPLACE(REPLACE(REPLACE(REPLACE(REPLACE(REPLACE(REPLACE(phone, ' ', ''), '+', ''), '-', ''), '(', ''), ')', ''), '.', ''), '/', ''), '\t', '')";
        $sql = "SELECT id FROM {$this->table_name()} WHERE page_id = %d AND {$normalized_column} = %s";
        $args = [$page_id, $normalized_phone];
        if ($exclude_id) {
            $sql .= ' AND id != %d';
            $args[] = $exclude_id;
        }

        return (bool) $wpdb->get_var($wpdb->prepare($sql, ...$args));
    }

    private function generate_token() {
        return wp_generate_password(20, false, false);
    }

    private function insert_registration($data) {
        global $wpdb;
        $page_id = (int) ($data['page_id'] ?? 0);
        if (!$page_id) {
            return ['id' => 0, 'error' => 'invalid_event'];
        }

        if (!$this->acquire_event_lock($page_id)) {
            return ['id' => 0, 'error' => 'lock_failed'];
        }

        try {
            if ($this->email_exists_for_event($data['email'], 0, $page_id)) {
                return ['id' => 0, 'error' => 'duplicate_email'];
            }

            if (!empty($data['phone']) && $this->phone_exists_for_event($data['phone'], 0, $page_id)) {
                return ['id' => 0, 'error' => 'duplicate_phone'];
            }

            if ($this->is_event_closed()) {
                return ['id' => 0, 'error' => 'event_closed'];
            }

            $now = current_time('mysql');
            $token = $this->generate_token();
            $inserted = $wpdb->insert($this->table_name(), [
                'page_id' => $page_id,
                'first_name' => $data['first_name'],
                'last_name' => $data['last_name'],
                'email' => $data['email'],
                'phone' => $data['phone'],
                'age' => $data['age'] ?? '',
                'registration_status' => $data['registration_status'] ?? self::STATUS_REGISTERED,
                'privacy_policy' => $data['privacy_policy'] ?? '',
                'marketing_policy' => $data['marketing_policy'] ?? '',
                'token' => $token,
                'confirmed_at' => null,
                'created_at' => $now,
                'updated_at' => $now,
            ], ['%d','%s','%s','%s','%s','%s','%s','%s','%s','%s','%s','%s','%s']);

            if (!$inserted) {
                if (!empty($wpdb->last_error)) {
                    if (stripos($wpdb->last_error, 'Duplicate') !== false) {
                        return ['id' => 0, 'error' => 'duplicate_email'];
                    }
                    return ['id' => 0, 'error' => 'db_error'];
                }
                return ['id' => 0, 'error' => 'insert_failed'];
            }

            return ['id' => (int) $wpdb->insert_id, 'error' => ''];
        } finally {
            $this->release_event_lock($page_id);
        }
    }

    private function get_registration($id) {
        global $wpdb;
        return $wpdb->get_row($wpdb->prepare("SELECT * FROM {$this->table_name()} WHERE id = %d", $id));
    }

    private function get_registration_by_token($token) {
        global $wpdb;
        return $wpdb->get_row($wpdb->prepare("SELECT * FROM {$this->table_name()} WHERE token = %s", $token));
    }

    private function get_qr_upload_dir() {
        $uploads = wp_upload_dir();
        $dir = trailingslashit($uploads['basedir']) . 'mova-qr/';
        $url = trailingslashit($uploads['baseurl']) . 'mova-qr/';
        if (!file_exists($dir)) {
            wp_mkdir_p($dir);
        }
        return [$dir, $url];
    }

    private function get_registration_public_url($registration, $mode = 'detail') {
        if ($mode === 'checkin') {
            return home_url('/mova-checkin/' . rawurlencode($registration->token) . '/');
        }
        return home_url('/mova-ticket/' . rawurlencode($registration->token) . '/');
    }

    private function generate_qr_for_registration($registration) {
        if (!class_exists('QRcode')) {
            require_once __DIR__ . '/vendor/phpqrcode/qrlib.php';
        }

        list($dir, $url) = $this->get_qr_upload_dir();
        $filename = 'mova-registration-' . $registration->token . '.png';
        $path = $dir . $filename;
        $public = $url . $filename;
        QRcode::png($this->get_registration_public_url($registration, 'checkin'), $path, QR_ECLEVEL_M, 6, 2);

        global $wpdb;
        $wpdb->update($this->table_name(), [
            'qr_file' => esc_url_raw($public),
            'updated_at' => current_time('mysql'),
        ], ['id' => (int) $registration->id], ['%s','%s'], ['%d']);

        return $public;
    }

    private function get_site_mail_headers($is_html = false, $reply_to = '') {
        $site_name = wp_specialchars_decode(get_bloginfo('name'), ENT_QUOTES);
        $from_email = sanitize_email(get_option('admin_email'));
        $headers = [];

        if ($from_email) {
            $headers[] = 'From: ' . $site_name . ' <' . $from_email . '>';
        }

        if ($reply_to && is_email($reply_to)) {
            $headers[] = 'Reply-To: ' . sanitize_email($reply_to);
        }

        if ($is_html) {
            $headers[] = 'Content-Type: text/html; charset=UTF-8';
        }

        return $headers;
    }

    public function capture_mail_error($wp_error) {
        update_option(self::OPTION_LAST_MAIL_STATUS, [
            'status' => 'failed',
            'context' => $this->current_mail_context,
            'message' => $wp_error instanceof WP_Error ? $wp_error->get_error_message() : 'Errore sconosciuto',
            'time' => current_time('mysql'),
        ], false);
    }

    private function send_mail($to, $subject, $message, $headers = [], $context = '') {
        $this->current_mail_context = $context;
        $sent = wp_mail($to, $subject, $message, $headers);

        if ($sent) {
            update_option(self::OPTION_LAST_MAIL_STATUS, [
                'status' => 'sent',
                'context' => $context,
                'message' => 'Invio eseguito tramite wp_mail().',
                'time' => current_time('mysql'),
            ], false);
        } else {
            $existing = get_option(self::OPTION_LAST_MAIL_STATUS, []);
            if (!is_array($existing) || empty($existing['time']) || $existing['time'] !== current_time('mysql')) {
                update_option(self::OPTION_LAST_MAIL_STATUS, [
                    'status' => 'failed',
                    'context' => $context,
                    'message' => 'wp_mail() ha restituito false. Verificare SMTP/transactional mail sul server.',
                    'time' => current_time('mysql'),
                ], false);
            }
        }

        $this->current_mail_context = '';
        return $sent;
    }

    private function send_admin_notification($registration) {
        $to = sanitize_email(get_option('admin_email'));
        if (!$to) {
            return false;
        }

        $subject = 'Nuova iscrizione evento MOVA';
        $body = "Nuova iscrizione registrata.\n\n" .
            "Stato iscrizione: {$this->get_registration_status_label($registration->registration_status ?? self::STATUS_REGISTERED)}\n" .
            "Nome: {$registration->first_name}\n" .
            "Cognome: {$registration->last_name}\n" .
            "Email: {$registration->email}\n" .
            "Telefono: {$registration->phone}\n" .
            "Eta: {$registration->age}\n" .
            "Data registrazione: {$registration->created_at}\n" .
            "Scheda registrazione: {$this->get_registration_public_url($registration, 'detail')}\n";

        return $this->send_mail(
            $to,
            $subject,
            $body,
            $this->get_site_mail_headers(false, $registration->email),
            'admin_notification'
        );
    }

    private function send_registered_email($registration) {
        $to = sanitize_email($registration->email);
        if (!$to) {
            return false;
        }

        $subject = self::EVENT_TITLE . ' - Registrazione ricevuta';
        $message = $this->build_registered_email($registration);

        return $this->send_mail(
            $to,
            $subject,
            $message,
            $this->get_site_mail_headers(true),
            'registered_notification'
        );
    }

    private function send_waiting_list_email($registration) {
        $to = sanitize_email($registration->email);
        if (!$to) {
            return false;
        }

        $subject = self::EVENT_TITLE . ' - Inserimento in Waiting List';
        $message = $this->build_waiting_list_email($registration);

        return $this->send_mail(
            $to,
            $subject,
            $message,
            $this->get_site_mail_headers(true),
            'waiting_list_notification'
        );
    }

    private function send_user_confirmation($registration) {
        $to = sanitize_email($registration->email);
        if (!$to) {
            return false;
        }

        $qr = $registration->qr_file ?: $this->generate_qr_for_registration($registration);
        $subject = self::EVENT_TITLE . ' - Conferma registrazione';
        $message = $this->build_confirmation_email($registration, $qr);

        $sent = $this->send_mail(
            $to,
            $subject,
            $message,
            $this->get_site_mail_headers(true),
            'user_confirmation'
        );

        if ($sent) {
            return true;
        }

        return $this->send_mail(
            $to,
            $subject,
            $this->build_confirmation_email_text($registration),
            $this->get_site_mail_headers(false),
            'user_confirmation_fallback'
        );
    }

    private function build_registration_mail_shell($name, $body_html) {
        ob_start();
        ?>
        <table role="presentation" width="100%" cellspacing="0" cellpadding="0" style="width:100%;background:#f3f3f3;border-collapse:collapse;">
            <tr>
                <td align="center" style="padding:24px 12px;">
                    <table role="presentation" width="100%" cellspacing="0" cellpadding="0" style="max-width:700px;width:100%;background:#ffffff;border-collapse:collapse;font-family:Arial,Helvetica,sans-serif;color:#111111;">
                        <tr>
                            <td><img src="<?php echo esc_url(self::HEADER_IMAGE); ?>" alt="MOVA" style="display:block;width:100%;height:auto;border:0;"></td>
                        </tr>
                        <tr>
                            <td style="padding:28px;">
                                <p style="margin:0 0 16px;font-size:16px;line-height:24px;">Ciao <?php echo esc_html($name); ?>,</p>
                                <?php echo $body_html; ?>
                            </td>
                        </tr>
                    </table>
                </td>
            </tr>
        </table>
        <?php
        return ob_get_clean();
    }

    private function build_registered_email($registration) {
        $name = trim($registration->first_name . ' ' . $registration->last_name);
        $address = esc_html($this->get_event_address());
        $body = ''
            . '<p style="margin:0 0 20px;font-size:15px;line-height:24px;">la tua registrazione è stata ricevuta correttamente. A ridosso dell\'evento ti contatteremo per confermare la tua presenza.</p>'
            . '<h2 style="margin:0 0 8px;font-size:26px;line-height:32px;">' . esc_html(self::EVENT_TITLE) . '</h2>'
            . '<p style="margin:0 0 4px;font-size:15px;line-height:22px;"><strong>' . esc_html(self::EVENT_DATE) . '</strong></p>'
            . '<p style="margin:0 0 20px;font-size:15px;line-height:22px;">' . esc_html(self::EVENT_LOCATION) . '<br>' . $address . '</p>'
            . '<p style="margin:0;font-size:14px;line-height:22px;">Questa email non contiene ancora il QR code di accesso. Se la tua partecipazione verrà confermata, riceverai successivamente una comunicazione dedicata con il QR personale.</p>';

        return $this->build_registration_mail_shell($name, $body);
    }

    private function build_waiting_list_email($registration) {
        $name = trim($registration->first_name . ' ' . $registration->last_name);
        $address = esc_html($this->get_event_address());
        $body = ''
            . '<p style="margin:0 0 20px;font-size:15px;line-height:24px;">la tua registrazione è stata ricevuta e sei stato inserito in <strong>Waiting List</strong>.</p>'
            . '<h2 style="margin:0 0 8px;font-size:26px;line-height:32px;">' . esc_html(self::EVENT_TITLE) . '</h2>'
            . '<p style="margin:0 0 4px;font-size:15px;line-height:22px;"><strong>' . esc_html(self::EVENT_DATE) . '</strong></p>'
            . '<p style="margin:0 0 20px;font-size:15px;line-height:22px;">' . esc_html(self::EVENT_LOCATION) . '<br>' . $address . '</p>'
            . '<p style="margin:0;font-size:14px;line-height:22px;">Nel caso si liberassero posti disponibili, verrai ricontattato in ordine cronologico. Questa email non contiene il QR code di accesso.</p>';

        return $this->build_registration_mail_shell($name, $body);
    }

    private function build_confirmation_email($registration, $qr_url) {
        $name = esc_html(trim($registration->first_name . ' ' . $registration->last_name));
        $address = esc_html($this->get_event_address());
        $detail_url = esc_url($this->get_registration_public_url($registration, 'detail'));
        ob_start();
        ?>
        <table role="presentation" width="100%" cellspacing="0" cellpadding="0" style="width:100%;background:#f3f3f3;border-collapse:collapse;">
            <tr>
                <td align="center" style="padding:24px 12px;">
                    <table role="presentation" width="100%" cellspacing="0" cellpadding="0" style="max-width:700px;width:100%;background:#ffffff;border-collapse:collapse;font-family:Arial,Helvetica,sans-serif;color:#111111;">
                        <tr>
                            <td><img src="<?php echo esc_url(self::HEADER_IMAGE); ?>" alt="MOVA" style="display:block;width:100%;height:auto;border:0;"></td>
                        </tr>
                        <tr>
                            <td style="padding:28px;">
                                <p style="margin:0 0 16px;font-size:16px;line-height:24px;">Ciao <?php echo $name; ?>,</p>
                                <p style="margin:0 0 20px;font-size:15px;line-height:24px;">la tua registrazione è confermata. Ti aspettiamo all’evento MOVA.</p>
                                <h2 style="margin:0 0 8px;font-size:26px;line-height:32px;"><?php echo esc_html(self::EVENT_TITLE); ?></h2>
                                <p style="margin:0 0 4px;font-size:15px;line-height:22px;"><strong><?php echo esc_html(self::EVENT_DATE); ?></strong></p>
                                <p style="margin:0 0 20px;font-size:15px;line-height:22px;"><?php echo esc_html(self::EVENT_LOCATION); ?><br><?php echo $address; ?></p>

                                <table role="presentation" width="100%" cellspacing="0" cellpadding="0" style="border-collapse:collapse;background:#fafafa;border:1px solid #e6e6e6;">
                                    <tr>
                                        <td align="center" style="padding:24px;">
                                            <p style="margin:0 0 10px;font-size:14px;line-height:20px;"><strong>QR CODE ACCESSO EVENTO</strong></p>
                                            <img src="<?php echo esc_url($qr_url); ?>" alt="QR Code" style="display:block;width:220px;max-width:100%;height:auto;margin:0 auto 12px;border:0;">
                                            <p style="margin:0 0 12px;font-size:13px;line-height:20px;">Presenta questo QR code in fase di accesso. La scansione registrerà automaticamente il check-in.</p>
                                            <p style="margin:0;font-size:12px;line-height:18px;"><a href="<?php echo $detail_url; ?>">Apri la scheda registrazione</a></p>
                                        </td>
                                    </tr>
                                </table>

                                <h3 style="margin:28px 0 12px;font-size:20px;line-height:26px;">Programma</h3>
                                <p style="margin:0 0 10px;font-size:14px;line-height:22px;"><strong>Dalle 15:00 alle 17:00 – Sample Lovers Experience</strong><br>Ricevi sconti esclusivi e scopri il mondo MOVA con vantaggi riservati ai partecipanti.</p>
                                <p style="margin:0 0 10px;font-size:14px;line-height:22px;"><strong>Dalle 17:00 alle 19:00 – Isole Esperienziali</strong><br>Prova, gioca, sfidati e vivi MOVA in prima persona.</p>
                                <p style="margin:0 0 10px;font-size:14px;line-height:22px;"><strong>Dalle 19:00 alle 19:30 – PoretCast LIVE</strong><br>Sul palco Giacomo Poretti insieme a Luka Modrić per un momento unico tra racconto, ironia e ispirazione.</p>
                                <p style="margin:0 0 10px;font-size:14px;line-height:22px;"><strong>Dalle 19:30 alle 20:00 – Meet&amp;Greet con Luka Modrić</strong><br>Un’occasione esclusiva per incontrare da vicino una leggenda del calcio.</p>
                                <p style="margin:0 0 0;font-size:14px;line-height:22px;"><strong>Per tutta la durata dell’evento</strong><br>Esposizione prodotti MOVA.</p>
                            </td>
                        </tr>
                    </table>
                </td>
            </tr>
        </table>
        <?php
        return ob_get_clean();
    }

    private function build_confirmation_email_text($registration) {
        $name = trim($registration->first_name . ' ' . $registration->last_name);
        $detail_url = $this->get_registration_public_url($registration, 'detail');
        $event_address = $this->get_event_address();

        return "Ciao {$name},\n\n"
            . "la tua registrazione a " . self::EVENT_TITLE . " e' confermata.\n\n"
            . "Data: " . self::EVENT_DATE . "\n"
            . "Location: " . self::EVENT_LOCATION . "\n"
            . "Indirizzo: {$event_address}\n\n"
            . "Apri la tua scheda registrazione: {$detail_url}\n\n"
            . "Questa email e' stata generata automaticamente dal plugin MOVA Event Manager.";
    }

    private function mark_registration_checked_in($id, $source = 'admin') {
        global $wpdb;
        $now = current_time('mysql');
        return (bool) $wpdb->query($wpdb->prepare(
            "UPDATE {$this->table_name()} SET checked_in_at = %s, checkin_source = %s, updated_at = %s WHERE id = %d AND checked_in_at IS NULL",
            $now,
            $source,
            $now,
            (int) $id
        ));
    }

    private function get_checkin_source_label($source) {
        if ($source === 'qr') {
            return 'QR';
        }
        if ($source === 'admin') {
            return 'Admin';
        }
        return '';
    }

    private function get_checkin_display($registration) {
        if (empty($registration->checked_in_at)) {
            return '—';
        }

        $label = $this->get_checkin_source_label($registration->checkin_source ?? '');
        $date = mysql2date('d/m/Y H:i', $registration->checked_in_at);
        return $label ? ($date . ' (' . $label . ')') : $date;
    }

    private function get_registration_status_badge($registration) {
        $status = $registration->registration_status ?? self::STATUS_REGISTERED;

        if ($status === self::STATUS_CONFIRMED) {
            return '<span class="mova-badge mova-badge--ok">Iscrizione Confermata</span>';
        }

        if ($status === self::STATUS_WAITING_LIST) {
            return '<span class="mova-badge mova-badge--wait">Waiting List</span>';
        }

        return '<span class="mova-badge mova-badge--neutral">Iscritto</span>';
    }

    private function get_access_status_badge($registration) {
        $status = $registration->registration_status ?? self::STATUS_REGISTERED;

        if (!empty($registration->checked_in_at)) {
            return '<span class="mova-badge mova-badge--ok">Check-in registrato</span>';
        }

        if ($status === self::STATUS_CONFIRMED) {
            return '<span class="mova-badge mova-badge--error">Check-in non registrato</span>';
        }

        return '<span class="mova-badge mova-badge--neutral">—</span>';
    }

    private function get_registration_actions($registration, $base_url) {
        $id = (int) $registration->id;
        $status = $registration->registration_status ?? self::STATUS_REGISTERED;
        $is_public_dashboard = strpos($base_url, home_url('/')) === 0;
        $edit_url = $is_public_dashboard
            ? add_query_arg(['edit' => $id], $base_url)
            : $base_url . '&edit=' . $id;
        $redirect_args = ['redirect_to' => $base_url];
        $actions = [
            '' => 'Azioni',
            $edit_url => 'Modifica iscrizione',
        ];

        if ($status !== self::STATUS_CONFIRMED) {
            $actions[wp_nonce_url(add_query_arg($redirect_args, admin_url('admin-post.php?action=mova_confirm_registration&id=' . $id)), 'mova_confirm_' . $id)] = 'Conferma iscrizione + invia QR';
        } else {
            $actions[wp_nonce_url(add_query_arg($redirect_args, admin_url('admin-post.php?action=mova_confirm_registration&id=' . $id)), 'mova_confirm_' . $id)] = 'Reinvia mail conferma';
        }

        if (!empty($registration->checked_in_at)) {
            $actions[wp_nonce_url(add_query_arg($redirect_args, admin_url('admin-post.php?action=mova_undo_checkin_registration&id=' . $id)), 'mova_undo_checkin_' . $id)] = 'Annulla check-in';
        } elseif ($status === self::STATUS_CONFIRMED) {
            $actions[wp_nonce_url(add_query_arg($redirect_args, admin_url('admin-post.php?action=mova_checkin_registration&id=' . $id)), 'mova_checkin_' . $id)] = 'Registra check-in';
        }

        if ($status === self::STATUS_CONFIRMED) {
            $actions[wp_nonce_url(add_query_arg($redirect_args, admin_url('admin-post.php?action=mova_regenerate_qr_registration&id=' . $id)), 'mova_regenerate_qr_' . $id)] = 'Rigenera QR';
        }
        $actions[$this->get_registration_public_url($registration, 'detail')] = 'Apri scheda';

        if (!empty($registration->qr_file)) {
            $actions[$registration->qr_file] = 'Apri QR';
        }

        $actions[wp_nonce_url(add_query_arg($redirect_args, admin_url('admin-post.php?action=mova_delete_registration&id=' . $id)), 'mova_delete_registration_' . $id)] = 'Elimina iscrizione';

        return $actions;
    }

    private function render_registration_action_menu($registration, $base_url, $context = 'table') {
        $actions = $this->get_registration_actions($registration, $base_url);
        $id = (int) $registration->id;
        ?>
        <form class="mova-action-form" data-context="<?php echo esc_attr($context); ?>">
            <label class="screen-reader-text" for="mova-action-<?php echo $context . '-' . $id; ?>">Azioni iscrizione <?php echo $id; ?></label>
            <select id="mova-action-<?php echo $context . '-' . $id; ?>" class="mova-action-select" data-registration-action>
                <?php foreach ($actions as $url => $label): ?>
                    <option value="<?php echo esc_url($url); ?>"><?php echo esc_html($label); ?></option>
                <?php endforeach; ?>
            </select>
            <button type="button" class="button button-secondary mova-action-go" data-registration-action-trigger>Vai</button>
        </form>
        <?php
    }

    public function handle_public_routes() {
        if (get_query_var('mova_dashboard')) {
            $this->render_public_dashboard_page();
            return;
        }

        $this->render_public_registration_page();
    }

    public function render_public_registration_page() {
        $token = get_query_var('mova_registration');
        if (!$token) {
            return;
        }

        $registration = $this->get_registration_by_token(sanitize_text_field($token));
        $is_checkin_scan = (string) get_query_var('mova_checkin') === '1' || (isset($_GET['checkin']) && sanitize_text_field(wp_unslash($_GET['checkin'])) === '1');
        $checkin_message = '';
        $checkin_alert_class = '';

        if ($registration && $is_checkin_scan) {
            if (($registration->registration_status ?? self::STATUS_REGISTERED) !== self::STATUS_CONFIRMED) {
                $checkin_message = 'Check-in non disponibile: iscrizione non ancora confermata.';
                $checkin_alert_class = 'mova-alert--error';
            } elseif (empty($registration->checked_in_at)) {
                $this->mark_registration_checked_in($registration->id, 'qr');
                $registration = $this->get_registration($registration->id);
                $checkin_message = 'Check-in registrato correttamente.';
                $checkin_alert_class = 'mova-alert--success';
            } else {
                $checkin_message = 'Check-in già registrato in precedenza.';
                $checkin_alert_class = 'mova-alert--error';
            }
        }

        status_header($registration ? 200 : 404);
        nocache_headers();
        ?>
        <!DOCTYPE html>
        <html <?php language_attributes(); ?>>
        <head>
            <meta charset="<?php bloginfo('charset'); ?>">
            <meta name="viewport" content="width=device-width, initial-scale=1">
            <title><?php echo esc_html(self::EVENT_TITLE); ?></title>
            <?php wp_head(); ?>
        </head>
        <body>
            <div class="mova-wrap">
                <div class="mova-card">
                    <img src="<?php echo esc_url(self::HEADER_IMAGE); ?>" alt="MOVA" style="display:block;width:100%;height:auto;">
                    <div class="mova-content">
                        <?php if (!$registration): ?>
                            <h1 style="margin-top:0;">Registrazione non trovata</h1>
                            <p>Il codice scansionato non corrisponde a nessuna registrazione valida.</p>
                        <?php else: ?>
                            <?php if ($checkin_message): ?>
                                <div class="mova-alert <?php echo esc_attr($checkin_alert_class); ?>">
                                    <strong><?php echo esc_html($checkin_message); ?></strong>
                                    <?php if (!empty($registration->checked_in_at)): ?>
                                        <div style="margin-top:6px;">Ora registrata: <?php echo esc_html(mysql2date('d/m/Y H:i', $registration->checked_in_at)); ?></div>
                                    <?php endif; ?>
                                </div>
                            <?php endif; ?>
                            <h1 style="margin:0 0 8px;"><?php echo esc_html(self::EVENT_TITLE); ?></h1>
                            <p style="margin:0 0 24px;"><strong><?php echo esc_html(self::EVENT_DATE); ?></strong> | <?php echo esc_html(self::EVENT_LOCATION); ?></p>
                            <div class="mova-grid">
                                <div class="mova-box"><strong>Nome</strong><br><?php echo esc_html($registration->first_name); ?></div>
                                <div class="mova-box"><strong>Cognome</strong><br><?php echo esc_html($registration->last_name); ?></div>
                                <div class="mova-box"><strong>Email</strong><br><?php echo esc_html($registration->email); ?></div>
                                <div class="mova-box"><strong>Telefono</strong><br><?php echo esc_html($registration->phone ?: '—'); ?></div>
                                <div class="mova-box"><strong>Età</strong><br><?php echo esc_html($registration->age ?: '—'); ?></div>
                                <div class="mova-box"><strong>Stato iscrizione</strong><br><?php echo esc_html($this->get_registration_status_label($registration->registration_status ?? self::STATUS_REGISTERED)); ?></div>
                                <div class="mova-box"><strong>Registrazione</strong><br><?php echo esc_html(mysql2date('d/m/Y H:i', $registration->created_at)); ?></div>
                                <div class="mova-box"><strong>Check-in</strong><br><?php echo esc_html($this->get_checkin_display($registration)); ?></div>
                            </div>
                        <?php endif; ?>
                    </div>
                </div>
            </div>
            <?php wp_footer(); ?>
        </body>
        </html>
        <?php
        exit;
    }

    private function render_dashboard_shell($callback, $title = 'MOVA Event Dashboard') {
        status_header(200);
        nocache_headers();
        ?>
        <!DOCTYPE html>
        <html <?php language_attributes(); ?>>
        <head>
            <meta charset="<?php bloginfo('charset'); ?>">
            <meta name="viewport" content="width=device-width, initial-scale=1">
            <title><?php echo esc_html($title); ?></title>
            <?php wp_head(); ?>
        </head>
        <body class="mova-dashboard-page">
            <?php call_user_func($callback); ?>
            <?php wp_footer(); ?>
        </body>
        </html>
        <?php
        exit;
    }

    public function render_public_dashboard_page() {
        $dashboard_url = $this->get_dashboard_url();

        if (!is_user_logged_in()) {
            $this->render_dashboard_shell(function () use ($dashboard_url) {
                ?>
                <div class="mova-auth">
                    <div class="mova-auth__grid">
                        <section class="mova-auth__intro">
                            <span class="mova-auth__eyebrow">MOVA Event</span>
                            <h1>Dashboard check-in e iscrizioni</h1>
                            <p>Accedi con il tuo account editoriale per gestire registrazioni, check-in e QR in un’interfaccia ottimizzata anche da mobile.</p>
                            <div class="mova-auth__highlights">
                                <span>Accesso rapido</span>
                                <span>Check-in live</span>
                                <span>Export XLSX</span>
                            </div>
                        </section>
                        <section class="mova-auth__panel">
                            <div class="mova-auth__panel-header">
                                <h2>Accedi</h2>
                                <p>Usa il tuo account WordPress per entrare nella dashboard MOVA.</p>
                            </div>
                            <div class="mova-dashboard-login">
                                <?php
                                wp_login_form([
                                    'redirect' => $dashboard_url,
                                    'label_username' => 'Email o username',
                                    'label_password' => 'Password',
                                    'label_log_in' => 'Accedi alla dashboard',
                                    'remember' => true,
                                ]);
                                ?>
                            </div>
                        </section>
                    </div>
                </div>
                <?php
            }, 'Accesso dashboard MOVA');
        }

        if (!$this->can_access_dashboard()) {
            $this->render_dashboard_shell(function () {
                ?>
                <div class="mova-auth">
                    <div class="mova-auth__grid mova-auth__grid--single">
                        <section class="mova-auth__panel">
                            <div class="mova-auth__panel-header">
                                <span class="mova-auth__eyebrow">Accesso negato</span>
                                <h2>Permessi insufficienti</h2>
                                <p>Il tuo account non ha i permessi necessari per visualizzare la dashboard MOVA.</p>
                            </div>
                        </section>
                    </div>
                </div>
                <?php
            }, 'Accesso non autorizzato');
        }

        $this->render_dashboard_shell(function () {
            echo '<div class="mova-dashboard-public">';
            echo '<div class="mova-dashboard-public__frame">';
            $this->render_admin_page();
            echo '</div></div>';
        });
    }

    public function admin_menu() {
        add_menu_page('MOVA Event', 'MOVA Event', 'edit_pages', 'mova-event', [$this, 'render_admin_page'], 'dashicons-tickets-alt', 26);
    }

    private function render_mail_status_notice() {
        $status = get_option(self::OPTION_LAST_MAIL_STATUS, []);
        if (!is_array($status) || empty($status['status'])) {
            return;
        }


        $context = !empty($status['context']) ? ' [' . esc_html($status['context']) . ']' : '';
        $message = !empty($status['message']) ? esc_html($status['message']) : '';
        $time = !empty($status['time']) ? esc_html(mysql2date('d/m/Y H:i', $status['time'])) : '';

        echo '<div class="' . esc_attr($class) . '"><p><strong>Stato invio mail plugin</strong>' . $context . ': ' . $message;
        if ($time) {
            echo ' <em>(' . $time . ')</em>';
        }
        echo '</p></div>';
    }

    public function render_admin_page() {
        if (!$this->can_access_dashboard()) {
            wp_die('Accesso negato');
        }

        global $wpdb;
        $edit_id = isset($_GET['edit']) ? (int) $_GET['edit'] : 0;
        $search = isset($_GET['s']) ? sanitize_text_field(wp_unslash($_GET['s'])) : '';
        $checkin = isset($_GET['checkin']) ? sanitize_text_field(wp_unslash($_GET['checkin'])) : '';
        $duplicate = isset($_GET['duplicate']) ? (int) $_GET['duplicate'] : 0;
        $duplicate_phone = isset($_GET['duplicate_phone']) ? (int) $_GET['duplicate_phone'] : 0;

        $where = ['page_id = %d'];
        $args = [$this->get_event_page_id()];

        if ($search !== '') {
            $where[] = '(first_name LIKE %s OR last_name LIKE %s OR email LIKE %s OR phone LIKE %s)';
            $like = '%' . $wpdb->esc_like($search) . '%';
            array_push($args, $like, $like, $like, $like);
        }

        if ($checkin === 'done') {
            $where[] = 'checked_in_at IS NOT NULL';
        } elseif ($checkin === 'pending') {
            $where[] = 'checked_in_at IS NULL';
        }

        $sql = "SELECT * FROM {$this->table_name()} WHERE " . implode(' AND ', $where) . ' ORDER BY created_at DESC';
        $rows = $wpdb->get_results($wpdb->prepare($sql, ...$args));
        $editing = $edit_id ? $this->get_registration($edit_id) : null;
        $base_url = $this->get_dashboard_base_url();
        $registration_count = $this->get_registration_count();
        $checked_in_count = $this->get_checkin_count();
        $pending_count = max(0, $registration_count - $checked_in_count);
        $capacity = $this->get_event_capacity();
        ?>
        <div class="wrap mova-admin-wrap">
            <div class="mova-hero">
                <div class="mova-hero__content">
                    <h1>MOVA Event</h1>
                    <p>Gestione centralizzata delle iscrizioni per <strong><?php echo esc_html(self::EVENT_TITLE); ?></strong>, con check-in, QR e monitoraggio dello stato evento.</p>
                    <div class="mova-hero__chips">
                        <span class="mova-hero__chip">Iscritti: <?php echo esc_html((string) $registration_count); ?></span>
                        <span class="mova-hero__chip">Check-in: <?php echo esc_html((string) $checked_in_count); ?></span>
                        <span class="mova-hero__chip">Capienza: <?php echo esc_html((string) $capacity); ?></span>
                    </div>
                </div>
                <div class="mova-badge <?php echo $this->get_event_status() === 'Chiuso' ? 'mova-badge--wait' : 'mova-badge--ok'; ?>">
                    <span><?php echo esc_html($this->get_event_status()); ?></span>
                </div>
            </div>

            <?php if (!get_query_var('mova_dashboard')): ?>
                <?php $this->render_mail_status_notice(); ?>
            <?php endif; ?>

            <?php if ($duplicate): ?>
                <div class="notice notice-error"><p>Esiste già una registrazione con questa email per l’evento. Modifica non salvata.</p></div>
            <?php endif; ?>
            <?php if ($duplicate_phone): ?>
                <div class="notice notice-error"><p>Esiste già una registrazione con questo numero di telefono per l’evento. Modifica non salvata.</p></div>
            <?php endif; ?>

            <div class="mova-kpis">
                <div class="mova-kpi"><span class="mova-kpi-label">Iscritti</span><span class="mova-kpi-value"><?php echo esc_html((string) $registration_count); ?></span><span class="mova-kpi-meta">Totale registrazioni raccolte</span></div>
                <div class="mova-kpi"><span class="mova-kpi-label">Check-in</span><span class="mova-kpi-value"><?php echo esc_html((string) $checked_in_count); ?></span><span class="mova-kpi-meta">Accessi registrati via QR o admin</span></div>
                <div class="mova-kpi"><span class="mova-kpi-label">In attesa</span><span class="mova-kpi-value"><?php echo esc_html((string) $pending_count); ?></span><span class="mova-kpi-meta">Iscritti non ancora entrati</span></div>
                <div class="mova-kpi"><span class="mova-kpi-label">Capienza</span><span class="mova-kpi-value"><?php echo esc_html((string) $this->get_event_capacity()); ?></span><span class="mova-kpi-meta">Indirizzo: <?php echo esc_html($this->get_event_address()); ?></span></div>
            </div>

            <?php if (!get_query_var('mova_dashboard')): ?>
                <div class="mova-panel">
                    <h2>Diagnostica invio mail</h2>
                    <p style="margin:0;">Il plugin usa <code>wp_mail()</code>. Se le email arrivano solo con Elementor Site Mailer attivo, il server non sta usando un trasporto email affidabile senza SMTP o mailer transazionale.</p>
                </div>
            <?php endif; ?>

            <?php if ($editing): ?>
                <div class="mova-panel" style="max-width:820px;">
                    <h2>Modifica iscrizione</h2>
                    <form method="post" action="<?php echo esc_url(admin_url('admin-post.php')); ?>">
                        <?php wp_nonce_field('mova_save_registration'); ?>
                        <input type="hidden" name="action" value="mova_save_registration">
                        <input type="hidden" name="id" value="<?php echo (int) $editing->id; ?>">
                        <input type="hidden" name="redirect_to" value="<?php echo esc_url($base_url); ?>">
                        <table class="form-table">
                            <tr><th>Nome</th><td><input type="text" class="regular-text" name="first_name" value="<?php echo esc_attr($editing->first_name); ?>"></td></tr>
                            <tr><th>Cognome</th><td><input type="text" class="regular-text" name="last_name" value="<?php echo esc_attr($editing->last_name); ?>"></td></tr>
                            <tr><th>Email</th><td><input type="email" class="regular-text" name="email" value="<?php echo esc_attr($editing->email); ?>"></td></tr>
                            <tr><th>Telefono</th><td><input type="text" class="regular-text" name="phone" value="<?php echo esc_attr($editing->phone); ?>"></td></tr>
                            <tr><th>Età</th><td><input type="text" class="regular-text" name="age" value="<?php echo esc_attr($editing->age); ?>"></td></tr>
                            <tr><th>Stato iscrizione</th><td><strong><?php echo esc_html($this->get_registration_status_label($editing->registration_status ?? self::STATUS_REGISTERED)); ?></strong></td></tr>
                            <tr><th>Note</th><td><textarea class="large-text" rows="4" name="notes"><?php echo esc_textarea($editing->notes); ?></textarea></td></tr>
                        </table>
                        <?php submit_button('Salva modifiche'); ?>
                        <a class="button" href="<?php echo esc_url($base_url); ?>">Annulla</a>
                    </form>
                </div>
            <?php endif; ?>

            <div class="mova-panel">
                <h2>Filtri e strumenti</h2>
                <form method="get" action="<?php echo esc_url($base_url); ?>" class="mova-toolbar">
                <?php if (!get_query_var('mova_dashboard')): ?>
                <input type="hidden" name="page" value="mova-event">
                <?php endif; ?>
                <input type="search" name="s" value="<?php echo esc_attr($search); ?>" placeholder="Cerca per nome, email o telefono">
                <select name="checkin">
                    <option value="">Tutti</option>
                    <option value="pending" <?php selected($checkin, 'pending'); ?>>Da check-in</option>
                    <option value="done" <?php selected($checkin, 'done'); ?>>Check-in effettuato</option>
                </select>
                <button class="button button-primary">Filtra</button>
                <a class="button" href="<?php echo esc_url($base_url); ?>">Reset</a>
                <a class="button button-secondary" href="<?php echo esc_url(wp_nonce_url(admin_url('admin-post.php?action=mova_export_registrations&s=' . rawurlencode($search) . '&checkin=' . rawurlencode($checkin)), 'mova_export_registrations')); ?>">Esporta XLSX</a>
                </form>
            </div>

            <div class="mova-panel mova-table-wrap">
                <h2>Lista iscrizioni</h2>
                <?php if (empty($rows)): ?>
                    <p>Nessuna iscrizione trovata.</p>
                <?php else: ?>
                    <div class="mova-desktop-table">
                        <table class="widefat fixed striped">
                        <thead>
                            <tr>
                                <th>ID</th><th>Partecipante</th><th>Età</th><th>Telefono</th><th>Registrato il</th><th>Stato iscrizione</th><th>Check-in</th><th>Stato accesso</th><th>QR</th><th>Azioni</th>
                            </tr>
                        </thead>
                        <tbody>
                        <?php foreach ($rows as $row): ?>
                            <tr>
                                <td><?php echo (int) $row->id; ?></td>
                                <td class="mova-attendee">
                                    <strong class="mova-attendee__name"><?php echo esc_html(trim($row->first_name . ' ' . $row->last_name)); ?></strong>
                                    <a class="mova-attendee__email" href="mailto:<?php echo antispambot(esc_attr($row->email)); ?>"><?php echo esc_html($row->email); ?></a>
                                </td>
                                <td><?php echo esc_html($row->age ?: '—'); ?></td>
                                <td><?php echo esc_html($row->phone); ?></td>
                                <td><span class="mova-table-date"><?php echo esc_html(mysql2date('d/m/Y H:i', $row->created_at)); ?></span></td>
                                <td><?php echo $this->get_registration_status_badge($row); ?></td>
                                <td class="mova-checkin"><?php echo esc_html($this->get_checkin_display($row)); ?></td>
                                <td><?php echo $this->get_access_status_badge($row); ?></td>
                                <td><?php if ($row->qr_file): ?><a class="mova-table-link" href="<?php echo esc_url($row->qr_file); ?>" target="_blank">Apri QR</a><?php else: ?><span class="mova-muted">—</span><?php endif; ?></td>
                                <td class="mova-actions">
                                    <?php $this->render_registration_action_menu($row, $base_url, 'table'); ?>
                                </td>
                            </tr>
                        <?php endforeach; ?>
                        </tbody>
                        </table>
                    </div>

                    <div class="mova-mobile-list">
                        <?php foreach ($rows as $row): ?>
                            <details class="mova-mobile-card">
                                <summary class="mova-mobile-card__summary">
                                    <div class="mova-mobile-card__heading">
                                        <strong>#<?php echo (int) $row->id; ?> · <?php echo esc_html(trim($row->first_name . ' ' . $row->last_name)); ?></strong>
                                        <span class="mova-mobile-card__meta"><?php echo esc_html(mysql2date('d/m/Y H:i', $row->created_at)); ?></span>
                                    </div>
                                    <div class="mova-mobile-card__status">
                                        <?php echo $this->get_access_status_badge($row); ?>
                                    </div>
                                </summary>
                                <div class="mova-mobile-card__body">
                                    <div class="mova-mobile-card__grid">
                                        <div><span class="mova-mobile-card__label">Email</span><a href="mailto:<?php echo antispambot(esc_attr($row->email)); ?>"><?php echo esc_html($row->email); ?></a></div>
                                        <div><span class="mova-mobile-card__label">Età</span><?php echo esc_html($row->age ?: '—'); ?></div>
                                        <div><span class="mova-mobile-card__label">Telefono</span><?php echo esc_html($row->phone ?: '—'); ?></div>
                                        <div><span class="mova-mobile-card__label">Stato iscrizione</span><?php echo $this->get_registration_status_badge($row); ?></div>
                                        <div><span class="mova-mobile-card__label">Check-in</span><?php echo esc_html($this->get_checkin_display($row)); ?></div>
                                        <div><span class="mova-mobile-card__label">QR</span><?php if ($row->qr_file): ?><a href="<?php echo esc_url($row->qr_file); ?>" target="_blank">Apri QR</a><?php else: ?><span class="mova-muted">—</span><?php endif; ?></div>
                                    </div>
                                    <div class="mova-mobile-card__actions">
                                        <?php $this->render_registration_action_menu($row, $base_url, 'mobile'); ?>
                                    </div>
                                </div>
                            </details>
                        <?php endforeach; ?>
                    </div>
                <?php endif; ?>
            </div>
        </div>
        <?php
    }

    public function handle_save_registration() {
        if (!$this->can_access_dashboard()) {
            wp_die('Accesso negato');
        }

        check_admin_referer('mova_save_registration');
        global $wpdb;
        $id = isset($_POST['id']) ? (int) $_POST['id'] : 0;
        $data = [
            'first_name' => sanitize_text_field(wp_unslash($_POST['first_name'] ?? '')),
            'last_name' => sanitize_text_field(wp_unslash($_POST['last_name'] ?? '')),
            'email' => sanitize_email(wp_unslash($_POST['email'] ?? '')),
            'phone' => sanitize_text_field(wp_unslash($_POST['phone'] ?? '')),
            'age' => sanitize_text_field(wp_unslash($_POST['age'] ?? '')),
            'notes' => sanitize_textarea_field(wp_unslash($_POST['notes'] ?? '')),
            'updated_at' => current_time('mysql'),
        ];

        $redirect_url = $this->get_safe_redirect_target(admin_url('admin.php?page=mova-event'));

        if ($id && $this->email_exists_for_event($data['email'], $id)) {
            wp_safe_redirect(add_query_arg('duplicate', 1, $redirect_url));
            exit;
        }

        if ($id && !empty($data['phone']) && $this->phone_exists_for_event($data['phone'], $id)) {
            wp_safe_redirect(add_query_arg('duplicate_phone', 1, $redirect_url));
            exit;
        }

        if ($id) {
            $wpdb->update($this->table_name(), $data, ['id' => $id], ['%s','%s','%s','%s','%s','%s','%s'], ['%d']);
            wp_safe_redirect($redirect_url);
            exit;
        }

        wp_safe_redirect(add_query_arg('duplicate', 1, $redirect_url));
        exit;
    }

    public function handle_delete_registration() {
        if (!$this->can_access_dashboard()) {
            wp_die('Accesso negato');
        }

        $id = isset($_GET['id']) ? (int) $_GET['id'] : 0;
        check_admin_referer('mova_delete_registration_' . $id);
        global $wpdb;
        $registration = $this->get_registration($id);
        if ($registration && !empty($registration->qr_file)) {
            $uploads = wp_upload_dir();
            $baseurl = trailingslashit($uploads['baseurl']);
            $basedir = trailingslashit($uploads['basedir']);
            $path = str_replace($baseurl, $basedir, $registration->qr_file);
            if (file_exists($path)) {
                @unlink($path);
            }
        }

        $wpdb->delete($this->table_name(), ['id' => $id], ['%d']);
        wp_safe_redirect($this->get_safe_redirect_target(admin_url('admin.php?page=mova-event')));
        exit;
    }

    public function handle_checkin_registration() {
        if (!$this->can_access_dashboard()) {
            wp_die('Accesso negato');
        }

        $id = isset($_GET['id']) ? (int) $_GET['id'] : 0;
        check_admin_referer('mova_checkin_' . $id);
        $registration = $this->get_registration($id);
        if ($registration && ($registration->registration_status ?? self::STATUS_REGISTERED) === self::STATUS_CONFIRMED) {
            $this->mark_registration_checked_in($id, 'admin');
        }
        wp_safe_redirect($this->get_safe_redirect_target(admin_url('admin.php?page=mova-event')));
        exit;
    }

    public function handle_undo_checkin_registration() {
        if (!$this->can_access_dashboard()) {
            wp_die('Accesso negato');
        }

        $id = isset($_GET['id']) ? (int) $_GET['id'] : 0;
        check_admin_referer('mova_undo_checkin_' . $id);
        global $wpdb;
        $wpdb->update($this->table_name(), [
            'checked_in_at' => null,
            'checkin_source' => '',
            'updated_at' => current_time('mysql'),
        ], ['id' => $id], ['%s','%s','%s'], ['%d']);
        wp_safe_redirect($this->get_safe_redirect_target(admin_url('admin.php?page=mova-event')));
        exit;
    }

    public function handle_regenerate_qr_registration() {
        if (!$this->can_access_dashboard()) {
            wp_die('Accesso negato');
        }

        $id = isset($_GET['id']) ? (int) $_GET['id'] : 0;
        check_admin_referer('mova_regenerate_qr_' . $id);
        $registration = $this->get_registration($id);
        if ($registration && ($registration->registration_status ?? self::STATUS_REGISTERED) === self::STATUS_CONFIRMED) {
            $this->generate_qr_for_registration($registration);
        }
        wp_safe_redirect($this->get_safe_redirect_target(admin_url('admin.php?page=mova-event')));
        exit;
    }

    public function handle_confirm_registration() {
        if (!$this->can_access_dashboard()) {
            wp_die('Accesso negato');
        }

        $id = isset($_GET['id']) ? (int) $_GET['id'] : 0;
        check_admin_referer('mova_confirm_' . $id);
        $registration = $this->get_registration($id);

        if ($registration) {
            global $wpdb;
            $now = current_time('mysql');
            $wpdb->update($this->table_name(), [
                'registration_status' => self::STATUS_CONFIRMED,
                'confirmed_at' => $now,
                'updated_at' => $now,
            ], ['id' => $id], ['%s','%s','%s'], ['%d']);

            $updated_registration = $this->get_registration($id);
            if ($updated_registration) {
                $this->send_user_confirmation($updated_registration);
            }
        }

        wp_safe_redirect($this->get_safe_redirect_target(admin_url('admin.php?page=mova-event')));
        exit;
    }

    public function handle_export() {
        if (!$this->can_access_dashboard()) {
            wp_die('Accesso negato');
        }

        check_admin_referer('mova_export_registrations');
        global $wpdb;
        $search = isset($_GET['s']) ? sanitize_text_field(wp_unslash($_GET['s'])) : '';
        $checkin = isset($_GET['checkin']) ? sanitize_text_field(wp_unslash($_GET['checkin'])) : '';

        $where = ['page_id = %d'];
        $args = [$this->get_event_page_id()];
        if ($search !== '') {
            $where[] = '(first_name LIKE %s OR last_name LIKE %s OR email LIKE %s OR phone LIKE %s)';
            $like = '%' . $wpdb->esc_like($search) . '%';
            array_push($args, $like, $like, $like, $like);
        }
        if ($checkin === 'done') {
            $where[] = 'checked_in_at IS NOT NULL';
        }
        if ($checkin === 'pending') {
            $where[] = 'checked_in_at IS NULL';
        }

        $sql = "SELECT id, first_name, last_name, email, phone, age, registration_status, privacy_policy, marketing_policy, created_at, checked_in_at, checkin_source, notes, token FROM {$this->table_name()} WHERE " . implode(' AND ', $where) . ' ORDER BY created_at DESC';
        $rows = $wpdb->get_results($wpdb->prepare($sql, ...$args), ARRAY_A);

        $data = [];
        $data[] = ['ID','Nome','Cognome','Email','Telefono','Età','Stato iscrizione','Privacy policy','Marketing policy','Registrato il','Check-in','Origine check-in','Note','Token','Scheda'];
        foreach ($rows as $row) {
            $data[] = [
                $row['id'],
                $row['first_name'],
                $row['last_name'],
                $row['email'],
                $row['phone'],
                $row['age'],
                $this->get_registration_status_label($row['registration_status'] ?? self::STATUS_REGISTERED),
                $this->format_consent_for_export($row['privacy_policy'], $row['created_at']),
                $this->format_consent_for_export($row['marketing_policy'], $row['created_at']),
                $row['created_at'],
                $row['checked_in_at'],
                $this->get_checkin_source_label($row['checkin_source']),
                $row['notes'],
                $row['token'],
                home_url('/mova-ticket/' . $row['token'] . '/'),
            ];
        }
        $filename = 'mova-iscrizioni-' . date('Ymd-His') . '.xlsx';
        $this->output_xlsx($filename, $data);
        exit;
    }

    private function xml_escape($value) {
        return htmlspecialchars((string) $value, ENT_QUOTES | ENT_XML1, 'UTF-8');
    }

    private function column_letter($index) {
        $index++;
        $letter = '';
        while ($index > 0) {
            $mod = ($index - 1) % 26;
            $letter = chr(65 + $mod) . $letter;
            $index = (int) (($index - $mod) / 26);
        }
        return $letter;
    }

    private function output_xlsx($filename, $rows) {
        if (!class_exists('ZipArchive')) {
            wp_die('ZipArchive non disponibile sul server.');
        }

        $tmp = wp_tempnam($filename);
        $zip = new ZipArchive();
        $zip->open($tmp, ZipArchive::CREATE | ZipArchive::OVERWRITE);

        $sheetRows = '';
        foreach ($rows as $r => $row) {
            $rowIndex = $r + 1;
            $sheetRows .= '<row r="' . $rowIndex . '">';
            foreach (array_values($row) as $c => $cell) {
                $ref = $this->column_letter($c) . $rowIndex;
                $sheetRows .= '<c r="' . $ref . '" t="inlineStr"><is><t>' . $this->xml_escape($cell) . '</t></is></c>';
            }
            $sheetRows .= '</row>';
        }

        $zip->addFromString('[Content_Types].xml', '<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<Types xmlns="http://schemas.openxmlformats.org/package/2006/content-types">
<Default Extension="rels" ContentType="application/vnd.openxmlformats-package.relationships+xml"/>
<Default Extension="xml" ContentType="application/xml"/>
<Override PartName="/xl/workbook.xml" ContentType="application/vnd.openxmlformats-officedocument.spreadsheetml.sheet.main+xml"/>
<Override PartName="/xl/worksheets/sheet1.xml" ContentType="application/vnd.openxmlformats-officedocument.spreadsheetml.worksheet+xml"/>
<Override PartName="/xl/styles.xml" ContentType="application/vnd.openxmlformats-officedocument.spreadsheetml.styles+xml"/>
<Override PartName="/docProps/core.xml" ContentType="application/vnd.openxmlformats-package.core-properties+xml"/>
<Override PartName="/docProps/app.xml" ContentType="application/vnd.openxmlformats-officedocument.extended-properties+xml"/>
</Types>');
        $zip->addFromString('_rels/.rels', '<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<Relationships xmlns="http://schemas.openxmlformats.org/package/2006/relationships">
<Relationship Id="rId1" Type="http://schemas.openxmlformats.org/officeDocument/2006/relationships/officeDocument" Target="xl/workbook.xml"/>
<Relationship Id="rId2" Type="http://schemas.openxmlformats.org/package/2006/relationships/metadata/core-properties" Target="docProps/core.xml"/>
<Relationship Id="rId3" Type="http://schemas.openxmlformats.org/officeDocument/2006/relationships/extended-properties" Target="docProps/app.xml"/>
</Relationships>');
        $zip->addFromString('docProps/app.xml', '<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<Properties xmlns="http://schemas.openxmlformats.org/officeDocument/2006/extended-properties" xmlns:vt="http://schemas.openxmlformats.org/officeDocument/2006/docPropsVTypes"><Application>WordPress</Application></Properties>');
        $zip->addFromString('docProps/core.xml', '<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<cp:coreProperties xmlns:cp="http://schemas.openxmlformats.org/package/2006/metadata/core-properties" xmlns:dc="http://purl.org/dc/elements/1.1/" xmlns:dcterms="http://purl.org/dc/terms/" xmlns:dcmitype="http://purl.org/dc/dcmitype/" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"><dc:title>MOVA Registrations</dc:title><dc:creator>MOVA Event Manager</dc:creator><cp:lastModifiedBy>MOVA Event Manager</cp:lastModifiedBy><dcterms:created xsi:type="dcterms:W3CDTF">' . gmdate('c') . '</dcterms:created></cp:coreProperties>');
        $zip->addFromString('xl/_rels/workbook.xml.rels', '<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<Relationships xmlns="http://schemas.openxmlformats.org/package/2006/relationships"><Relationship Id="rId1" Type="http://schemas.openxmlformats.org/officeDocument/2006/relationships/worksheet" Target="worksheets/sheet1.xml"/><Relationship Id="rId2" Type="http://schemas.openxmlformats.org/officeDocument/2006/relationships/styles" Target="styles.xml"/></Relationships>');
        $zip->addFromString('xl/workbook.xml', '<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<workbook xmlns="http://schemas.openxmlformats.org/spreadsheetml/2006/main" xmlns:r="http://schemas.openxmlformats.org/officeDocument/2006/relationships"><sheets><sheet name="Registrazioni" sheetId="1" r:id="rId1"/></sheets></workbook>');
        $zip->addFromString('xl/styles.xml', '<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<styleSheet xmlns="http://schemas.openxmlformats.org/spreadsheetml/2006/main"><fonts count="1"><font><sz val="11"/><name val="Calibri"/></font></fonts><fills count="1"><fill><patternFill patternType="none"/></fill></fills><borders count="1"><border/></borders><cellStyleXfs count="1"><xf/></cellStyleXfs><cellXfs count="1"><xf xfId="0"/></cellXfs><cellStyles count="1"><cellStyle name="Normal" xfId="0" builtinId="0"/></cellStyles></styleSheet>');
        $zip->addFromString('xl/worksheets/sheet1.xml', '<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<worksheet xmlns="http://schemas.openxmlformats.org/spreadsheetml/2006/main"><sheetData>' . $sheetRows . '</sheetData></worksheet>');
        $zip->close();

        nocache_headers();
        header('Content-Type: application/vnd.openxmlformats-officedocument.spreadsheetml.sheet');
        header('Content-Disposition: attachment; filename=' . $filename);
        header('Content-Length: ' . filesize($tmp));
        readfile($tmp);
        @unlink($tmp);
    }
}

new MOVA_Event_Manager();
