<?php
if ( ! defined( 'ABSPATH' ) ) {
    exit;
}

/**
 * @return string
 */
function elementsell_get_legacy_payment_widget_type() {
    return 'platebni_formular';
}

/**
 * @return string
 */
function elementsell_get_new_payment_widget_type() {
    return 'elementsell_payment_form';
}

/**
 * @param string $raw
 * @return array<int, array<string, mixed>>
 */
function elementsell_decode_elementor_data( $raw ) {
    if ( ! is_string( $raw ) || $raw === '' ) {
        return [];
    }

    $data = json_decode( wp_unslash( $raw ), true );

    return is_array( $data ) ? $data : [];
}

/**
 * @param array<int, array<string, mixed>> $elements
 * @return string
 */
function elementsell_json_encode_elementor_data( $elements ) {
    $json = wp_json_encode( $elements );

    return ( is_string( $json ) && $json !== '' ) ? $json : '';
}

/**
 * Uloží Elementor data podle konvence Elementoru: update_metadata + wp_slash(JSON).
 * Nepoužívat update_post_meta — ten volá wp_slash znovu a rozbije unicode escapes.
 *
 * @param int                              $post_id
 * @param array<int, array<string, mixed>> $elements
 * @return bool
 */
function elementsell_save_elementor_data( $post_id, $elements ) {
    $post_id = (int) $post_id;

    if ( $post_id < 1 ) {
        return false;
    }

    $json = elementsell_json_encode_elementor_data( $elements );

    if ( $json === '' ) {
        return false;
    }

    $saved = update_metadata( 'post', $post_id, '_elementor_data', wp_slash( $json ) );

    if ( false === $saved ) {
        return false;
    }

    delete_post_meta( $post_id, '_elementor_css' );
    delete_post_meta( $post_id, '_elementor_element_cache' );

    return true;
}

/**
 * @param array<int, array<string, mixed>> $elements
 * @return string JSON se wp_slash pro update_metadata (Elementor konvence).
 */
function elementsell_encode_elementor_data( $elements ) {
    $json = elementsell_json_encode_elementor_data( $elements );

    return $json !== '' ? wp_slash( $json ) : '';
}

/**
 * Rozbitý unicode escape v JSON: u0161 místo \u0161 (bez zpětného lomítka).
 *
 * @return string
 */
function elementsell_get_broken_json_unicode_escape_regex() {
    return '/(?<!\\\\)u[0-9a-fA-F]{4}/';
}

/**
 * @param string $value
 * @return bool
 */
function elementsell_string_has_broken_json_unicode_escapes( $value ) {
    return is_string( $value ) && preg_match( elementsell_get_broken_json_unicode_escape_regex(), $value ) === 1;
}

/**
 * @param string $value
 * @return string
 */
function elementsell_fix_broken_json_unicode_escapes_in_string( $value ) {
    if ( ! elementsell_string_has_broken_json_unicode_escapes( $value ) ) {
        return $value;
    }

    $fixed = preg_replace_callback(
        elementsell_get_broken_json_unicode_escape_regex(),
        static function ( $matches ) {
            if ( ! preg_match( '/^u([0-9a-fA-F]{4})$/i', $matches[0], $code_matches ) ) {
                return $matches[0];
            }

            $char = json_decode( '"\\u' . $code_matches[1] . '"' );
            return ( is_string( $char ) && $char !== '' ) ? $char : $matches[0];
        },
        $value
    );

    return is_string( $fixed ) ? $fixed : $value;
}

/**
 * @param mixed $data
 * @return mixed
 */
function elementsell_fix_broken_json_unicode_escapes_recursive( $data ) {
    if ( is_string( $data ) ) {
        return elementsell_fix_broken_json_unicode_escapes_in_string( $data );
    }

    if ( ! is_array( $data ) ) {
        return $data;
    }

    foreach ( $data as $key => $value ) {
        $data[ $key ] = elementsell_fix_broken_json_unicode_escapes_recursive( $value );
    }

    return $data;
}

/**
 * @param string $raw
 * @return bool
 */
function elementsell_elementor_raw_data_has_broken_unicode_escapes( $raw ) {
    return elementsell_string_has_broken_json_unicode_escapes( $raw );
}

/**
 * SQL LIKE vzory pro předběžný výběr _elementor_data s možnými rozbitými uXXXX sekvencemi.
 *
 * @return array<int, string>
 */
function elementsell_get_broken_elementor_unicode_sql_like_patterns() {
    $patterns = [];

    for ( $prefix = 0; $prefix <= 0x1f; ++$prefix ) {
        $hex = sprintf( '%02x', $prefix );
        $patterns[] = '%u' . $hex . '%';
        $patterns[] = '%u' . strtoupper( $hex ) . '%';
    }

    return $patterns;
}

