<?php
/**
 * Tool: insert_block
 *
 * Insert a new Gutenberg block before / after / inside a target block
 * on the page. The cloud passes a `block` descriptor:
 *
 *   {
 *     name:        'core/button',
 *     attrs?:      { url: 'https://...', backgroundColor: 'primary' },
 *     inner_text?: 'Boek nu',          // for buttons / headings / paragraphs
 *     innerBlocks?: [ ...nested descriptors ]
 *   }
 *
 * Supported names for v1 (most common asks):
 *   core/heading, core/paragraph, core/button, core/buttons, core/image,
 *   core/spacer, core/separator, core/list, core/quote.
 *
 * For everything else, the descriptor is still serialized — the
 * resulting block may not render perfectly without a manual touch in
 * Gutenberg, but it is editable. The full HTML the cloud passes via the
 * optional `raw_html` field bypasses the descriptor and is parsed
 * directly by parse_blocks() — useful when Claude wants to insert a
 * complex block it has the markup for.
 */
if (!defined('ABSPATH')) exit;

function swcp_tool_insert_block($input, $ctx) {
    $page_arg     = isset($input['page']) ? $input['page'] : 'current';
    $anchor_loc   = isset($input['anchor']) && is_array($input['anchor']) ? $input['anchor'] : null;
    $position     = isset($input['position']) ? (string) $input['position'] : 'after';
    $descriptor   = isset($input['block']) && is_array($input['block']) ? $input['block'] : null;
    $raw_html     = isset($input['raw_html']) ? (string) $input['raw_html'] : '';

    if (!in_array($position, ['before', 'after', 'inside_start', 'inside_end'], true)) {
        return ['ok' => false, 'message' => 'Position must be "before", "after", "inside_start", or "inside_end".'];
    }

    if (!$descriptor && $raw_html === '') {
        return ['ok' => false, 'message' => 'Tell me what to insert (block name + content).'];
    }

    $resolved = swcp_resolve_or_error($page_arg, $ctx['post_id'] ?? 0);
    if (is_array($resolved) && isset($resolved['ok']) && !$resolved['ok']) return $resolved;
    $post = $resolved;

    $before_html = (string) $post->post_content;
    $blocks      = swcp_get_blocks($post);

    // Build the new block from descriptor or raw_html.
    if ($raw_html !== '') {
        $parsed = parse_blocks($raw_html);
        if (empty($parsed)) {
            return ['ok' => false, 'message' => 'I could not parse that block markup. Pass a block descriptor instead.'];
        }
        $new_block = $parsed[0];
    } else {
        $new_block = swcp_build_block_from_descriptor($descriptor);
        if (!$new_block) {
            return ['ok' => false, 'message' => 'Block descriptor missing a name.'];
        }
    }

    if ($anchor_loc) {
        $found = swcp_find_block_by_locator($blocks, $anchor_loc);
        if (!$found) {
            return swcp_locator_ambiguous_or_missing($blocks, $anchor_loc) + ['page_id' => (int) $post->ID];
        }
        $ok = swcp_insert_block_at($blocks, $found['path'], $position, $new_block);
    } else {
        // No anchor: append at end of top-level (or prepend if position=before).
        if ($position === 'before' || $position === 'inside_start') {
            array_unshift($blocks, $new_block);
        } else {
            $blocks[] = $new_block;
        }
        $ok = true;
    }

    if (!$ok) {
        return ['ok' => false, 'message' => 'Could not place the new block at that position.'];
    }

    $saved = swcp_save_blocks($post->ID, $blocks);
    if (!$saved) {
        return ['ok' => false, 'message' => 'Built the new block but failed to save the page.'];
    }

    $what = $descriptor['name'] ?? '(custom markup)';
    return [
        'ok'               => true,
        'page_id'          => (int) $post->ID,
        'message'          => 'Inserted a ' . $what . ' block ' . $position . ($anchor_loc ? ' the target' : ' on "' . esc_html(get_the_title($post)) . '"') . '.',
        'view_url'         => swcp_view_url_for($post->ID),
        'edit_url'         => swcp_edit_url_for($post->ID),
        'inserted_block'   => $what,
        'requires_refresh' => true,
        'undo' => [
            'tool'  => 'restore_page_version',
            'input' => ['post_id' => (int) $post->ID, 'snapshot_html' => $before_html],
            'label' => 'Undo',
        ],
    ];
}

