<?php
/**
 * Tool: redesign_page
 *
 * v1 heuristic recomposition. We do not generate new content — we
 * restructure what is already there so that:
 *   - Each (heading + following blocks) becomes its own section,
 *     wrapped in a core/group with the marker class
 *     "swcp-redesigned-section" so visitors get real breathing room.
 *   - Heading hierarchy is normalized (page title is H1, sections are
 *     H2, sub-items H3).
 *   - Adjacent duplicate headings are collapsed.
 *   - The first image on the page is promoted to a "lead" position in
 *     its section so it reads as a hero rather than a mid-page drop-in.
 *
 * The plugin enqueues a small CSS file that styles
 * .swcp-redesigned-section and .swcp-redesigned-lead-image so the
 * visual change is immediate without theme dependencies.
 */
if (!defined('ABSPATH')) exit;

function swcp_tool_redesign_page($input, $ctx) {
    $page_arg = isset($input['page']) ? $input['page'] : 'current';
    $resolved = swcp_resolve_or_error($page_arg, $ctx['post_id'] ?? 0);
    if (is_array($resolved) && isset($resolved['ok']) && !$resolved['ok']) return $resolved;
    $post = $resolved;

    $compat = swcp_check_builder_compat($post->ID);
    if ($compat !== null) return $compat;

    $blocks = swcp_get_blocks($post);
    if (empty($blocks)) {
        return ['ok' => false, 'message' => 'The page "' . esc_html(get_the_title($post)) . '" is still empty. Add some text or images first, then I can redesign it.'];
    }

    // Step 1: collapse adjacent duplicate headings.
    list($blocks, $dupes_removed) = swcp_redesign_dedupe_headings($blocks);

    // Step 2: normalize heading levels.
    list($blocks, $headings_changed) = swcp_redesign_normalize_levels($blocks);

    // Step 3: split into sections by H2 boundaries.
    $sections = swcp_redesign_split_sections($blocks);

    // Step 4: wrap each section (excluding the first "intro" block group
    // which is content before the first H2) in a marker group. If we found
    // zero section breaks, still wrap the whole thing for breathing room.
    $wrapped = [];
    if (count($sections) <= 1) {
        // No sectioning available — just wrap everything for spacing.
        $all_blocks = $sections[0];
        $wrapped[]  = swcp_build_marker_group('swcp-redesigned-section', $all_blocks);
    } else {
        foreach ($sections as $i => $section) {
            if ($i === 0) {
                $wrapped = array_merge($wrapped, $section);
                continue;
            }
            $wrapped[] = swcp_build_marker_group('swcp-redesigned-section', $section);
        }
    }

    // Step 5: promote the first image (if any) to "lead" if it appears
    // inside the first wrapped section.
    $promoted = swcp_redesign_promote_first_image($wrapped);

    // Preview-first flow (May 14 2026): instead of writing the new blocks
    // onto the live page, create a separate DRAFT page with `_swcp_is_preview=1`
    // so the user reviews-and-accepts. Same UX as design_page_preview, but
    // the preview body is Gutenberg blocks instead of an Opus-designed HTML.
    // apply_page_design then swaps it in (it already operates on any
    // _swcp_is_preview draft regardless of kind).
    $serialized = function_exists('serialize_blocks')
        ? serialize_blocks($wrapped)
        : swcp_serialize_blocks_fallback($wrapped);

    $preview_title = get_the_title($post) . ' (preview)';
    $preview_slug  = $post->post_name . '-redesign-' . wp_generate_uuid4();

    $preview_id = wp_insert_post([
        'post_type'    => 'page',
        'post_status'  => 'draft',
        'post_title'   => $preview_title,
        'post_name'    => $preview_slug,
        'post_content' => $serialized,
    ], true);
    if (is_wp_error($preview_id) || !$preview_id) {
        return ['ok' => false, 'message' => 'Composed a redesigned layout but could not save the preview.'];
    }

    update_post_meta($preview_id, '_swcp_is_preview', 1);
    update_post_meta($preview_id, '_swcp_preview_of', (int) $post->ID);
    update_post_meta($preview_id, '_swcp_preview_kind', 'blocks');

    $preview_url = add_query_arg('swcp_render_preview', $preview_id, home_url('/'));
    $edit_url    = swcp_edit_url_for($preview_id);

    $sections_count = max(0, count($sections) - 1);
    $msg_parts = [
        'I built a preview of "' . esc_html(get_the_title($post)) . '" with ' . $sections_count . ' clearly-spaced section' . ($sections_count === 1 ? '' : 's') . '.',
    ];
    if ($headings_changed) $msg_parts[] = 'Normalized ' . $headings_changed . ' heading level' . ($headings_changed === 1 ? '' : 's') . '.';
    if ($dupes_removed)    $msg_parts[] = 'Removed ' . $dupes_removed . ' duplicate title' . ($dupes_removed === 1 ? '' : 's') . '.';
    if ($promoted)         $msg_parts[] = 'Lifted the first image to read as a lead visual.';
    $msg_parts[] = 'Your live page is unchanged. Open the preview to see it; tell me to apply it when you like it.';

    return [
        'ok'          => true,
        'message'     => implode(' ', $msg_parts),
        'preview_id'  => (int) $preview_id,
        'original_id' => (int) $post->ID,
        'preview_url' => $preview_url,
        'edit_url'    => $edit_url,
        'view_url'    => $preview_url,
        'sections'    => $sections_count,
    ];
}