/**
 * @return array<int, string>
 */
function elementsell_get_migrated_payment_widget_repair_like_patterns() {
    global $wpdb;

    $widget_type = elementsell_get_new_payment_widget_type();
    $escaped     = $wpdb->esc_like( $widget_type );

    return array_merge(
        [
            '%"widgetType":"' . $escaped . '"%',
            '%"widgetType": "' . $escaped . '"%',
        ],
        elementsell_get_broken_elementor_unicode_sql_like_patterns()
    );
}

/**
 * @param string              $select
 * @param string              $extra
 * @param array<int, string>  $like_patterns
 * @return array{sql: string, params: array<int, string>}
 */
function elementsell_build_broken_elementor_unicode_repair_sql( $select, $like_patterns, $extra = '' ) {
    global $wpdb;

    if ( empty( $like_patterns ) ) {
        return [
            'sql'    => '',
            'params' => [],
        ];
    }

    $excluded                 = elementsell_get_legacy_payment_widget_excluded_query_filters();
    $post_type_placeholders   = implode( ', ', array_fill( 0, count( $excluded['post_types'] ), '%s' ) );
    $post_status_placeholders = implode( ', ', array_fill( 0, count( $excluded['post_statuses'] ), '%s' ) );
    $like_clauses             = array_fill( 0, count( $like_patterns ), 'pm.meta_value LIKE %s' );

    $sql = "SELECT {$select}
            FROM {$wpdb->postmeta} pm
            INNER JOIN {$wpdb->posts} p ON p.ID = pm.post_id
            WHERE pm.meta_key = '_elementor_data'
            AND (" . implode( ' OR ', $like_clauses ) . ")
            AND p.post_type NOT IN ({$post_type_placeholders})
            AND p.post_status NOT IN ({$post_status_placeholders})
            {$extra}";

    return [
        'sql'    => $sql,
        'params' => array_merge( $like_patterns, $excluded['post_types'], $excluded['post_statuses'] ),
    ];
}

/**
 * @param string $select
 * @param string $extra
 * @return array{sql: string, params: array<int, string>}
 */
function elementsell_build_migrated_payment_widget_repair_sql( $select, $extra = '' ) {
    return elementsell_build_broken_elementor_unicode_repair_sql(
        $select,
        elementsell_get_migrated_payment_widget_repair_like_patterns(),
        $extra
    );
}

/**
 * @param array<string, mixed> $args
 * @return array<int, array{post_id: int, post_title: string, post_type: string, post_status: string}>
 */
function elementsell_find_documents_with_broken_elementor_unicode( $args = [] ) {
    global $wpdb;

    $require_migrated_widget = ! empty( $args['require_migrated_widget'] );
    $like_patterns           = $require_migrated_widget
        ? elementsell_get_migrated_payment_widget_repair_like_patterns()
        : elementsell_get_broken_elementor_unicode_sql_like_patterns();

    $query = elementsell_build_broken_elementor_unicode_repair_sql(
        'pm.post_id, pm.meta_value, p.post_title, p.post_type, p.post_status',
        $like_patterns,
        'LIMIT ' . (int) elementsell_get_legacy_payment_widget_query_limit()
    );

    if ( $query['sql'] === '' ) {
        return [];
    }

    $rows = $wpdb->get_results(
        $wpdb->prepare( $query['sql'], $query['params'] ),
        ARRAY_A
    );

    if ( empty( $rows ) ) {
        return [];
    }

    $documents     = [];
    $seen_post_ids = [];
    $max_bytes     = elementsell_get_legacy_payment_widget_meta_max_bytes();
    $widget_type   = elementsell_get_new_payment_widget_type();

    foreach ( $rows as $row ) {
        $post_id = (int) ( $row['post_id'] ?? 0 );

        if ( $post_id < 1 || isset( $seen_post_ids[ $post_id ] ) ) {
            continue;
        }

        $raw = (string) ( $row['meta_value'] ?? '' );

        if ( $raw === '' || strlen( $raw ) > $max_bytes ) {
            continue;
        }

        if ( $require_migrated_widget && strpos( $raw, $widget_type ) === false ) {
            continue;
        }

        if ( ! elementsell_elementor_raw_data_has_broken_unicode_escapes( $raw ) ) {
            continue;
        }

        $seen_post_ids[ $post_id ] = true;

        $documents[] = [
            'post_id'     => $post_id,
            'post_title'  => (string) ( $row['post_title'] ?? '' ),
            'post_type'   => (string) ( $row['post_type'] ?? '' ),
            'post_status' => (string) ( $row['post_status'] ?? '' ),
        ];
    }

    usort(
        $documents,
        static function ( $a, $b ) {
            return strcasecmp( (string) $a['post_title'], (string) $b['post_title'] );
        }
    );

    return $documents;
}