/**
 * Compose a Gutenberg block array (the same shape parse_blocks returns)
 * from a high-level descriptor. Recurses into innerBlocks so Claude can
 * hand us nested structures like a buttons-row containing a button.
 */
function swcp_build_block_from_descriptor($d) {
    if (!is_array($d) || empty($d['name'])) return null;
    $name = (string) $d['name'];
    $attrs = isset($d['attrs']) && is_array($d['attrs']) ? $d['attrs'] : [];
    $text  = array_key_exists('inner_text', $d) ? (string) $d['inner_text'] : null;

    $inner_blocks = [];
    if (!empty($d['innerBlocks']) && is_array($d['innerBlocks'])) {
        foreach ($d['innerBlocks'] as $child) {
            $built = swcp_build_block_from_descriptor($child);
            if ($built) $inner_blocks[] = $built;
        }
    }

    $inner_html = swcp_block_inner_html_for($name, $attrs, $text);

    return [
        'blockName'    => $name,
        'attrs'        => $attrs,
        'innerBlocks'  => $inner_blocks,
        'innerHTML'    => $inner_html,
        'innerContent' => [$inner_html ?: null],
    ];
}

/**
 * Produce the rendered innerHTML for the most common block types. Falls
 * back to an empty string for blocks we don't know how to render — the
 * resulting block is still saveable and editable in Gutenberg.
 */
function swcp_block_inner_html_for($name, $attrs, $text) {
    $text = $text === null ? '' : (string) $text;

    if ($name === 'core/heading') {
        $level = isset($attrs['level']) ? max(1, min(6, (int) $attrs['level'])) : 2;
        $align = isset($attrs['textAlign']) ? ' style="text-align:' . esc_attr($attrs['textAlign']) . '"' : '';
        return "\n<h{$level} class=\"wp-block-heading\"{$align}>" . esc_html($text) . "</h{$level}>\n";
    }
    if ($name === 'core/paragraph') {
        $align = isset($attrs['align']) ? ' style="text-align:' . esc_attr($attrs['align']) . '"' : '';
        return "\n<p{$align}>" . esc_html($text) . "</p>\n";
    }
    if ($name === 'core/button') {
        $url    = isset($attrs['url']) ? esc_url($attrs['url']) : '';
        $target = !empty($attrs['linkTarget']) ? ' target="' . esc_attr($attrs['linkTarget']) . '" rel="noreferrer noopener"' : '';
        $bg     = !empty($attrs['backgroundColor']) ? ' has-' . sanitize_html_class($attrs['backgroundColor']) . '-background-color has-background' : '';
        $tc     = !empty($attrs['textColor']) ? ' has-' . sanitize_html_class($attrs['textColor']) . '-color has-text-color' : '';
        return "\n<div class=\"wp-block-button{$bg}{$tc}\"><a class=\"wp-block-button__link wp-element-button\" href=\"{$url}\"{$target}>" . esc_html($text) . "</a></div>\n";
    }
    if ($name === 'core/buttons') {
        // Outer wrapper; inner buttons live in innerBlocks.
        return "\n<div class=\"wp-block-buttons\"></div>\n";
    }
    if ($name === 'core/image') {
        $url = isset($attrs['url']) ? esc_url($attrs['url']) : '';
        $alt = isset($attrs['alt']) ? esc_attr($attrs['alt']) : '';
        if (!$url) return '';
        return "\n<figure class=\"wp-block-image\"><img src=\"{$url}\" alt=\"{$alt}\" /></figure>\n";
    }
    if ($name === 'core/spacer') {
        $h = isset($attrs['height']) ? esc_attr($attrs['height']) : '100px';
        return "\n<div style=\"height:{$h}\" aria-hidden=\"true\" class=\"wp-block-spacer\"></div>\n";
    }
    if ($name === 'core/separator') {
        return "\n<hr class=\"wp-block-separator has-alpha-channel-opacity\"/>\n";
    }
    if ($name === 'core/list') {
        $ordered = !empty($attrs['ordered']);
        $tag = $ordered ? 'ol' : 'ul';
        return "\n<{$tag} class=\"wp-block-list\"></{$tag}>\n";
    }
    if ($name === 'core/quote') {
        return "\n<blockquote class=\"wp-block-quote\">" . esc_html($text) . "</blockquote>\n";
    }
    return '';
}