/**
 * Fallback for sites where WP core's serialize_blocks() is somehow missing
 * (extremely old WP versions). Builds a minimal serializer just for the
 * group + inner blocks we emit. Real WP installs hit the core path above.
 */
function swcp_serialize_blocks_fallback($blocks) {
    if (!is_array($blocks)) return '';
    $out = '';
    foreach ($blocks as $b) {
        $out .= swcp_serialize_block_fallback($b);
    }
    return $out;
}
function swcp_serialize_block_fallback($block) {
    if (!isset($block['blockName'])) return isset($block['innerHTML']) ? (string) $block['innerHTML'] : '';
    $name  = $block['blockName'];
    $attrs = !empty($block['attrs']) ? wp_json_encode($block['attrs']) : '';
    $inner = '';
    if (!empty($block['innerBlocks'])) {
        foreach ($block['innerBlocks'] as $ib) $inner .= swcp_serialize_block_fallback($ib);
    } else {
        $inner = isset($block['innerHTML']) ? (string) $block['innerHTML'] : '';
    }
    $open  = '<!-- wp:' . $name . ($attrs ? ' ' . $attrs : '') . ' -->';
    $close = '<!-- /wp:' . $name . ' -->';
    return $open . "\n" . $inner . "\n" . $close . "\n";
}

function swcp_redesign_dedupe_headings($blocks) {
    $out  = [];
    $prev = null;
    $removed = 0;
    foreach ($blocks as $b) {
        if (
            $prev
            && isset($b['blockName'], $prev['blockName'])
            && $b['blockName'] === 'core/heading'
            && $prev['blockName'] === 'core/heading'
            && strcasecmp(swcp_block_text($b), swcp_block_text($prev)) === 0
            && swcp_heading_level($b) === swcp_heading_level($prev)
        ) {
            $removed++;
            continue;
        }
        $out[] = $b;
        $prev  = $b;
    }
    return [$out, $removed];
}

function swcp_redesign_normalize_levels($blocks) {
    $first_level = null;
    $changes = 0;
    $blocks  = swcp_walk_blocks($blocks, function (&$b) use (&$first_level, &$changes) {
        if (!isset($b['blockName']) || $b['blockName'] !== 'core/heading') return null;
        $level = swcp_heading_level($b);
        if (!$level) return null;
        if ($first_level === null) $first_level = $level;

        // Whatever level the user used FIRST is treated as the section anchor.
        // Anything at or above that becomes H2; anything below becomes H3.
        // (Page title is always H1, so we never leave H1s in content.)
        if ($level === 1) {
            $new = 2;
        } elseif ($level <= $first_level) {
            $new = 2;
        } else {
            $new = 3;
        }

        if ($new !== $level) {
            swcp_set_heading_level($b, $new);
            $changes++;
        }
        return $b;
    });
    return [$blocks, $changes];
}

/**
 * Split top-level blocks into sections at H2 boundaries.
 * Returns an array of arrays. Index 0 is the "intro" (anything before
 * the first H2). Subsequent entries each begin with an H2.
 */
function swcp_redesign_split_sections($blocks) {
    $sections = [[]];
    $cur = 0;
    foreach ($blocks as $b) {
        if (
            isset($b['blockName']) && $b['blockName'] === 'core/heading'
            && swcp_heading_level($b) === 2
        ) {
            $cur++;
            $sections[$cur] = [];
        }
        $sections[$cur][] = $b;
    }
    return $sections;
}

/**
 * Find the first core/image inside any wrapped section and add the
 * "swcp-redesigned-lead-image" className to it. Returns true if one
 * was promoted.
 */
function swcp_redesign_promote_first_image(&$blocks) {
    $promoted = false;
    foreach ($blocks as &$b) {
        if (!isset($b['innerBlocks'])) continue;
        foreach ($b['innerBlocks'] as &$ib) {
            if (!$promoted && isset($ib['blockName']) && $ib['blockName'] === 'core/image') {
                if (!isset($ib['attrs']) || !is_array($ib['attrs'])) $ib['attrs'] = [];
                $existing = isset($ib['attrs']['className']) ? (string) $ib['attrs']['className'] : '';
                $ib['attrs']['className'] = trim($existing . ' swcp-redesigned-lead-image');
                $promoted = true;
                break;
            }
        }
        unset($ib);
        if ($promoted) break;
    }
    unset($b);
    return $promoted;
}

/**
 * Build a core/group wrapping inner blocks with a marker className.
 * Always alignfull so the design renders edge-to-edge regardless of host
 * theme. Without align=full, Astra / Kadence / GeneratePress / FSE themes
 * constrain the group to their content-width and the design renders in a
 * narrow box surrounded by white space (see May 12 2026 stress test on
 * test3.sailwp.com — "Should be a full bleed page").
 */
function swcp_build_marker_group($class_name, $inner_blocks) {
    $full_class = trim($class_name . ' alignfull');
    $opening = "<div class=\"wp-block-group alignfull " . esc_attr($class_name) . "\">";
    $closing = "</div>";
    return [
        'blockName'    => 'core/group',
        'attrs'        => [
            'align'     => 'full',
            'className' => $full_class,
            'tagName'   => 'div',
        ],
        'innerBlocks'  => $inner_blocks,
        'innerHTML'    => $opening . $closing,
        'innerContent' => [$opening, null, $closing],
    ];
}