/**
 * @return array<int, array{post_id: int, post_title: string, post_type: string, post_status: string}>
 */
function elementsell_find_documents_with_broken_migrated_elementor_unicode() {
    return elementsell_find_documents_with_broken_elementor_unicode(
        [
            'require_migrated_widget' => true,
        ]
    );
}

/**
 * @param int                  $post_id
 * @param array<string, mixed> $args
 * @return array{success: bool, repaired: bool, message: string}
 */
function elementsell_repair_elementor_unicode_escapes_in_document( $post_id, $args = [] ) {
    $post_id                 = (int) $post_id;
    $require_migrated_widget = ! empty( $args['require_migrated_widget'] );

    if ( $post_id < 1 ) {
        return [
            'success'  => false,
            'repaired' => false,
            'message'  => 'Neplatné ID dokumentu.',
        ];
    }

    $raw = get_post_meta( $post_id, '_elementor_data', true );

    if ( ! is_string( $raw ) || $raw === '' ) {
        return [
            'success'  => false,
            'repaired' => false,
            'message'  => 'Dokument nemá Elementor data.',
        ];
    }

    if ( $require_migrated_widget && strpos( $raw, elementsell_get_new_payment_widget_type() ) === false ) {
        return [
            'success'  => true,
            'repaired' => false,
            'message'  => 'Dokument nemá migrovaný widget ElementSell.',
        ];
    }

    if ( ! elementsell_elementor_raw_data_has_broken_unicode_escapes( $raw ) ) {
        return [
            'success'  => true,
            'repaired' => false,
            'message'  => 'Unicode v datech není potřeba opravovat.',
        ];
    }

    $elements = elementsell_decode_elementor_data( $raw );

    if ( empty( $elements ) ) {
        return [
            'success'  => false,
            'repaired' => false,
            'message'  => 'Nepodařilo se načíst Elementor data.',
        ];
    }

    $fixed = elementsell_fix_broken_json_unicode_escapes_recursive( $elements );

    if ( ! elementsell_save_elementor_data( $post_id, $fixed ) ) {
        return [
            'success'  => false,
            'repaired' => false,
            'message'  => 'Nepodařilo se uložit opravená Elementor data.',
        ];
    }

    return [
        'success'  => true,
        'repaired' => true,
        'message'  => 'Opraveny rozbité unicode znaky v Elementor datech.',
    ];
}

/**
 * @param int $post_id
 * @return array{success: bool, repaired: bool, message: string}
 */
function elementsell_repair_migrated_elementor_unicode_escapes_in_document( $post_id ) {
    return elementsell_repair_elementor_unicode_escapes_in_document(
        $post_id,
        [
            'require_migrated_widget' => true,
        ]
    );
}

/**
 * @param array<string, mixed> $args
 * @return array{documents: int, repaired: int, errors: array<int, string>}
 */
function elementsell_repair_elementor_unicode_escapes( $args = [] ) {
    $require_migrated_widget = ! empty( $args['require_migrated_widget'] );
    $documents               = elementsell_find_documents_with_broken_elementor_unicode(
        [
            'require_migrated_widget' => $require_migrated_widget,
        ]
    );
    $repaired = 0;
    $errors   = [];

    foreach ( $documents as $document ) {
        $post_id = (int) ( $document['post_id'] ?? 0 );
        $result  = elementsell_repair_elementor_unicode_escapes_in_document(
            $post_id,
            [
                'require_migrated_widget' => $require_migrated_widget,
            ]
        );

        if ( ! $result['success'] ) {
            $errors[] = sprintf(
                '#%d %s: %s',
                $post_id,
                (string) ( $document['post_title'] ?? '' ),
                (string) ( $result['message'] ?? '' )
            );
            continue;
        }

        if ( ! empty( $result['repaired'] ) ) {
            ++$repaired;
        }
    }

    if ( $repaired > 0 && class_exists( '\Elementor\Plugin' ) ) {
        \Elementor\Plugin::$instance->files_manager->clear_cache();
    }

    return [
        'documents' => count( $documents ),
        'repaired'  => $repaired,
        'errors'    => $errors,
    ];
}

/**
 * @return array{documents: int, repaired: int, errors: array<int, string>}
 */
function elementsell_repair_migrated_elementor_unicode_escapes() {
    return elementsell_repair_elementor_unicode_escapes(
        [
            'require_migrated_widget' => true,
        ]
    );
}

/**
 * @return array{documents: int, repaired: int, errors: array<int, string>}
 */
function elementsell_repair_all_elementor_unicode_escapes() {
    return elementsell_repair_elementor_unicode_escapes(
        [
            'require_migrated_widget' => false,
        ]
    );
}

/**
 * @param array<string, mixed> $settings
 * @return array<string, mixed>
 */
function elementsell_map_legacy_payment_widget_settings( $settings ) {
    if ( ! is_array( $settings ) ) {
        return [];
    }

    if (
        ! function_exists( 'elementsell_get_thepay_environment_from_settings' )
        && defined( 'PF_PLUGIN_DIR' )
    ) {
        $credentials_path = PF_PLUGIN_DIR . 'includes/payments/payment-widget-credentials.php';

        if ( is_readable( $credentials_path ) ) {
            require_once $credentials_path;
        }
    }

    $ui_only_keys = [
        'payment_button',
        'failed_payment_button',
        'reminder_notice',
        'api_intro_text',
        'server_ip',
    ];

    $mapped = $settings;

    foreach ( $ui_only_keys as $key ) {
        unset( $mapped[ $key ] );
    }

    if ( function_exists( 'elementsell_get_thepay_environment_from_settings' ) ) {
        $environment = elementsell_get_thepay_environment_from_settings( $settings );
    } else {
        $environment = ( isset( $settings['environment'] ) && $settings['environment'] === 'production' ) ? 'production' : 'demo';
    }

    $prefix = $environment . '_';

    $legacy_project_id   = trim( (string) ( $settings['project_id'] ?? '' ) );
    $legacy_merchant_id  = trim( (string) ( $settings['merchant_id'] ?? '' ) );
    $legacy_api_password = trim( (string) ( $settings['api_password'] ?? '' ) );

    if ( $legacy_project_id !== '' || $legacy_merchant_id !== '' || $legacy_api_password !== '' ) {
        if ( empty( $mapped[ $prefix . 'project_id' ] ) && $legacy_project_id !== '' ) {
            $mapped[ $prefix . 'project_id' ] = $legacy_project_id;
        }

        if ( empty( $mapped[ $prefix . 'merchant_id' ] ) && $legacy_merchant_id !== '' ) {
            $mapped[ $prefix . 'merchant_id' ] = $legacy_merchant_id;
        }

        if ( empty( $mapped[ $prefix . 'api_password' ] ) && $legacy_api_password !== '' ) {
            $mapped[ $prefix . 'api_password' ] = $legacy_api_password;
        }
    }

    unset( $mapped['project_id'], $mapped['merchant_id'], $mapped['api_password'] );

    if ( function_exists( 'elementsell_is_legacy_widget_sender_name' )
        && function_exists( 'elementsell_get_widget_email_from_name_default' )
        && isset( $mapped['email_from_name'] )
        && elementsell_is_legacy_widget_sender_name( $mapped['email_from_name'] )
    ) {
        $default_name = elementsell_get_widget_email_from_name_default();

        if ( $default_name !== '' ) {
            $mapped['email_from_name'] = $default_name;
        }
    }

    return $mapped;
}

/**
 * @param array<int, array<string, mixed>> $elements
 * @return int
 */
function elementsell_count_legacy_payment_widgets_in_elements( $elements ) {
    if ( ! is_array( $elements ) || empty( $elements ) ) {
        return 0;
    }

    $count      = 0;
    $legacy_type = elementsell_get_legacy_payment_widget_type();

    foreach ( $elements as $element ) {
        if ( ! is_array( $element ) ) {
            continue;
        }

        if (
            ! empty( $element['widgetType'] )
            && $element['widgetType'] === $legacy_type
        ) {
            ++$count;
        }

        if ( ! empty( $element['elements'] ) && is_array( $element['elements'] ) ) {
            $count += elementsell_count_legacy_payment_widgets_in_elements( $element['elements'] );
        }
    }

    return $count;
}

/**
 * @param array<int, array<string, mixed>> $elements
 * @return array{elements: array<int, array<string, mixed>>, count: int}
 */
function elementsell_migrate_legacy_payment_widgets_in_elements( $elements ) {
    if ( ! is_array( $elements ) ) {
        return [
            'elements' => [],
            'count'    => 0,
        ];
    }

    $count       = 0;
    $legacy_type = elementsell_get_legacy_payment_widget_type();
    $new_type    = elementsell_get_new_payment_widget_type();

    foreach ( $elements as $index => $element ) {
        if ( ! is_array( $element ) ) {
            continue;
        }

        if (
            ! empty( $element['widgetType'] )
            && $element['widgetType'] === $legacy_type
        ) {
            $elements[ $index ]['widgetType'] = $new_type;

            if ( isset( $element['settings'] ) && is_array( $element['settings'] ) ) {
                $elements[ $index ]['settings'] = elementsell_map_legacy_payment_widget_settings( $element['settings'] );
            }

            ++$count;
        }

        if ( ! empty( $element['elements'] ) && is_array( $element['elements'] ) ) {
            $child = elementsell_migrate_legacy_payment_widgets_in_elements( $element['elements'] );
            $elements[ $index ]['elements'] = $child['elements'];
            $count += $child['count'];
        }
    }

    return [
        'elements' => $elements,
        'count'    => $count,
    ];
}

/**
 * Post typy a stavy, které se při hledání legacy widgetů ignorují.
 *
 * Revize (post_type revision, status inherit) často obsahují kopii _elementor_data
 * z každého uložení stránky a bez filtru nadhodnocují počet widgetů desítky násobně.
 *
 * @return array{post_types: array<int, string>, post_statuses: array<int, string>}
 */
function elementsell_get_legacy_payment_widget_excluded_query_filters() {
    return [
        'post_types'   => [
            'revision',
            'attachment',
            'nav_menu_item',
            'customize_changeset',
            'oembed_cache',
            'user_request',
            'wp_block',
        ],
        'post_statuses' => [
            'trash',
            'auto-draft',
            'inherit',
        ],
    ];
}

/**
 * @return string
 */
function elementsell_get_legacy_payment_widget_stats_transient_key() {
    return 'elementsell_legacy_widget_stats_v1';
}

/**
 * @return string
 */
function elementsell_get_legacy_payment_widget_has_transient_key() {
    return 'elementsell_legacy_widget_has_v1';
}

/**
 * @return int
 */
function elementsell_get_legacy_payment_widget_stats_cache_ttl() {
    return 15 * MINUTE_IN_SECONDS;
}

/**
 * @return int
 */
function elementsell_get_legacy_payment_widget_meta_max_bytes() {
    return 2 * 1024 * 1024;
}

/**
 * @return int
 */
function elementsell_get_legacy_payment_widget_query_limit() {
    return 500;
}

/**
 * @return void
 */
function elementsell_clear_legacy_payment_widget_stats_cache() {
    delete_transient( elementsell_get_legacy_payment_widget_stats_transient_key() );
    delete_transient( elementsell_get_legacy_payment_widget_has_transient_key() );
}

/**
 * @return array<int, string>
 */
function elementsell_get_legacy_payment_widget_candidate_like_patterns() {
    global $wpdb;

    $legacy_type = elementsell_get_legacy_payment_widget_type();
    $escaped     = $wpdb->esc_like( $legacy_type );

    return [
        '%"widgetType":"' . $escaped . '"%',
        '%"widgetType": "' . $escaped . '"%',
    ];
}

/**
 * @return array{sql: string, params: array<int, string>}
 */
function elementsell_build_legacy_payment_widget_candidate_sql( $select, $extra = '' ) {
    global $wpdb;

    $excluded                 = elementsell_get_legacy_payment_widget_excluded_query_filters();
    $like_patterns            = elementsell_get_legacy_payment_widget_candidate_like_patterns();
    $post_type_placeholders   = implode( ', ', array_fill( 0, count( $excluded['post_types'] ), '%s' ) );
    $post_status_placeholders = implode( ', ', array_fill( 0, count( $excluded['post_statuses'] ), '%s' ) );
    $like_clauses             = array_fill( 0, count( $like_patterns ), 'pm.meta_value LIKE %s' );

    $sql = "SELECT {$select}
            FROM {$wpdb->postmeta} pm
            INNER JOIN {$wpdb->posts} p ON p.ID = pm.post_id
            WHERE pm.meta_key = '_elementor_data'
            AND (" . implode( ' OR ', $like_clauses ) . ")
            AND p.post_type NOT IN ({$post_type_placeholders})
            AND p.post_status NOT IN ({$post_status_placeholders})
            {$extra}";

    return [
        'sql'    => $sql,
        'params' => array_merge( $like_patterns, $excluded['post_types'], $excluded['post_statuses'] ),
    ];
}

/**
 * Rychlá existence kontrola bez parsování JSON (pro admin notice).
 *
 * @return bool
 */
function elementsell_query_legacy_payment_widget_candidates_exists() {
    global $wpdb;

    $query = elementsell_build_legacy_payment_widget_candidate_sql( 'pm.post_id', 'LIMIT 1' );
    $sql   = $wpdb->prepare( $query['sql'], $query['params'] );
    $found = $wpdb->get_var( $sql );

    return (int) $found > 0;
}

/**
 * @param array<int, array{post_id: int, post_title: string, post_type: string, post_status: string, widget_count: int}> $documents
 * @return array{
 *     documents: array<int, array{post_id: int, post_title: string, post_type: string, post_status: string, widget_count: int}>,
 *     document_count: int,
 *     widget_count: int,
 *     by_post_type: array<string, array{documents: int, widgets: int, label: string}>
 * }
 */
function elementsell_build_legacy_payment_widget_stats_from_documents( $documents ) {
    $widget_count = 0;
    $by_post_type = [];

    foreach ( $documents as $document ) {
        $widgets = (int) ( $document['widget_count'] ?? 0 );
        $widget_count += $widgets;

        $post_type = (string) ( $document['post_type'] ?? '' );

        if ( ! isset( $by_post_type[ $post_type ] ) ) {
            $by_post_type[ $post_type ] = [
                'documents' => 0,
                'widgets'   => 0,
                'label'     => elementsell_get_legacy_payment_widget_post_type_label( $post_type ),
            ];
        }

        ++$by_post_type[ $post_type ]['documents'];
        $by_post_type[ $post_type ]['widgets'] += $widgets;
    }

    uasort(
        $by_post_type,
        static function ( $a, $b ) {
            return ( $b['widgets'] ?? 0 ) <=> ( $a['widgets'] ?? 0 );
        }
    );

    return [
        'documents'      => $documents,
        'document_count' => count( $documents ),
        'widget_count'   => $widget_count,
        'by_post_type'   => $by_post_type,
    ];
}

/**
 * @return array{
 *     documents: array<int, array{post_id: int, post_title: string, post_type: string, post_status: string, widget_count: int}>,
 *     document_count: int,
 *     widget_count: int,
 *     by_post_type: array<string, array{documents: int, widgets: int, label: string}>
 * }
 */
function elementsell_get_legacy_payment_widget_stats_fallback() {
    return [
        'documents'      => [],
        'document_count' => 0,
        'widget_count'   => 0,
        'by_post_type'   => [],
    ];
}

/**
 * @return array<int, array{post_id: int, post_title: string, post_type: string, post_status: string, widget_count: int}>
 */
function elementsell_find_documents_with_legacy_payment_widgets() {
    global $wpdb;

    $query = elementsell_build_legacy_payment_widget_candidate_sql(
        'pm.post_id, pm.meta_value, p.post_title, p.post_type, p.post_status',
        'LIMIT ' . (int) elementsell_get_legacy_payment_widget_query_limit()
    );

    $rows = $wpdb->get_results(
        $wpdb->prepare( $query['sql'], $query['params'] ),
        ARRAY_A
    );

    if ( empty( $rows ) ) {
        return [];
    }

    $documents     = [];
    $seen_post_ids = [];
    $max_bytes     = elementsell_get_legacy_payment_widget_meta_max_bytes();

    foreach ( $rows as $row ) {
        $post_id = (int) ( $row['post_id'] ?? 0 );

        if ( $post_id < 1 || isset( $seen_post_ids[ $post_id ] ) ) {
            continue;
        }

        $raw = (string) ( $row['meta_value'] ?? '' );

        if ( $raw === '' || strlen( $raw ) > $max_bytes ) {
            continue;
        }

        try {
            $elements     = elementsell_decode_elementor_data( $raw );
            $widget_count = elementsell_count_legacy_payment_widgets_in_elements( $elements );
            unset( $elements );
        } catch ( Throwable $exception ) {
            if ( defined( 'WP_DEBUG' ) && WP_DEBUG ) {
                error_log(
                    sprintf(
                        'ElementSell: legacy widget scan failed for post #%d: %s',
                        $post_id,
                        $exception->getMessage()
                    )
                );
            }
            continue;
        }

        if ( $widget_count < 1 ) {
            continue;
        }

        $seen_post_ids[ $post_id ] = true;

        $documents[] = [
            'post_id'      => $post_id,
            'post_title'   => (string) ( $row['post_title'] ?? '' ),
            'post_type'    => (string) ( $row['post_type'] ?? '' ),
            'post_status'  => (string) ( $row['post_status'] ?? '' ),
            'widget_count' => $widget_count,
        ];
    }

    usort(
        $documents,
        static function ( $a, $b ) {
            return strcasecmp( (string) $a['post_title'], (string) $b['post_title'] );
        }
    );

    return $documents;
}

/**
 * @param array<string, mixed> $args
 * @return array{
 *     documents: array<int, array{post_id: int, post_title: string, post_type: string, post_status: string, widget_count: int}>,
 *     document_count: int,
 *     widget_count: int,
 *     by_post_type: array<string, array{documents: int, widgets: int, label: string}>
 * }
 */
function elementsell_get_legacy_payment_widget_stats( $args = [] ) {
    $args = wp_parse_args(
        $args,
        [
            'use_cache'     => true,
            'force_refresh' => false,
        ]
    );

    $transient_key = elementsell_get_legacy_payment_widget_stats_transient_key();
    $has_key       = elementsell_get_legacy_payment_widget_has_transient_key();
    $cache_ttl     = elementsell_get_legacy_payment_widget_stats_cache_ttl();

    if ( $args['use_cache'] && ! $args['force_refresh'] ) {
        $cached = get_transient( $transient_key );

        if ( is_array( $cached ) ) {
            return $cached;
        }
    }

    try {
        $stats = elementsell_build_legacy_payment_widget_stats_from_documents(
            elementsell_find_documents_with_legacy_payment_widgets()
        );

        if ( $args['use_cache'] ) {
            set_transient( $transient_key, $stats, $cache_ttl );
            set_transient( $has_key, $stats['widget_count'] > 0 ? '1' : '0', $cache_ttl );
        }

        return $stats;
    } catch ( Throwable $exception ) {
        if ( defined( 'WP_DEBUG' ) && WP_DEBUG ) {
            error_log( 'ElementSell: legacy widget stats failed: ' . $exception->getMessage() );
        }

        return elementsell_get_legacy_payment_widget_stats_fallback();
    }
}

/**
 * @param string $post_type
 * @return string
 */
function elementsell_get_legacy_payment_widget_post_type_label( $post_type ) {
    $known = [
        'page'              => 'stránky',
        'post'              => 'příspěvky',
        'elementor_library' => 'šablony Elementoru',
    ];

    if ( isset( $known[ $post_type ] ) ) {
        return $known[ $post_type ];
    }

    $object = get_post_type_object( $post_type );

    if ( $object && ! empty( $object->labels->name ) ) {
        return (string) $object->labels->name;
    }

    return $post_type;
}

/**
 * @param array<string, array{documents: int, widgets: int, label: string}> $by_post_type
 * @return string
 */
function elementsell_format_legacy_payment_widget_breakdown( $by_post_type ) {
    if ( empty( $by_post_type ) || ! is_array( $by_post_type ) ) {
        return '';
    }

    $parts = [];

    foreach ( $by_post_type as $group ) {
        if ( empty( $group['documents'] ) ) {
            continue;
        }

        $parts[] = sprintf(
            '%1$d %2$s',
            (int) $group['documents'],
            (string) ( $group['label'] ?? '' )
        );
    }

    return implode( ', ', $parts );
}

/**
 * @return int
 */
function elementsell_count_legacy_payment_widgets() {
    $cached = get_transient( elementsell_get_legacy_payment_widget_stats_transient_key() );

    if ( is_array( $cached ) ) {
        return (int) ( $cached['widget_count'] ?? 0 );
    }

    return (int) ( elementsell_get_legacy_payment_widget_stats( [ 'use_cache' => true ] )['widget_count'] ?? 0 );
}

/**
 * Lehká kontrola bez parsování _elementor_data (vhodná pro admin_notices).
 *
 * @return bool
 */
function elementsell_has_legacy_payment_widgets() {
    $has_key = elementsell_get_legacy_payment_widget_has_transient_key();
    $cached  = get_transient( $has_key );

    if ( $cached !== false ) {
        return $cached === '1';
    }

    try {
        $has_legacy = elementsell_query_legacy_payment_widget_candidates_exists();
    } catch ( Throwable $exception ) {
        if ( defined( 'WP_DEBUG' ) && WP_DEBUG ) {
            error_log( 'ElementSell: legacy widget existence check failed: ' . $exception->getMessage() );
        }

        return false;
    }

    set_transient(
        $has_key,
        $has_legacy ? '1' : '0',
        elementsell_get_legacy_payment_widget_stats_cache_ttl()
    );

    return $has_legacy;
}

/**
 * @param int $post_id
 * @return array{success: bool, widgets_migrated: int, message: string}
 */
function elementsell_migrate_legacy_payment_widgets_in_document( $post_id ) {
    $post_id = (int) $post_id;

    if ( $post_id < 1 ) {
        return [
            'success'          => false,
            'widgets_migrated' => 0,
            'message'          => 'Neplatné ID dokumentu.',
        ];
    }

    $raw = get_post_meta( $post_id, '_elementor_data', true );

    if ( ! is_string( $raw ) || $raw === '' ) {
        return [
            'success'          => false,
            'widgets_migrated' => 0,
            'message'          => 'Dokument nemá Elementor data.',
        ];
    }

    $elements = elementsell_decode_elementor_data( $raw );
    $result   = elementsell_migrate_legacy_payment_widgets_in_elements( $elements );

    if ( $result['count'] < 1 ) {
        return [
            'success'          => true,
            'widgets_migrated' => 0,
            'message'          => 'Žádné staré widgety k migraci.',
        ];
    }

    if ( ! elementsell_save_elementor_data( $post_id, $result['elements'] ) ) {
        return [
            'success'          => false,
            'widgets_migrated' => 0,
            'message'          => 'Nepodařilo se uložit Elementor data.',
        ];
    }

    if ( function_exists( 'elementsell_ensure_elementor_sync_dependencies' ) ) {
        elementsell_ensure_elementor_sync_dependencies();
        elementsell_walk_elements_for_widget_sync( $result['elements'] );
    }

    return [
        'success'          => true,
        'widgets_migrated' => (int) $result['count'],
        'message'          => sprintf( 'Migrováno %d widgetů.', (int) $result['count'] ),
    ];
}

/**
 * @param array<string, mixed> $entry
 * @return void
 */
function elementsell_append_widget_migration_log_entry( $entry ) {
    $log = get_option( 'elementsell_widget_migration_log', [] );

    if ( ! is_array( $log ) ) {
        $log = [];
    }

    $log[] = array_merge(
        [
            'time' => current_time( 'mysql' ),
        ],
        $entry
    );

    if ( count( $log ) > 100 ) {
        $log = array_slice( $log, -100 );
    }

    update_option( 'elementsell_widget_migration_log', $log, false );
}

/**
 * @return array<int, array<string, mixed>>
 */
function elementsell_get_widget_migration_log() {
    $log = get_option( 'elementsell_widget_migration_log', [] );

    return is_array( $log ) ? array_reverse( $log ) : [];
}

/**
 * @return array{documents: int, widgets: int, unicode_repaired: int, errors: array<int, string>}
 */
function elementsell_migrate_all_legacy_payment_widgets() {
    $documents      = elementsell_find_documents_with_legacy_payment_widgets();
    $total_widgets  = 0;
    $total_documents = 0;
    $errors         = [];

    foreach ( $documents as $document ) {
        $post_id = (int) ( $document['post_id'] ?? 0 );
        $result  = elementsell_migrate_legacy_payment_widgets_in_document( $post_id );

        if ( ! $result['success'] ) {
            $errors[] = sprintf(
                '#%d %s: %s',
                $post_id,
                (string) ( $document['post_title'] ?? '' ),
                (string) ( $result['message'] ?? '' )
            );
            continue;
        }

        if ( (int) $result['widgets_migrated'] < 1 ) {
            continue;
        }

        ++$total_documents;
        $total_widgets += (int) $result['widgets_migrated'];

        elementsell_append_widget_migration_log_entry(
            [
                'post_id'          => $post_id,
                'post_title'       => (string) ( $document['post_title'] ?? '' ),
                'post_type'        => (string) ( $document['post_type'] ?? '' ),
                'widgets_migrated' => (int) $result['widgets_migrated'],
            ]
        );
    }

    if ( $total_widgets > 0 && class_exists( '\Elementor\Plugin' ) ) {
        \Elementor\Plugin::$instance->files_manager->clear_cache();
    }

    $repair_result = elementsell_repair_migrated_elementor_unicode_escapes();

    if ( ! empty( $repair_result['errors'] ) ) {
        foreach ( $repair_result['errors'] as $repair_error ) {
            $errors[] = $repair_error;
        }
    }

    elementsell_clear_legacy_payment_widget_stats_cache();

    return [
        'documents'        => $total_documents,
        'widgets'          => $total_widgets,
        'unicode_repaired' => (int) ( $repair_result['repaired'] ?? 0 ),
        'errors'           => $errors,
    ];
}

/**
 * @return bool
 */
function elementsell_is_legacy_payment_plugin_active() {
    if ( ! function_exists( 'is_plugin_active' ) ) {
        require_once ABSPATH . 'wp-admin/includes/plugin.php';
    }

    $candidates = [
        'platebni-formular/plugin.php',
        'platebni-formular/platebni-formular.php',
    ];

    foreach ( $candidates as $plugin_file ) {
        if ( is_plugin_active( $plugin_file ) ) {
            return true;
        }
    }

    return false;
}
