<?php
/**
 * Block Registry Admin Controller
 * Handles CRUD operations for block management
 */
namespace Mtc\Modules\BlockRegistry\Http\Controllers;

use Illuminate\Http\Request;
use Illuminate\Support\Facades\DB;
use Illuminate\Support\Facades\Session;
use Mtc\Modules\BlockRegistry\Models\BlockRegistry;
use Mtc\Modules\BlockRegistry\Models\BlockRegistryCategory;
use Mtc\Modules\BlockRegistry\Models\BlockRegistryField;
use Mtc\Modules\BlockRegistry\Models\EntityBlock;
use Mtc\Modules\BlockRegistry\Services\BlockRegistryService;
use Mtc\Modules\BlockRegistry\Services\EntityBlockService;
use Mtc\Modules\BlockRegistry\Services\TemplateBlockService;
use Mtc\Modules\BlockRegistry\Services\ImageProcessingService;
use Mtc\Modules\BlockRegistry\Services\BlockTemplateService;
use Mtc\Modules\BlockRegistry\Services\BlockSyncService;
use Mtc\Modules\BlockRegistry\Models\BlockTemplate;
use App\Services\ComponentScannerService;
use Illuminate\Support\Facades\Cache;
use Illuminate\Support\Str;

class BlockRegistryController extends \App\Http\Controllers\Controller
{
    protected BlockRegistryService $registryService;
    protected TemplateBlockService $templateService;
    protected BlockTemplateService $blockTemplateService;
    protected ComponentScannerService $componentScanner;
    protected EntityBlockService $entityBlockService;

    public function __construct(
        BlockRegistryService $registryService,
        TemplateBlockService $templateService,
        BlockTemplateService $blockTemplateService,
        ComponentScannerService $componentScanner,
        EntityBlockService $entityBlockService
    ) {
        $this->registryService = $registryService;
        $this->templateService = $templateService;
        $this->blockTemplateService = $blockTemplateService;
        $this->componentScanner = $componentScanner;
        $this->entityBlockService = $entityBlockService;
    }

    /**
     * List all blocks
     */
    public function index()
    {
        $data = $this->registryService->getAllBlocks();
        $messages = $this->getFormMessages();

        return template('admin/block_registry/index.twig', [
            'title' => 'Block Registry',
            'blocks' => $data['blocks'],
            'categories' => $data['categories'],
            'messages' => $messages,
            'is_production' => $this->isDefinitionLocked(),
        ]);
    }

    /**
     * Create new block form
     */
    public function create()
    {
        $categories = BlockRegistryCategory::orderBy('name')->get();
        $templates = $this->templateService->getAllAvailableTemplates();
        $fieldTypes = BlockRegistryField::FIELD_TYPES;
        $subFieldTypes = BlockRegistryField::getSubFieldTypes();
        $componentTemplates = $this->getComponentTemplates();

        return template('admin/block_registry/edit.twig', [
            'title' => 'Create Block',
            'block' => null,
            'categories' => $categories,
            'templates' => $templates,
            'componentTemplates' => $componentTemplates,
            'fieldTypes' => $fieldTypes,
            'subFieldTypes' => $subFieldTypes,
            'messages' => [],
            'is_production' => $this->isDefinitionLocked(),
        ]);
    }

    /**
     * Edit existing block
     */
    public function edit(int $id)
    {
        $block = BlockRegistry::with(['fields.subFields', 'templateAssignments', 'category'])->findOrFail($id);
        $categories = BlockRegistryCategory::orderBy('name')->get();
        $templates = $this->templateService->getAllAvailableTemplates();
        $fieldTypes = BlockRegistryField::FIELD_TYPES;
        $subFieldTypes = BlockRegistryField::getSubFieldTypes();
        $componentTemplates = $this->getComponentTemplates();
        $stats = $this->registryService->getBlockStatistics($block);
        $messages = $this->getFormMessages();

        return template('admin/block_registry/edit.twig', [
            'title' => 'Edit Block: ' . $block->name,
            'block' => $block,
            'categories' => $categories,
            'templates' => $templates,
            'componentTemplates' => $componentTemplates,
            'fieldTypes' => $fieldTypes,
            'subFieldTypes' => $subFieldTypes,
            'stats' => $stats,
            'messages' => $messages,
            'is_production' => $this->isDefinitionLocked(),
        ]);
    }

    /**
     * Store new block
     */
    public function store(Request $request)
    {
        if ($denied = $this->denyIfLocked()) {
            return $denied;
        }

        $validated = $request->validate([
            'name' => 'required|string|max:255',
            'slug' => 'nullable|string|max:255|unique:block_registry,slug',
            'description' => 'nullable|string',
            'category_id' => 'nullable|integer|exists:block_registry_categories,id',
            'icon' => 'nullable|string|max:100',
            'template' => 'nullable|string|max:255',
            'preview_image' => 'nullable|string|max:255',
            'is_active' => 'boolean',
        ]);

        // Parse fields from request
        $fields = $this->parseFieldsFromRequest($request);

        try {
            $block = $this->registryService->createBlock([
                'name' => $validated['name'],
                'slug' => $validated['slug'] ?? null,
                'description' => $validated['description'] ?? null,
                'category_id' => $validated['category_id'] ?? null,
                'icon' => $validated['icon'] ?? 'fa-cube',
                'template' => $validated['template'] ?? null,
                'preview_image' => $validated['preview_image'] ?? null,
                'is_active' => $validated['is_active'] ?? true,
                'fields' => $fields,
                'user_id' => $this->getCurrentUserId(),
            ]);

            // Handle template assignments
            $this->handleTemplateAssignments($block, $request);

            $this->autoExportBlock($block);
            $this->flashSuccess('Block created successfully.');
            return redirect()->route('admin.blocks.edit', $block->id);

        } catch (\Exception $e) {
            $this->flashError($e->getMessage());
            return back()->withInput();
        }
    }

    /**
     * Update existing block
     */
    public function update(Request $request, int $id)
    {
        if ($denied = $this->denyIfLocked()) {
            return $denied;
        }

        $block = BlockRegistry::findOrFail($id);

        $validated = $request->validate([
            'name' => 'required|string|max:255',
            'slug' => 'nullable|string|max:255|unique:block_registry,slug,' . $id,
            'description' => 'nullable|string',
            'category_id' => 'nullable|integer|exists:block_registry_categories,id',
            'icon' => 'nullable|string|max:100',
            'template' => 'nullable|string|max:255',
            'preview_image' => 'nullable|string|max:255',
            'is_active' => 'boolean',
        ]);

        // Parse fields from request
        $fields = $this->parseFieldsFromRequest($request);

        // Capture the slug before the update so a rename can migrate the
        // file-only example_data and clean up the stale old-slug JSON file.
        $previousSlug = $block->slug;

        try {
            $block = $this->registryService->updateBlock($block, [
                'name' => $validated['name'],
                'slug' => $validated['slug'] ?? $block->slug,
                'description' => $validated['description'] ?? null,
                'category_id' => $validated['category_id'] ?? null,
                'icon' => $validated['icon'] ?? $block->icon,
                'template' => $validated['template'] ?? null,
                'preview_image' => $validated['preview_image'] ?? null,
                'is_active' => $validated['is_active'] ?? true,
                'fields' => $fields,
            ], $this->getCurrentUserId());

            // Handle template assignments
            $this->handleTemplateAssignments($block, $request);

            $this->autoExportBlock($block, $previousSlug);
            $this->flashSuccess('Block updated successfully.');
            return redirect()->route('admin.blocks.edit', $block->id);

        } catch (\Exception $e) {
            $this->flashError($e->getMessage());
            return back()->withInput();
        }
    }

    /**
     * Delete block
     */
    public function destroy(int $id, Request $request)
    {
        if ($this->isDefinitionLocked()) {
            if ($request->ajax() || $request->wantsJson()) {
                return response()->json(['success' => false, 'error' => 'Block definitions cannot be modified in production.'], 403);
            }
            return $this->denyIfLocked();
        }

        $block = BlockRegistry::findOrFail($id);

        try {
            $this->registryService->deleteBlock($block);

            if ($request->ajax() || $request->wantsJson()) {
                return response()->json(['success' => true]);
            }

            $this->flashSuccess('Block deleted successfully.');
        } catch (\Exception $e) {
            if ($request->ajax() || $request->wantsJson()) {
                return response()->json(['success' => false, 'error' => $e->getMessage()], 422);
            }

            $this->flashError($e->getMessage());
        }

        return redirect()->route('admin.blocks.index');
    }

    /**
     * Duplicate block
     */
    public function duplicate(int $id)
    {
        if ($denied = $this->denyIfLocked()) {
            return $denied;
        }

        $block = BlockRegistry::findOrFail($id);

        try {
            $newBlock = $this->registryService->duplicateBlock($block);
            $this->autoExportBlock($newBlock);
            $this->flashSuccess('Block duplicated successfully.');
            return redirect()->route('admin.blocks.edit', $newBlock->id);
        } catch (\Exception $e) {
            $this->flashError($e->getMessage());
            return back();
        }
    }

    /**
     * Edit entity block data
     */
    public function editData(int $id)
    {
        $entityBlock = EntityBlock::with(['block', 'block.fields.subFields', 'block.category', 'sourceBlock'])->findOrFail($id);

        // If this is a global reference, redirect to edit the source global block
        if ($entityBlock->isGlobalReference() && $entityBlock->sourceBlock && $entityBlock->sourceBlock->isGlobalBlock()) {
            return redirect()->route('admin.blocks.data.edit', $entityBlock->source_block_id);
        }

        $fieldTypes = BlockRegistryField::FIELD_TYPES;
        $messages = $this->getFormMessages();

        // Build back URL based on entity type
        $backUrl = $this->getBackUrlForEntity($entityBlock);

        // "Fill with example content": when ?prefill=example is present and the
        // block has authored example_data, render the form populated from it
        // WITHOUT saving — the editor reviews and clicks Save to persist (or
        // navigates away to discard). Selector fields are blanked (local IDs
        // don't map across environments).
        $data = $entityBlock->data ?? [];
        $syncService = app(BlockSyncService::class);
        $hasExampleData = $syncService->hasExampleData($entityBlock->block->slug);
        $prefillExample = request('prefill') === 'example' && $hasExampleData;
        if ($prefillExample) {
            $exampleData = $syncService->getExampleData($entityBlock->block->slug);
            $sanitized = EntityBlock::sanitizeExampleData($exampleData, $entityBlock->block);
            $data = EntityBlock::normalizeExampleData($sanitized, $entityBlock->block);
        }

        // Pre-parse field data for the template
        $parsedData = [];
        foreach ($entityBlock->block->fields as $field) {
            $fieldName = $field->name;
            $value = $data[$fieldName] ?? '';

            // Parse JSON image data into url/alt/crop components
            if ($field->type === 'image' && is_string($value) && str_starts_with($value, '{')) {
                $imageData = json_decode($value, true);
                if (is_array($imageData)) {
                    $parsedData[$fieldName] = [
                        'url' => $imageData['url'] ?? '',
                        'alt' => $imageData['alt'] ?? '',
                        'crop' => $imageData['crop'] ?? [],
                        'processed' => $imageData['processed'] ?? [],
                        'srcset' => $imageData['srcset'] ?? '',
                    ];
                    continue;
                }
            }

            // Parse JSON file data into url component (same pattern as image)
            if ($field->type === 'file' && is_string($value) && str_starts_with($value, '{')) {
                $fileData = json_decode($value, true);
                if (is_array($fileData)) {
                    // Unwrap multi-encoded url values (from prior double-encoding bug)
                    $url = $fileData['url'] ?? '';
                    while (is_string($url) && str_starts_with($url, '{')) {
                        $nested = json_decode($url, true);
                        if (is_array($nested) && isset($nested['url'])) {
                            $url = $nested['url'];
                        } else {
                            break;
                        }
                    }
                    $parsedData[$fieldName] = [
                        'url' => $url,
                    ];
                    continue;
                }
            }

            // Parse link JSON data
            if ($field->type === 'link' && is_string($value) && str_starts_with($value, '{')) {
                $linkData = json_decode($value, true);
                if (is_array($linkData)) {
                    $parsedData[$fieldName] = $linkData;
                    continue;
                }
            }

            // Parse repeater JSON data
            if ($field->type === 'repeater' && is_string($value) && str_starts_with($value, '[')) {
                $repeaterData = json_decode($value, true);
                if (is_array($repeaterData)) {
                    $parsedData[$fieldName] = $repeaterData;
                    continue;
                }
            }

            // For non-image fields or legacy string values
            $parsedData[$fieldName] = $value;
        }

        $templateVars = [
            'title' => 'Edit Block Data: ' . $entityBlock->block->name,
            'entityBlock' => $entityBlock,
            'block' => $entityBlock->block,
            'fields' => $entityBlock->block->fields,
            'data' => $parsedData,
            'fieldTypes' => $fieldTypes,
            'backUrl' => $backUrl,
            'messages' => $messages,
            'hasExampleData' => $hasExampleData,
            'prefillExample' => $prefillExample,
        ];

        // Add global block context if editing a global block
        if ($entityBlock->isGlobalBlock()) {
            $templateVars['isGlobal'] = true;
            $templateVars['referenceCount'] = $entityBlock->references()->count();
        }

        return template('admin/block_registry/edit_data.twig', $templateVars);
    }

    /**
     * Update entity block data
     */
    public function updateData(Request $request, int $id)
    {
        $entityBlock = EntityBlock::with(['block', 'block.fields.subFields'])->findOrFail($id);
        $imageProcessor = app(ImageProcessingService::class);

        $cropWarnings = [];

        try {
            // Build data array from form fields
            $data = [];
            foreach ($entityBlock->block->fields as $field) {
                $fieldName = $field->name;
                // PHP converts spaces in form field names to underscores
                // So we need to check for both versions
                $formKeyOriginal = "field_{$fieldName}";
                $formKeySanitized = "field_" . str_replace(' ', '_', $fieldName);

                $value = $request->input($formKeyOriginal) ?? $request->input($formKeySanitized);

                // Handle different field types
                if ($field->type === 'checkbox') {
                    // Check both possible form keys for checkbox
                    $value = ($request->boolean($formKeyOriginal) || $request->boolean($formKeySanitized)) ? '1' : '0';
                } elseif ($field->type === 'image') {
                    // Process image with crop and generate srcset if crop is enabled
                    if (is_array($value) && !empty($value['url'])) {
                        // Check if this field has crop settings enabled
                        if ($imageProcessor->fieldHasCropEnabled($field)) {
                            $value = $imageProcessor->processImageField($entityBlock, $field, $value);
                            if (!empty($value['crop_rejected'])) {
                                $cropWarnings[] = ($field->label ?? $field->name) . ': ' . $value['crop_rejected_reason'];
                            }
                        }
                        $value = json_encode($value);
                    } elseif (is_array($value)) {
                        $value = json_encode($value);
                    }
                } elseif ($field->type === 'link') {
                    if (is_array($value)) {
                        $value = json_encode($value);
                    }
                } elseif ($field->type === 'repeater') {
                    // Handle repeater data - comes in as nested array
                    if (is_array($value)) {
                        // Process complex sub-fields within repeater rows
                        foreach ($field->subFields as $subField) {
                            if ($subField->type === 'image' && $imageProcessor->fieldHasCropEnabled($subField)) {
                                foreach ($value as $rowIndex => &$row) {
                                    if (isset($row[$subField->name]) && is_array($row[$subField->name]) && !empty($row[$subField->name]['url'])) {
                                        $row[$subField->name] = $imageProcessor->processImageField($entityBlock, $subField, $row[$subField->name], $rowIndex, $field->name);
                                    }
                                }
                                unset($row); // break reference
                            }
                        }
                        // Re-index the array to ensure sequential keys (in case rows were deleted)
                        $value = array_values($value);
                        $value = json_encode($value);
                    } else {
                        $value = '[]';
                    }
                } elseif (is_array($value)) {
                    $value = json_encode($value);
                }

                $data[$fieldName] = $value ?? '';
            }

            $entityBlock->data = $data;

            // Handle status field
            $status = $request->input('status', $entityBlock->status ?? EntityBlock::STATUS_DRAFT);
            $entityBlock->status = $status;

            $entityBlock->save();

            $statusMessage = $entityBlock->isPublished() ? 'Block published.' : 'Block saved as draft.';
            if (!empty($cropWarnings)) {
                $statusMessage .= ' Warning: ' . implode(' ', $cropWarnings);
                $this->flashWarning($statusMessage);
            } else {
                $this->flashSuccess($statusMessage);
            }

            // Return to edit page
            return redirect()->route('admin.blocks.data.edit', $id);

        } catch (\Exception $e) {
            $this->flashError($e->getMessage());
            return back()->withInput();
        }
    }

    /**
     * Delete entity block
     */
    public function destroyData(int $id)
    {
        $entityBlock = EntityBlock::findOrFail($id);
        $backUrl = $this->getBackUrlForEntity($entityBlock);
        $imageProcessor = app(ImageProcessingService::class);

        try {
            // Clean up processed images before deleting
            $imageProcessor->deleteProcessedImages($entityBlock->id);

            $entityBlock->delete();
            $this->flashSuccess('Block removed successfully.');
        } catch (\Exception $e) {
            $this->flashError($e->getMessage());
        }

        return redirect($backUrl);
    }

    /**
     * Preview entity block with full frontend styles
     */
    public function previewData(int $id)
    {
        $entityBlock = EntityBlock::with(['block', 'block.fields'])->findOrFail($id);

        if (!$entityBlock->block->hasTemplate()) {
            return response('No template linked to this block.', 404);
        }

        try {
            // Render the block using EntityBlockService
            $entityBlockService = app(\Mtc\Modules\BlockRegistry\Services\EntityBlockService::class);
            $blockOutput = $entityBlockService->renderBlock($entityBlock);

            if ($blockOutput === null) {
                $blockOutput = '<div class="preview-error">Failed to render block preview.</div>';
            }

            // Generate full HTML page with frontend styles (like ComponentRendererService)
            $html = $this->renderBlockPreviewHtml($entityBlock, $blockOutput);

            return response($html, 200, [
                'Content-Type' => 'text/html; charset=UTF-8',
            ]);

        } catch (\Exception $e) {
            \Log::error('Block preview error: ' . $e->getMessage());
            return response('Preview error: ' . htmlspecialchars($e->getMessage(), ENT_QUOTES, 'UTF-8'), 500);
        }
    }

    /**
     * Preview a block definition (uses an existing instance with real data if available)
     */
    public function previewBlockDefinition(int $id)
    {
        $block = BlockRegistry::with('fields')->findOrFail($id);

        if (!$block->hasTemplate()) {
            return response('No template linked to this block.', 404);
        }

        try {
            // Try to find an existing entity block with real content for a better preview
            $entityBlock = EntityBlock::where('block_id', $block->id)
                ->whereNotNull('data')
                ->with(['block', 'block.fields'])
                ->first();

            if ($entityBlock) {
                $entityBlock->setRelation('block', $block);
                $entityBlockService = app(EntityBlockService::class);
                $blockOutput = $entityBlockService->renderBlock($entityBlock);
            } else {
                // No instances exist — render template with show_dummy_data flag
                // so the Twig template uses its built-in sample content for the preview
                $twig = app('twig');
                $templatePath = $block->getTemplatePath();
                $blockOutput = $twig->render($templatePath, [
                    'show_dummy_data' => true,
                    'block' => $block,
                    'block_name' => $block->name,
                    'block_slug' => $block->slug,
                ]);
            }

            if ($blockOutput === null) {
                $blockOutput = '<div class="preview-error">Failed to render block preview.</div>';
            }

            $html = $this->renderPreviewHtml('Block Preview: ' . $block->name, $blockOutput);

            return response($html, 200, [
                'Content-Type' => 'text/html; charset=UTF-8',
            ]);
        } catch (\Exception $e) {
            \Log::error('Block definition preview error: ' . $e->getMessage());
            return response('Preview error: ' . htmlspecialchars($e->getMessage(), ENT_QUOTES, 'UTF-8'), 500);
        }
    }

    /**
     * AJAX: Toggle entity block status (draft/published)
     */
    public function ajaxToggleStatus(Request $request)
    {
        if ($error = $this->requireAjaxRequest()) {
            return $error;
        }

        $id = (int) $request->input('id');
        $status = $request->input('status');

        if (!$id) {
            return response()->json(['error' => 'id is required'], 400);
        }

        if (!in_array($status, [EntityBlock::STATUS_DRAFT, EntityBlock::STATUS_PUBLISHED])) {
            return response()->json(['error' => 'Invalid status'], 400);
        }

        try {
            $entityBlock = EntityBlock::findOrFail($id);
            $entityBlock->status = $status;
            $entityBlock->save();

            return response()->json([
                'success' => true,
                'status' => $entityBlock->status,
                'message' => $entityBlock->isPublished() ? 'Block published.' : 'Block set to draft.',
            ]);

        } catch (\Exception $e) {
            return response()->json([
                'success' => false,
                'error' => 'Failed to update status',
                'message' => $e->getMessage(),
            ], 500);
        }
    }

    /**
     * AJAX: Toggle block definition active status
     */
    public function ajaxToggleActive(Request $request)
    {
        if ($this->isDefinitionLocked()) {
            return response()->json(['success' => false, 'error' => 'Block definitions cannot be modified in production.'], 403);
        }

        if ($error = $this->requireAjaxRequest()) {
            return $error;
        }

        $id = (int) $request->input('id');
        $isActive = (bool) $request->input('is_active');

        if (!$id) {
            return response()->json(['error' => 'id is required'], 400);
        }

        try {
            $block = BlockRegistry::findOrFail($id);
            $block->is_active = $isActive;
            $block->save();

            return response()->json([
                'success' => true,
                'is_active' => $block->is_active,
                'message' => $block->is_active ? 'Block activated.' : 'Block deactivated.',
            ]);
        } catch (\Exception $e) {
            return response()->json([
                'success' => false,
                'error' => 'Failed to update status',
                'message' => $e->getMessage(),
            ], 500);
        }
    }

    /**
     * AJAX: Get active products for product selector fields
     */
    public function ajaxGetProducts(Request $request)
    {
        $products = \Mtc\Shop\Item::query()
            ->active()
            ->orderBy('name', 'ASC')
            ->get(['id', 'name']);

        return response()->json($products);
    }

    /**
     * AJAX: Search internal content for link fields
     */
    public function ajaxSearchInternalLinks(Request $request)
    {
        $query = trim($request->input('q', ''));
        $type = trim($request->input('type', ''));
        $results = [];

        // Pages
        if ($type === '' || $type === 'page') {
            $pages = \Mtc\Cms\Models\Page::query()
                ->where('draft_for_page_id', 0)
                ->where('hide_in_cms', 0)
                ->when($query, function ($q) use ($query) {
                    $q->where('title', 'like', "%{$query}%");
                })
                ->orderBy('title')
                ->limit(30)
                ->get(['id', 'title', 'slug', 'url']);

            foreach ($pages as $page) {
                $url = $page->url ?: ($page->slug ? '/' . $page->slug : '/');
                $results[] = [
                    'url' => $url,
                    'title' => $page->title,
                    'type' => 'PAGE',
                ];
            }
        }

        // Items (products)
        if ($type === '' || $type === 'item') {
            $items = \Mtc\Shop\Item::query()
                ->active()
                ->when($query, function ($q) use ($query) {
                    $q->where('name', 'like', "%{$query}%");
                })
                ->orderBy('name')
                ->limit(30)
                ->get();

            foreach ($items as $item) {
                $results[] = [
                    'url' => $item->url,
                    'title' => $item->name,
                    'type' => 'ITEM',
                ];
            }
        }

        // Categories
        if ($type === '' || $type === 'category') {
            $categories = \Mtc\Shop\Category::query()
                ->where('hide', 0)
                ->when($query, function ($q) use ($query) {
                    $q->where(function ($q2) use ($query) {
                        $q2->where('name', 'like', "%{$query}%")
                           ->orWhere('display_name', 'like', "%{$query}%");
                    });
                })
                ->orderBy('name')
                ->limit(30)
                ->get();

            foreach ($categories as $category) {
                $title = $category->display_name ?: $category->name;

                if ($category->is_online_doctor) {
                    $results[] = [
                        'url' => $category->getLink(false),
                        'title' => $title,
                        'type' => 'CATEGORY LANDING',
                    ];
                }

                // Browse/filter URL for all categories
                $browseUrl = '/pharmacy/' . $category->id . '-' . Str::slug($category->name) . '-category/';
                $results[] = [
                    'url' => $browseUrl,
                    'title' => $title,
                    'type' => 'CATEGORY',
                ];
            }
        }

        // Assessment Forms
        if ($type === '' || $type === 'assessment') {
            $forms = \Mtc\Shop\Assessment\Form::query()
                ->when($query, function ($q) use ($query) {
                    $q->where('name', 'like', "%{$query}%");
                })
                ->orderBy('name')
                ->limit(30)
                ->get();

            foreach ($forms as $form) {
                $results[] = [
                    'url' => '/assessment/' . $form->id,
                    'title' => $form->name,
                    'type' => 'ASSESSMENT',
                ];
            }
        }

        return response()->json($results);
    }

    /**
     * AJAX: Search CMS pages for page_selector field
     */
    public function ajaxSearchPages(Request $request)
    {
        $query = trim($request->input('q', ''));
        $containerId = $request->input('container_id');
        $publishedOnly = $request->boolean('published_only', true);

        $pages = \Mtc\Cms\Models\Page::query()
            ->where(function ($q) {
                $q->whereNull('draft_for_page_id')->orWhere('draft_for_page_id', 0);
            })
            ->where('hide_in_cms', 0)
            ->where('type', 'default')
            ->when($containerId, function ($q) use ($containerId) {
                $q->where('sub_id', $containerId);
            })
            ->when($publishedOnly, function ($q) {
                $q->where('published', 1);
            })
            ->when($query, function ($q) use ($query) {
                $q->where('title', 'like', "%{$query}%");
            })
            ->orderBy('title')
            ->limit(30)
            ->get(['id', 'title', 'url', 'sub_id', 'published']);

        $results = $pages->map(function ($page) {
            return [
                'id' => $page->id,
                'title' => $page->title,
                'url' => $page->url ?: '/',
                'published' => (bool) $page->published,
            ];
        });

        return response()->json($results);
    }

    /**
     * AJAX: Get listing container pages for page_selector settings
     */
    public function ajaxGetListingContainers()
    {
        $containers = \Mtc\Cms\Models\Page::query()
            ->where('listing_container', 1)
            ->where('hide_in_cms', 0)
            ->orderBy('title')
            ->get(['id', 'title']);

        return response()->json($containers);
    }

    /**
     * AJAX: Reorder entity blocks
     */
    public function ajaxReorderBlocks(Request $request)
    {
        if ($error = $this->requireAjaxRequest()) {
            return $error;
        }

        $input = $request->json()->all();

        if (empty($input['blocks']) || !is_array($input['blocks'])) {
            return response()->json(['success' => false, 'error' => 'blocks array is required'], 400);
        }

        try {
            $blocks = collect($input['blocks'])->filter(fn($item) => isset($item['id'], $item['order']));

            if ($blocks->isEmpty()) {
                return response()->json(['success' => true, 'message' => 'No blocks to update', 'updated' => 0]);
            }

            $ids = $blocks->pluck('id')->map(fn($id) => (int) $id);

            // Defensive check: all reordered blocks must belong to the same
            // (entity_type, entity_id, region) bucket. NULL region is treated
            // as 'main' so legacy rows and explicit 'main' rows share a bucket
            // and can be reordered together. Prevents a malformed payload from
            // mixing ordering across regions or entities.
            $rows = EntityBlock::query()
                ->whereIn('id', $ids)
                ->get(['id', 'entity_type', 'entity_id', 'region']);

            if ($rows->count() !== $ids->count()) {
                return response()->json([
                    'success' => false,
                    'error' => 'One or more block IDs were not found',
                ], 400);
            }

            $buckets = $rows
                ->map(fn($row) => $row->entity_type
                    . ':' . (int) $row->entity_id
                    . ':' . ($row->region ?? EntityBlock::DEFAULT_REGION))
                ->unique();

            if ($buckets->count() > 1) {
                return response()->json([
                    'success' => false,
                    'error' => 'All blocks must belong to the same entity and region',
                ], 400);
            }

            $cases = $blocks->map(fn($item) => "WHEN id = " . (int) $item['id'] . " THEN " . (int) $item['order'])->implode(' ');

            EntityBlock::whereIn('id', $ids)->update([
                'order' => DB::raw("CASE {$cases} END"),
            ]);

            // Mass query-builder updates bypass Eloquent model events, so the
            // saved-listener that normally clears the rendered cache never
            // fires. All rows share one (entity_type, entity_id) bucket — we
            // verified that above — so a single clearCache call covers them.
            $first = $rows->first();
            EntityBlockService::clearCache($first->entity_type, (int) $first->entity_id);

            return response()->json([
                'success' => true,
                'message' => "Updated order for {$ids->count()} block(s)",
                'updated' => $ids->count(),
            ]);

        } catch (\Exception $e) {
            return response()->json([
                'success' => false,
                'error' => 'Failed to update order',
                'message' => $e->getMessage(),
            ], 500);
        }
    }

    /**
     * Render full HTML preview page with frontend styles for a single block
     */
    protected function renderBlockPreviewHtml(EntityBlock $entityBlock, string $blockOutput): string
    {
        $blockName = $entityBlock->block->name;
        return $this->renderPreviewHtml('Block Preview: ' . $blockName, $blockOutput);
    }

    /**
     * Get all instances of a block definition for the instances modal
     */
    public function ajaxGetBlockInstances(int $id)
    {
        BlockRegistry::findOrFail($id);

        $instances = EntityBlock::where('block_id', $id)
            ->orderBy('entity_type')
            ->orderBy('entity_id')
            ->get();

        $entityNames = [];
        $results = [];

        foreach ($instances as $instance) {
            $key = $instance->entity_type . ':' . $instance->entity_id;

            if (!isset($entityNames[$key])) {
                $entityNames[$key] = $this->resolveEntityName($instance->entity_type, $instance->entity_id);
            }

            $results[] = [
                'id' => $instance->id,
                'entity_type' => $instance->entity_type,
                'entity_id' => $instance->entity_id,
                'entity_name' => $entityNames[$key],
                'status' => $instance->status,
                'edit_url' => '/admin/blocks/data/' . $instance->id,
                'entity_edit_url' => $this->getBackUrlForEntity($instance),
            ];
        }

        return response()->json(['instances' => $results]);
    }

    /**
     * Resolve a human-readable name for an entity
     */
    protected function resolveEntityName(string $entityType, int $entityId): string
    {
        switch ($entityType) {
            case 'page':
                $page = \Mtc\Cms\Models\Page::find($entityId, ['id', 'title']);
                return $page ? $page->title : 'Page #' . $entityId;
            case 'item':
                $item = \Mtc\Shop\Item::find($entityId, ['id', 'name']);
                return $item ? $item->name : 'Product #' . $entityId;
            case 'category':
                $cat = \Mtc\Shop\Category::find($entityId, ['id', 'name']);
                return $cat ? $cat->name : 'Category #' . $entityId;
            case 'global':
                return 'Global Block';
            default:
                return ucfirst($entityType) . ' #' . $entityId;
        }
    }

    /**
     * Get back URL based on entity type
     */
    protected function getBackUrlForEntity(EntityBlock $entityBlock): string
    {
        switch ($entityBlock->entity_type) {
            case 'page':
                return '/cms/admin/content_manager/?page=' . $entityBlock->entity_id;
            case 'item':
                return '/modules/BlockRegistry/admin/products/blocks.php?id=' . $entityBlock->entity_id;
            case 'category':
                return '/modules/BlockRegistry/admin/categories/blocks.php?id=' . $entityBlock->entity_id;
            case 'global':
                return '/admin/blocks/global';
            default:
                return '/admin/blocks';
        }
    }

    /**
     * Save the current populated entity block as a new global block.
     * The original placement is converted to a reference of the new global.
     */
    public function saveAsGlobal(Request $request, int $id)
    {
        $entityBlock = EntityBlock::findOrFail($id);

        if ($entityBlock->isGlobalBlock() || $entityBlock->isGlobalReference()) {
            $this->flashError('This block cannot be saved as a global.');
            return redirect()->route('admin.blocks.data.edit', $id);
        }

        $validated = $request->validate([
            'global_name' => 'required|string|max:255',
        ]);

        $name = trim($validated['global_name']);
        if ($name === '') {
            $this->flashError('Global block name cannot be blank.');
            return redirect()->route('admin.blocks.data.edit', $id);
        }

        try {
            $global = app(EntityBlockService::class)
                ->promoteToGlobal($entityBlock, $name);

            $this->flashSuccess('Block saved as global. The original is now linked to it.');
            return redirect()->route('admin.blocks.data.edit', $global->id);
        } catch (\Exception $e) {
            $this->flashError($e->getMessage());
            return redirect()->route('admin.blocks.data.edit', $id);
        }
    }

    /**
     * List all global blocks
     */
    public function globalIndex()
    {
        $entityBlockService = app(EntityBlockService::class);
        $globalBlocks = $entityBlockService->getGlobalBlocks();
        $messages = $this->getFormMessages();

        return template('admin/block_registry/global_index.twig', [
            'title' => 'Global Blocks',
            'globalBlocks' => $globalBlocks,
            'messages' => $messages,
        ]);
    }

    /**
     * Create global block form
     */
    public function globalCreate()
    {
        $blocks = BlockRegistry::where('is_active', true)
            ->with('category')
            ->orderBy('name')
            ->get();
        $categories = BlockRegistryCategory::orderBy('name')->get();

        return template('admin/block_registry/global_create.twig', [
            'title' => 'Create Global Block',
            'blocks' => $blocks,
            'categories' => $categories,
        ]);
    }

    /**
     * Store a new global block
     */
    public function globalStore(Request $request)
    {
        $validated = $request->validate([
            'name' => 'required|string|max:255',
            'block_id' => 'required|integer|exists:block_registry,id',
        ]);

        try {
            $block = BlockRegistry::with('fields')->findOrFail($validated['block_id']);

            $entityBlock = EntityBlock::create([
                'entity_type' => 'global',
                'entity_id' => 0,
                'block_id' => $validated['block_id'],
                'name' => $validated['name'],
                'status' => EntityBlock::STATUS_DRAFT,
            ]);

            $this->flashSuccess('Global block created. Fill in the content below.');
            return redirect()->route('admin.blocks.data.edit', $entityBlock->id);

        } catch (\Exception $e) {
            $this->flashError($e->getMessage());
            return back()->withInput();
        }
    }

    /**
     * Delete a global block
     */
    public function globalDestroy(int $id)
    {
        $globalBlock = EntityBlock::global()->findOrFail($id);
        $referenceCount = $globalBlock->references()->count();

        try {
            // References will have source_block_id set to null by FK ON DELETE SET NULL
            $globalBlock->delete();

            $message = 'Global block deleted.';
            if ($referenceCount > 0) {
                $message .= " {$referenceCount} reference(s) have been unlinked.";
            }
            $this->flashSuccess($message);
        } catch (\Exception $e) {
            $this->flashError($e->getMessage());
        }

        return redirect()->route('admin.blocks.global.index');
    }

    /**
     * AJAX: Get available blocks for any entity type
     */
    public function ajaxGetAvailableBlocksForEntity(Request $request)
    {
        $entity_type = $request->input('entity_type');
        $entity_id = (int) $request->input('entity_id');

        if (!$entity_type || !$entity_id) {
            return response()->json(['error' => 'entity_type and entity_id are required'], 400);
        }
        if ($error = $this->validateEntityType($entity_type)) {
            return $error;
        }

        $search = trim($request->input('search', ''));
        $category_id = $request->input('category_id') ? (int) $request->input('category_id') : null;

        try {
            // Map entity type to virtual template name for whitelisting
            $templateMap = [
                'item' => 'shop_item',
                'category' => 'shop_category',
            ];
            $templateName = $templateMap[$entity_type] ?? null;

            // For pages, resolve the CMS template
            if ($entity_type === 'page' && $templateName === null) {
                try {
                    $page = \ContentManagerPanel::loadPage($entity_id, false);
                    $templateName = $page->template ?? null;
                } catch (\Exception $e) {
                    // Fall through to showing all blocks
                }
            }

            // Get blocks whitelisted for this template
            $blocks = collect();
            if ($templateName) {
                $blocks = $this->templateService->getWhitelistedBlocks($templateName);
            }

            // Apply search filter
            if (!empty($search)) {
                $blocks = $blocks->filter(function ($block) use ($search) {
                    return stripos($block->name, $search) !== false ||
                           stripos($block->slug, $search) !== false ||
                           stripos($block->description, $search) !== false;
                });
            }

            // Apply category filter
            if ($category_id !== null) {
                $blocks = $blocks->filter(function ($block) use ($category_id) {
                    return $block->category_id === $category_id;
                });
            }

            $categories = BlockRegistryCategory::orderBy('order_index')->get();

            // Get published global blocks
            $globalBlocks = EntityBlock::global()
                ->where('status', EntityBlock::STATUS_PUBLISHED)
                ->with(['block', 'block.category'])
                ->ordered()
                ->get();

            return response()->json([
                'success' => true,
                'categories' => $categories->map(function ($cat) {
                    return [
                        'id' => $cat->id,
                        'name' => $cat->name,
                        'slug' => $cat->slug,
                        'icon' => $cat->icon,
                    ];
                })->values()->toArray(),
                'blocks' => $blocks->map(function ($block) {
                    return [
                        'id' => $block->id,
                        'name' => $block->name,
                        'slug' => $block->slug,
                        'description' => $block->description,
                        'icon' => $block->icon,
                        'category_id' => $block->category_id,
                        'category_name' => $block->category ? $block->category->name : null,
                        'field_count' => $block->fields ? $block->fields->count() : 0,
                        'has_template' => $block->hasTemplate(),
                    ];
                })->values()->toArray(),
                'global_blocks' => $globalBlocks->map(function ($gb) {
                    return [
                        'id' => $gb->id,
                        'name' => $gb->name,
                        'block_name' => $gb->block->name ?? 'Unknown',
                        'block_slug' => $gb->block->slug ?? '',
                        'icon' => $gb->block->icon ?? 'fa-cube',
                        'category_name' => $gb->block->category->name ?? null,
                        'category_id' => $gb->block->category_id ?? null,
                        'has_template' => $gb->block ? $gb->block->hasTemplate() : false,
                    ];
                })->values()->toArray(),
            ]);

        } catch (\Exception $e) {
            return response()->json([
                'error' => 'Failed to load blocks',
                'message' => $e->getMessage(),
            ], 500);
        }
    }

    /**
     * AJAX: Add a block to any entity type (items, categories)
     */
    public function ajaxAddBlockToEntity(Request $request)
    {
        if ($error = $this->requireAjaxRequest()) {
            return $error;
        }

        $entity_type = $request->input('entity_type');
        $entity_id = (int) $request->input('entity_id');
        $block_id = (int) $request->input('block_id');
        $region = $this->resolveRegionInput($request, $entity_type, $entity_id);

        if (!$entity_type || !$entity_id) {
            return response()->json(['error' => 'entity_type and entity_id are required'], 400);
        }
        if ($error = $this->validateEntityType($entity_type)) {
            return $error;
        }
        if (!$block_id) {
            return response()->json(['error' => 'block_id is required'], 400);
        }

        try {
            $block = BlockRegistry::with('fields')->find($block_id);
            if (!$block) {
                return response()->json(['error' => 'Block not found in registry'], 404);
            }

            $entityBlock = EntityBlock::create([
                'entity_type' => $entity_type,
                'entity_id' => $entity_id,
                'block_id' => $block_id,
                'region' => $region,
                'status' => EntityBlock::STATUS_DRAFT,
            ]);

            return response()->json([
                'success' => true,
                'message' => "Block '{$block->name}' added successfully",
                'block' => [
                    'id' => $block->id,
                    'name' => $block->name,
                    'slug' => $block->slug,
                    'icon' => $block->icon,
                ],
                'entity_block_id' => $entityBlock->id,
                'edit_url' => '/admin/blocks/data/' . $entityBlock->id,
            ]);

        } catch (\Exception $e) {
            $response = ['error' => 'Failed to add block'];
            if (config('app.debug')) {
                $response['message'] = $e->getMessage();
            }
            return response()->json($response, 500);
        }
    }

    /**
     * AJAX: Add a global block reference to any entity type (items, categories)
     */
    public function ajaxAddGlobalBlockToEntity(Request $request)
    {
        if ($error = $this->requireAjaxRequest()) {
            return $error;
        }

        $entity_type = $request->input('entity_type');
        $entity_id = (int) $request->input('entity_id');
        $global_block_id = (int) $request->input('global_block_id');
        $region = $this->resolveRegionInput($request, $entity_type, $entity_id);

        if (!$entity_type || !$entity_id) {
            return response()->json(['error' => 'entity_type and entity_id are required'], 400);
        }
        if ($error = $this->validateEntityType($entity_type)) {
            return $error;
        }
        if (!$global_block_id) {
            return response()->json(['error' => 'global_block_id is required'], 400);
        }

        try {
            $entityBlockService = app(EntityBlockService::class);
            $reference = $entityBlockService->addGlobalBlockReference($global_block_id, $entity_type, $entity_id, $region);

            $globalBlock = EntityBlock::with('block')->find($global_block_id);
            $name = $globalBlock->name ?? 'Global block';

            return response()->json([
                'success' => true,
                'message' => "Global block '{$name}' added",
                'entity_block_id' => $reference->id,
            ]);

        } catch (\Exception $e) {
            $response = ['error' => 'Failed to add global block'];
            if (config('app.debug')) {
                $response['message'] = $e->getMessage();
            }
            return response()->json($response, 500);
        }
    }

    /**
     * AJAX: Return the entity blocks HTML section for any entity type
     */
    public function ajaxEntityBlocksHtmlForEntity(Request $request)
    {
        if ($error = $this->requireAjaxRequest()) {
            return $error;
        }

        $entity_type = $request->input('entity_type');
        $entity_id = (int) $request->input('entity_id');

        if (!$entity_type || !$entity_id) {
            return response('entity_type and entity_id are required', 400);
        }
        if (!in_array($entity_type, self::VALID_ENTITY_TYPES, true)) {
            return response('Invalid entity_type', 400);
        }

        if ($entity_type === 'page') {
            $page = \ContentManagerPanel::loadPage($entity_id, false);
            if (empty($page->id)) {
                return response('Page not found', 404);
            }
            $post = (array) $page;
            ob_start();
            include base_path('modules/BlockRegistry/admin/content_manager/templates/edit_page_entity_blocks.php');
            $html = ob_get_clean();
        } else {
            ob_start();
            include base_path('modules/BlockRegistry/admin/templates/entity_blocks.php');
            $html = ob_get_clean();
        }

        return response($html);
    }

    /**
     * Preview all blocks for any entity type
     */
    public function previewEntityBlocks(string $entityType, int $entityId)
    {
        $entityBlocks = EntityBlock::forEntity($entityType, $entityId)
            ->with(['block', 'block.fields'])
            ->ordered()
            ->get();

        if ($entityBlocks->isEmpty()) {
            return response('No blocks found for this ' . htmlspecialchars($entityType, ENT_QUOTES, 'UTF-8') . '.', 404);
        }

        $entityBlockService = app(\Mtc\Modules\BlockRegistry\Services\EntityBlockService::class);
        $blocksHtml = '';

        foreach ($entityBlocks as $entityBlock) {
            if (!$entityBlock->block || !$entityBlock->block->hasTemplate()) {
                continue;
            }

            try {
                $blockOutput = $entityBlockService->renderBlock($entityBlock);
            } catch (\Exception $e) {
                $blockOutput = null;
            }

            if ($blockOutput === null) {
                continue;
            }

            if ($entityBlock->isDraft()) {
                $blocksHtml .= '<div class="block-draft-overlay">'
                    . '<span class="block-draft-badge">DRAFT</span>'
                    . $blockOutput
                    . '</div>';
            } else {
                $blocksHtml .= $blockOutput;
            }
        }

        if (empty($blocksHtml)) {
            return response('No blocks with templates found.', 404);
        }

        $draftStyles = '        .block-draft-overlay {
            position: relative;
            opacity: 0.55;
            border: 2px dashed #6c757d;
        }
        .block-draft-badge {
            position: absolute;
            top: 8px;
            right: 12px;
            background: #6c757d;
            color: #fff;
            padding: 2px 10px;
            border-radius: 3px;
            font-size: 11px;
            font-family: -apple-system, BlinkMacSystemFont, sans-serif;
            z-index: 100;
            letter-spacing: 0.5px;
        }
';
        $title = ucfirst($entityType) . ' Blocks Preview (' . $entityType . ' ' . $entityId . ')';
        $html = $this->renderPreviewHtml($title, $blocksHtml, $draftStyles);

        return response($html, 200, [
            'Content-Type' => 'text/html; charset=UTF-8',
            'X-Robots-Tag' => 'noindex, nofollow',
        ]);
    }

    /**
     * Categories management
     */
    public function categories()
    {
        $categories = BlockRegistryCategory::withCount('blocks')->orderBy('order_index')->get();
        $messages = $this->getFormMessages();

        return template('admin/block_registry/categories.twig', [
            'title' => 'Block Categories',
            'categories' => $categories,
            'messages' => $messages,
            'is_production' => $this->isDefinitionLocked(),
        ]);
    }

    /**
     * Store/update category
     */
    public function categoryStore(Request $request, ?int $id = null)
    {
        if ($denied = $this->denyIfLocked()) {
            return $denied;
        }

        $validated = $request->validate([
            'name' => 'required|string|max:100',
            'slug' => 'nullable|string|max:100',
            'icon' => 'nullable|string|max:50',
            'order_index' => 'nullable|integer',
        ]);

        try {
            if ($id) {
                $category = BlockRegistryCategory::findOrFail($id);
                $category->update($validated);
            } else {
                BlockRegistryCategory::create($validated);
            }

            $this->flashSuccess('Category saved.');
        } catch (\Exception $e) {
            $this->flashError($e->getMessage());
        }

        return back();
    }

    /**
     * Delete category
     */
    public function categoryDestroy(int $id)
    {
        if ($denied = $this->denyIfLocked()) {
            return $denied;
        }

        $category = BlockRegistryCategory::findOrFail($id);

        // Move blocks to uncategorized before deleting
        BlockRegistry::where('category_id', $id)->update(['category_id' => null]);

        $category->delete();

        $this->flashSuccess('Category deleted.');
        return back();
    }

    /**
     * Parse fields from request
     */
    protected function parseFieldsFromRequest(Request $request): array
    {
        $fields = [];
        $fieldNames = $request->input('field_name', []);
        $fieldTypes = $request->input('field_type', []);
        $fieldLabels = $request->input('field_label', []);
        $fieldRequired = $request->input('field_required', []);
        $fieldCssClass = $request->input('field_css_class', []);
        $fieldSettings = $request->input('field_settings', []);
        $fieldOptions = $request->input('field_options', []);
        $fieldInfo = $request->input('field_info', []);

        // Repeater settings
        $repeaterMin = $request->input('field_repeater_min', []);
        $repeaterMax = $request->input('field_repeater_max', []);
        $repeaterText = $request->input('field_repeater_text', []);

        // Image settings
        $cropEnabled = $request->input('field_crop_enabled', []);
        $aspectRatio = $request->input('field_aspect_ratio', []);
        $customRatio = $request->input('field_custom_ratio', []);
        $srcsetSizes = $request->input('field_srcset_sizes', []);

        // Page selector settings
        $pageMode = $request->input('field_page_mode', []);
        $pageContainer = $request->input('field_page_container', []);
        $pageLimit = $request->input('field_page_limit', []);

        // Conditional visibility settings
        $visibilityField = $request->input('field_visibility_field', []);
        $visibilityValues = $request->input('field_visibility_values', []);
        $subFieldVisibilityField = $request->input('subfield_visibility_field', []);
        $subFieldVisibilityValues = $request->input('subfield_visibility_values', []);

        // Sub-fields for repeaters
        $subFieldNames = $request->input('subfield_name', []);
        $subFieldTypes = $request->input('subfield_type', []);
        $subFieldLabels = $request->input('subfield_label', []);
        $subFieldRequired = $request->input('subfield_required', []);
        $subFieldParentIndex = $request->input('subfield_parent_index', []);
        $subFieldCssClass = $request->input('subfield_css_class', []);
        $subFieldOptions = $request->input('subfield_options', []);
        $subFieldInfo = $request->input('subfield_info', []);
        $subFieldCropEnabled = $request->input('subfield_crop_enabled', []);
        $subFieldAspectRatio = $request->input('subfield_aspect_ratio', []);
        $subFieldCustomRatio = $request->input('subfield_custom_ratio', []);
        $subFieldSrcsetSizes = $request->input('subfield_srcset_sizes', []);

        foreach ($fieldNames as $index => $name) {
            if (empty($name)) continue;

            $fieldData = [
                'name' => $name,
                'label' => $fieldLabels[$index] ?? $name,
                'type' => $fieldTypes[$index] ?? 'text',
                'order_index' => $index,
                'required' => isset($fieldRequired[$index]),
                'css_class' => $fieldCssClass[$index] ?? null,
                'settings' => $fieldSettings[$index] ?? null,
                'options' => $fieldOptions[$index] ?? null,
                'info' => $fieldInfo[$index] ?? null,
                'sub_fields' => [],
            ];

            // Add repeater settings if this is a repeater field
            if ($fieldData['type'] === 'repeater') {
                $fieldData['repeater_min'] = (int) ($repeaterMin[$index] ?? 0);
                $fieldData['repeater_max'] = (int) ($repeaterMax[$index] ?? 100);
                $fieldData['repeater_text'] = $repeaterText[$index] ?? 'Add Row';

                // Collect sub-fields for this repeater
                foreach ($subFieldNames as $subIndex => $subName) {
                    if (empty($subName)) continue;
                    if (($subFieldParentIndex[$subIndex] ?? -1) != $index) continue;

                    $subFieldData = [
                        'name' => $subName,
                        'label' => $subFieldLabels[$subIndex] ?? $subName,
                        'type' => $subFieldTypes[$subIndex] ?? 'text',
                        'required' => isset($subFieldRequired[$subIndex]),
                        'css_class' => $subFieldCssClass[$subIndex] ?? null,
                        'options' => $subFieldOptions[$subIndex] ?? null,
                        'info' => $subFieldInfo[$subIndex] ?? null,
                    ];

                    // Build image settings for image sub-fields with crop enabled
                    if ($subFieldData['type'] === 'image' && ($subFieldCropEnabled[$subIndex] ?? '0') === '1') {
                        $subFieldData['settings'] = $this->buildImageSettingsJson(
                            $subFieldSrcsetSizes[$subIndex] ?? '',
                            $subFieldAspectRatio[$subIndex] ?? '',
                            $subFieldCustomRatio[$subIndex] ?? ''
                        );
                    }

                    // Merge conditional visibility settings for sub-fields
                    if (!empty($subFieldVisibilityField[$subIndex])) {
                        $visSettings = ['visibility' => [
                            'field' => $subFieldVisibilityField[$subIndex],
                            'values' => array_values(array_filter(explode(',', $subFieldVisibilityValues[$subIndex] ?? ''))),
                        ]];
                        $existing = isset($subFieldData['settings']) && $subFieldData['settings'] ? (json_decode($subFieldData['settings'], true) ?? []) : [];
                        $subFieldData['settings'] = json_encode(array_merge($existing, $visSettings));
                    } else {
                        if (isset($subFieldData['settings']) && $subFieldData['settings']) {
                            $existing = json_decode($subFieldData['settings'], true);
                            if (is_array($existing)) {
                                unset($existing['visibility']);
                                $subFieldData['settings'] = !empty($existing) ? json_encode($existing) : null;
                            }
                        }
                    }

                    $fieldData['sub_fields'][] = $subFieldData;
                }
            }

            // Add image settings if this is an image field with crop enabled
            if ($fieldData['type'] === 'image' && isset($cropEnabled[$index])) {
                $fieldData['settings'] = $this->buildImageSettingsJson(
                    $srcsetSizes[$index] ?? '',
                    $aspectRatio[$index] ?? '',
                    $customRatio[$index] ?? ''
                );
            }

            // Add page selector settings
            if ($fieldData['type'] === 'page_selector') {
                $fieldData['settings'] = json_encode([
                    'page_selector_settings' => [
                        'mode' => $pageMode[$index] ?? 'manual',
                        'container_id' => !empty($pageContainer[$index]) ? (int) $pageContainer[$index] : null,
                        'limit' => (int) ($pageLimit[$index] ?? 8),
                    ]
                ]);
            }

            // Merge conditional visibility settings
            if (!empty($visibilityField[$index])) {
                $visSettings = ['visibility' => [
                    'field' => $visibilityField[$index],
                    'values' => array_values(array_filter(explode(',', $visibilityValues[$index] ?? ''))),
                ]];
                $existing = $fieldData['settings'] ? (json_decode($fieldData['settings'], true) ?? []) : [];
                $fieldData['settings'] = json_encode(array_merge($existing, $visSettings));
            } else {
                // Remove visibility from settings if cleared
                if ($fieldData['settings']) {
                    $existing = json_decode($fieldData['settings'], true);
                    if (is_array($existing)) {
                        unset($existing['visibility']);
                        $fieldData['settings'] = !empty($existing) ? json_encode($existing) : null;
                    }
                }
            }

            $fields[] = $fieldData;
        }

        // Strip orphaned visibility references: if a controller field was renamed or deleted,
        // drop the visibility rule so the dependent field doesn't silently stay always-visible.
        $validTopLevel = [];
        foreach ($fields as $f) {
            if (($f['type'] ?? null) === 'select') {
                $validTopLevel[$f['name']] = true;
            }
        }
        $stripOrphan = function (?string $settingsJson, array $validNames): ?string {
            if (!$settingsJson) return $settingsJson;
            $decoded = json_decode($settingsJson, true);
            if (!is_array($decoded) || !isset($decoded['visibility']['field'])) return $settingsJson;
            if (isset($validNames[$decoded['visibility']['field']])) return $settingsJson;
            unset($decoded['visibility']);
            return !empty($decoded) ? json_encode($decoded) : null;
        };
        foreach ($fields as &$f) {
            $f['settings'] = $stripOrphan($f['settings'] ?? null, $validTopLevel);

            if (!empty($f['sub_fields'])) {
                // Sub-field controllers are other sub-fields within the same repeater
                $validSub = [];
                foreach ($f['sub_fields'] as $sf) {
                    if (($sf['type'] ?? null) === 'select') {
                        $validSub[$sf['name']] = true;
                    }
                }
                foreach ($f['sub_fields'] as &$sf) {
                    $sf['settings'] = $stripOrphan($sf['settings'] ?? null, $validSub);
                }
                unset($sf);
            }
        }
        unset($f);

        return $fields;
    }

    /**
     * Handle template assignments
     */
    protected function handleTemplateAssignments(BlockRegistry $block, Request $request): void
    {
        $templatesInput = $request->input('templates', []);
        $selectedTemplates = is_array($templatesInput) ? $templatesInput : [];

        $defaultsInput = $request->input('template_defaults', []);
        $defaults = array_flip(array_filter(
            is_array($defaultsInput) ? $defaultsInput : [],
            'is_scalar'
        ));

        $block->templateAssignments()->delete();

        foreach ($selectedTemplates as $index => $templateName) {
            $this->templateService->assignBlockToTemplate(
                $block->id,
                $templateName,
                isset($defaults[$templateName]),
                $index
            );
        }
    }

    /**
     * Get current user ID
     */
    protected function getCurrentUserId(): ?int
    {
        // Try to get from session
        if (isset($_SESSION['content_manager']['user_id'])) {
            return (int) $_SESSION['content_manager']['user_id'];
        }
        return null;
    }

    /**
     * Guard: require XMLHttpRequest header, return error response if missing
     */
    private const VALID_ENTITY_TYPES = ['page', 'item', 'category', 'global'];

    private function validateEntityType(?string $entityType): ?\Illuminate\Http\JsonResponse
    {
        if (!$entityType || !in_array($entityType, self::VALID_ENTITY_TYPES, true)) {
            return response()->json(['error' => 'Invalid entity_type'], 400);
        }
        return null;
    }

    private function requireAjaxRequest(): ?\Illuminate\Http\JsonResponse
    {
        if (empty($_SERVER['HTTP_X_REQUESTED_WITH']) || $_SERVER['HTTP_X_REQUESTED_WITH'] !== 'XMLHttpRequest') {
            return response()->json(['error' => 'Invalid request'], 403);
        }
        return null;
    }

    /**
     * Check if block definition mutations are blocked (production environment)
     */
    protected function isDefinitionLocked(): bool
    {
        return app()->environment('production');
    }

    /**
     * Return a redirect with error if definitions are locked on production
     */
    protected function denyIfLocked()
    {
        if ($this->isDefinitionLocked()) {
            $this->flashError('Block definitions cannot be modified in production. Make changes on local/staging and sync via JSON.');
            return redirect()->route('admin.blocks.index');
        }
        return null;
    }

    /**
     * Auto-export a block definition to its JSON file after save.
     * Only runs on non-production environments.
     */
    protected function autoExportBlock(BlockRegistry $block, ?string $previousSlug = null): void
    {
        if ($this->isDefinitionLocked()) {
            return;
        }

        try {
            app(BlockSyncService::class)->exportBlock($block, $previousSlug);
        } catch (\Exception $e) {
            \Log::warning('BlockRegistry auto-export failed for block ' . $block->id . ': ' . $e->getMessage());
        }
    }

    /**
     * Resolve and validate the optional region parameter on entity-block
     * AJAX requests. Empty/missing returns null (default region). Validated
     * against the entity's template's declared regions; an invalid value
     * falls back to null so we never write a rogue region.
     */
    private function resolveRegionInput(Request $request, ?string $entityType, int $entityId): ?string
    {
        $region = $request->input('region');
        if ($region === null || $region === '') {
            return null;
        }
        $region = (string) $region;

        $templateName = \Mtc\Modules\BlockRegistry\Services\TemplateRegionRegistry::resolveTemplateName($entityType, $entityId);
        if (!\Mtc\Modules\BlockRegistry\Services\TemplateRegionRegistry::isValidRegion($templateName, $region)) {
            return null;
        }
        return $region;
    }

    /**
     * Flash a success message to session
     */
    protected function flashSuccess(string $message): void
    {
        Session::flash('success_message', ['type' => 'success', 'content' => $message]);
    }

    /**
     * Flash an error message to session
     */
    protected function flashError(string $message): void
    {
        Session::flash('form_errors', ['type' => 'error', 'content' => $message]);
    }

    /**
     * Flash a warning message to session
     */
    protected function flashWarning(string $message): void
    {
        Session::flash('success_message', ['type' => 'warning', 'content' => $message]);
    }

    /**
     * Build image settings JSON from form input values
     */
    private function buildImageSettingsJson(string $rawSizes, string $rawRatio, string $customRatio): string
    {
        $sizes = [400, 800, 1200, 1600];
        if (!empty($rawSizes)) {
            $sizes = array_map('intval', array_filter(array_map('trim', explode(',', $rawSizes))));
            sort($sizes);
        }

        if ($rawRatio === 'custom' && !empty($customRatio)) {
            $rawRatio = $customRatio;
        } elseif ($rawRatio === 'custom') {
            $rawRatio = '';
        }

        return json_encode([
            'image_settings' => [
                'crop_enabled' => true,
                'aspect_ratio' => $rawRatio,
                'srcset_sizes' => $sizes,
                'quality' => 85,
            ]
        ]);
    }

    /**
     * Render a full HTML preview page with frontend Vite assets
     */
    protected function renderPreviewHtml(string $title, string $bodyContent, string $extraStyles = ''): string
    {
        $twig = app('twig');
        $jsUrl = '';
        $cssUrls = [];

        $viteFunction = $twig->getFunction('vite');
        $viteCssFunction = $twig->getFunction('vite_css');

        if ($viteFunction) {
            $jsUrl = $viteFunction->getCallable()('js/app.js');
        }
        if ($viteCssFunction) {
            $cssUrls = $viteCssFunction->getCallable()('js/app.js');
        }

        $cssLinks = '';
        foreach ($cssUrls as $url) {
            $cssLinks .= '    <link href="' . htmlspecialchars($url) . '" rel="stylesheet">' . "\n";
        }

        return '<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>' . htmlspecialchars($title) . '</title>
' . $cssLinks . '    <style>
        body {
            margin: 0;
            padding: 0;
            background: #f8f9fa;
        }
        .preview-wrapper {
            background: white;
        }
        .preview-error {
            padding: 2rem;
            text-align: center;
            color: #dc3545;
            background: #f8d7da;
            border: 1px solid #f5c6cb;
            margin: 1rem;
            border-radius: 4px;
        }
' . $extraStyles . '    </style>
</head>
<body>
<div id="vue_environment">
    <div class="preview-wrapper">
        ' . $bodyContent . '
    </div>
</div>
    <script src="' . htmlspecialchars($jsUrl) . '" type="module"></script>
</body>
</html>';
    }

    // =========================================================================
    // Sync
    // =========================================================================

    /**
     * Sync dashboard page
     */
    public function syncIndex()
    {
        $syncService = app(BlockSyncService::class);
        $status = $syncService->getSyncStatus();
        $messages = $this->getFormMessages();

        $counts = [];
        foreach ($status as $item) {
            $s = $item['status'];
            $counts[$s] = ($counts[$s] ?? 0) + 1;
        }

        // Add icon from DB block for display
        $dbBlocks = BlockRegistry::pluck('icon', 'slug')->toArray();
        foreach ($status as &$item) {
            $item['icon'] = $dbBlocks[$item['slug']] ?? 'fa-cube';
        }
        unset($item);

        return template('admin/block_registry/sync.twig', [
            'title' => 'Block Sync',
            'blocks' => $status,
            'counts' => $counts,
            'messages' => $messages,
            'is_production' => app()->environment('production'),
        ]);
    }

    /**
     * Export a single block to JSON
     */
    public function syncExport(string $slug)
    {
        if (app()->environment('production')) {
            Session::flash('form_errors', ['type' => 'error', 'content' => 'Export is not allowed in production.']);
            return redirect()->route('admin.blocks.sync');
        }

        $syncService = app(BlockSyncService::class);
        $block = BlockRegistry::where('slug', $slug)->first();

        if (!$block) {
            Session::flash('form_errors', ['type' => 'error', 'content' => "Block not found: {$slug}"]);
            return redirect()->route('admin.blocks.sync');
        }

        try {
            $syncService->exportBlock($block);
            Session::flash('success_message', ['type' => 'success', 'content' => "Exported '{$block->name}' to JSON."]);
        } catch (\Exception $e) {
            Session::flash('form_errors', ['type' => 'error', 'content' => "Export failed: {$e->getMessage()}"]);
        }

        return redirect()->route('admin.blocks.sync');
    }

    /**
     * Export all blocks to JSON
     */
    public function syncExportAll()
    {
        if (app()->environment('production')) {
            Session::flash('form_errors', ['type' => 'error', 'content' => 'Export is not allowed in production.']);
            return redirect()->route('admin.blocks.sync');
        }

        $syncService = app(BlockSyncService::class);

        try {
            $result = $syncService->exportAllBlocks();
            $count = count($result['exported']);
            Session::flash('success_message', ['type' => 'success', 'content' => "Exported {$count} block(s) to JSON."]);

            if (!empty($result['errors'])) {
                $errorSlugs = array_column($result['errors'], 'slug');
                Session::flash('form_errors', ['type' => 'warning', 'content' => 'Errors exporting: ' . implode(', ', $errorSlugs)]);
            }
        } catch (\Exception $e) {
            Session::flash('form_errors', ['type' => 'error', 'content' => "Export failed: {$e->getMessage()}"]);
        }

        return redirect()->route('admin.blocks.sync');
    }

    /**
     * Import a single block from JSON
     */
    public function syncImport(string $slug)
    {
        $syncService = app(BlockSyncService::class);
        $filePath = $syncService->getBlocksDirectory() . '/' . $slug . '.json';

        if (!file_exists($filePath)) {
            Session::flash('form_errors', ['type' => 'error', 'content' => "JSON file not found for: {$slug}"]);
            return redirect()->route('admin.blocks.sync');
        }

        try {
            $data = $syncService->readBlockFile($filePath);
            $syncService->importBlock($data);
            Session::flash('success_message', ['type' => 'success', 'content' => "Imported '{$data['name']}' from JSON."]);
        } catch (\Exception $e) {
            Session::flash('form_errors', ['type' => 'error', 'content' => "Import failed: {$e->getMessage()}"]);
        }

        return redirect()->route('admin.blocks.sync');
    }

    /**
     * Import all blocks from JSON
     */
    public function syncImportAll()
    {
        $syncService = app(BlockSyncService::class);

        try {
            $result = $syncService->importAllBlocks();
            $total = count($result['created']) + count($result['updated']);
            Session::flash('success_message', ['type' => 'success', 'content' => "Imported {$total} block(s) from JSON ({$this->formatImportCounts($result)})."]);

            if (!empty($result['errors'])) {
                $errorSlugs = array_column($result['errors'], 'slug');
                Session::flash('form_errors', ['type' => 'warning', 'content' => 'Errors importing: ' . implode(', ', $errorSlugs)]);
            }
        } catch (\Exception $e) {
            Session::flash('form_errors', ['type' => 'error', 'content' => "Import failed: {$e->getMessage()}"]);
        }

        return redirect()->route('admin.blocks.sync');
    }

    /**
     * Get diff detail for a block (AJAX)
     */
    public function syncDiff(string $slug)
    {
        $syncService = app(BlockSyncService::class);
        $block = BlockRegistry::where('slug', $slug)->first();
        $filePath = $syncService->getBlocksDirectory() . '/' . $slug . '.json';

        if (!$block || !file_exists($filePath)) {
            return response()->json(['error' => 'Block or JSON file not found'], 404);
        }

        try {
            $jsonData = $syncService->readBlockFile($filePath);
            $diff = $syncService->diffBlock($block, $jsonData);
            return response()->json(['diff' => $diff]);
        } catch (\Exception $e) {
            return response()->json(['error' => $e->getMessage()], 500);
        }
    }

    /**
     * Format import result counts for display
     */
    protected function formatImportCounts(array $result): string
    {
        $parts = [];
        if (!empty($result['created'])) {
            $parts[] = count($result['created']) . ' created';
        }
        if (!empty($result['updated'])) {
            $parts[] = count($result['updated']) . ' updated';
        }
        return implode(', ', $parts) ?: 'no changes';
    }

    /**
     * Get form messages from session
     */
    protected function getFormMessages(): array
    {
        $messages = [];

        if ($success = Session::pull('success_message')) {
            $messages[] = $success;
        }

        if ($error = Session::pull('form_errors')) {
            $messages[] = $error;
        }

        return $messages;
    }

    /**
     * Get component templates grouped by category for the template dropdown
     */
    protected function getComponentTemplates(): array
    {
        try {
            $discovery = $this->componentScanner->discoverComponents();
            $components = $discovery['components'] ?? [];

            // Group by category (directory structure)
            $grouped = [];
            foreach ($components as $component) {
                $category = $component->category ?? 'Other';
                if (!isset($grouped[$category])) {
                    $grouped[$category] = [];
                }
                $grouped[$category][] = [
                    'path' => $component->path,
                    'name' => $component->name,
                    'description' => $component->description ?? '',
                ];
            }

            // Sort categories alphabetically
            ksort($grouped);

            return $grouped;
        } catch (\Exception $e) {
            \Log::error('Failed to load component templates: ' . $e->getMessage());
            return [];
        }
    }

    // ───────────────────────────────────────────────
    // Block Templates
    // ───────────────────────────────────────────────

    /**
     * List all block templates
     */
    public function blockTemplatesIndex()
    {
        $templates = $this->blockTemplateService->getAllTemplates();
        $messages = $this->getFormMessages();

        return template('admin/block_registry/block_templates/index.twig', [
            'title' => 'Block Templates',
            'block_templates' => $templates,
            'messages' => $messages,
        ]);
    }

    /**
     * Show create block template form
     */
    public function blockTemplateCreate()
    {
        $blocks = BlockRegistry::where('is_active', true)
            ->with('category')
            ->orderBy('name')
            ->get();

        $categories = BlockRegistryCategory::orderBy('order_index')->get();
        $templates = $this->templateService->getAllAvailableTemplates();
        $globalBlocks = $this->entityBlockService->getGlobalBlocks();

        return template('admin/block_registry/block_templates/edit.twig', [
            'title' => 'Create Block Template',
            'block_template' => null,
            'blocks' => $blocks,
            'global_blocks' => $globalBlocks,
            'categories' => $categories,
            'templates' => $templates,
        ]);
    }

    /**
     * Store a new block template
     */
    public function blockTemplateStore(Request $request)
    {
        $name = trim($request->input('name', ''));
        if (empty($name)) {
            Session::flash('form_errors', 'Template name is required.');
            return redirect()->route('admin.blocks.templates.create');
        }

        try {
            $this->blockTemplateService->createTemplate([
                'name' => $name,
                'description' => $request->input('description'),
                'items' => $this->normaliseTemplateItems($request->input('items', [])),
                'templates' => $request->input('templates', []),
            ]);

            Session::flash('success_message', 'Block template created successfully.');
            return redirect()->route('admin.blocks.templates.index');
        } catch (\Exception $e) {
            Session::flash('form_errors', 'Failed to create template: ' . $e->getMessage());
            return redirect()->route('admin.blocks.templates.create');
        }
    }

    /**
     * Show edit block template form
     */
    public function blockTemplateEdit(int $id)
    {
        $blockTemplate = $this->blockTemplateService->getTemplate($id);
        if (!$blockTemplate) {
            Session::flash('form_errors', 'Block template not found.');
            return redirect()->route('admin.blocks.templates.index');
        }

        $blocks = BlockRegistry::where('is_active', true)
            ->with('category')
            ->leftJoin('block_registry_categories', 'block_registry.category_id', '=', 'block_registry_categories.id')
            ->orderBy('block_registry_categories.order_index')
            ->orderBy('block_registry_categories.name')
            ->orderBy('block_registry.name')
            ->select('block_registry.*')
            ->get();

        $categories = BlockRegistryCategory::orderBy('order_index')->get();
        $templates = $this->templateService->getAllAvailableTemplates();
        $globalBlocks = $this->entityBlockService->getGlobalBlocks();

        return template('admin/block_registry/block_templates/edit.twig', [
            'title' => 'Edit Block Template',
            'block_template' => $blockTemplate,
            'blocks' => $blocks,
            'global_blocks' => $globalBlocks,
            'categories' => $categories,
            'templates' => $templates,
        ]);
    }

    /**
     * Update a block template
     */
    public function blockTemplateUpdate(Request $request, int $id)
    {
        $name = trim($request->input('name', ''));
        if (empty($name)) {
            Session::flash('form_errors', 'Template name is required.');
            return redirect()->route('admin.blocks.templates.edit', $id);
        }

        try {
            $this->blockTemplateService->updateTemplate($id, [
                'name' => $name,
                'description' => $request->input('description'),
                'items' => $this->normaliseTemplateItems($request->input('items', [])),
                'templates' => $request->input('templates', []),
            ]);

            Session::flash('success_message', 'Block template updated successfully.');
            return redirect()->route('admin.blocks.templates.index');
        } catch (\Exception $e) {
            Session::flash('form_errors', 'Failed to update template: ' . $e->getMessage());
            return redirect()->route('admin.blocks.templates.edit', $id);
        }
    }

    /**
     * Normalise the submitted items[] payload into the typed
     * ['type' => 'block'|'global', 'id' => int] shape the service expects.
     * Silently drops malformed rows; the form-builder JS owns producing this
     * input so any malformed entries indicate tampering, not user error.
     */
    protected function normaliseTemplateItems($raw): array
    {
        if (!is_array($raw)) {
            return [];
        }

        $items = [];
        foreach ($raw as $row) {
            if (!is_array($row)) {
                continue;
            }
            $type = $row['type'] ?? null;
            $id = isset($row['id']) ? (int) $row['id'] : 0;
            if (!in_array($type, ['block', 'global'], true) || $id <= 0) {
                continue;
            }
            $items[] = ['type' => $type, 'id' => $id];
        }
        return $items;
    }

    /**
     * Delete a block template
     */
    public function blockTemplateDestroy(int $id)
    {
        try {
            $this->blockTemplateService->deleteTemplate($id);
            Session::flash('success_message', 'Block template deleted successfully.');
        } catch (\Exception $e) {
            Session::flash('form_errors', 'Failed to delete template: ' . $e->getMessage());
        }

        return redirect()->route('admin.blocks.templates.index');
    }

    /**
     * AJAX: Get block templates available for an entity
     */
    public function ajaxGetAvailableTemplates(Request $request)
    {
        $entity_type = $request->input('entity_type');
        $entity_id = (int) $request->input('entity_id');

        if (!$entity_type || !$entity_id) {
            return response()->json(['error' => 'entity_type and entity_id are required'], 400);
        }
        if ($error = $this->validateEntityType($entity_type)) {
            return $error;
        }

        try {
            $templateName = $this->resolveEntityTemplateName($entity_type, $entity_id);

            $blockTemplates = collect();
            if ($templateName) {
                $blockTemplates = $this->blockTemplateService->getTemplatesForTemplateName($templateName);
            }

            return response()->json([
                'success' => true,
                'block_templates' => $blockTemplates->map(function ($bt) {
                    return [
                        'id' => $bt->id,
                        'name' => $bt->name,
                        'description' => $bt->description,
                        'block_count' => $bt->items_count,
                        'blocks' => $bt->items->map(function ($item) {
                            if ($item->source_block_id && $item->sourceBlock) {
                                $source = $item->sourceBlock;
                                return [
                                    'name' => $source->name ?: ($source->block?->name ?? 'Global Block'),
                                    'icon' => $source->block?->icon ?? 'fa-globe',
                                    'is_global' => true,
                                ];
                            }
                            return [
                                'name' => $item->block?->name ?? 'Unknown',
                                'icon' => $item->block?->icon ?? 'fa-cube',
                                'is_global' => false,
                            ];
                        })->values()->toArray(),
                    ];
                })->values()->toArray(),
            ]);
        } catch (\Exception $e) {
            return response()->json([
                'error' => 'Failed to load templates',
                'message' => $e->getMessage(),
            ], 500);
        }
    }

    /**
     * AJAX: Apply a block template to an entity
     */
    public function ajaxApplyTemplate(Request $request)
    {
        if ($error = $this->requireAjaxRequest()) {
            return $error;
        }

        $entity_type = $request->input('entity_type');
        $entity_id = (int) $request->input('entity_id');
        $blockTemplateId = (int) $request->input('block_template_id');

        if (!$entity_type || !$entity_id || !$blockTemplateId) {
            return response()->json(['error' => 'entity_type, entity_id, and block_template_id are required'], 400);
        }
        if ($error = $this->validateEntityType($entity_type)) {
            return $error;
        }

        $region = $this->resolveRegionInput($request, $entity_type, $entity_id);

        try {
            $count = $this->blockTemplateService->applyTemplateToEntity(
                $blockTemplateId,
                $entity_type,
                $entity_id,
                $region
            );

            return response()->json([
                'success' => true,
                'blocks_added' => $count,
            ]);
        } catch (\Exception $e) {
            return response()->json([
                'error' => 'Failed to apply template',
                'message' => $e->getMessage(),
            ], 500);
        }
    }

    /**
     * Resolve the page template name for an entity type/id
     */
    protected function resolveEntityTemplateName(string $entityType, int $entityId): ?string
    {
        $templateMap = [
            'item' => 'shop_item',
            'category' => 'shop_category',
        ];
        $templateName = $templateMap[$entityType] ?? null;

        if ($entityType === 'page' && $templateName === null) {
            try {
                $page = \ContentManagerPanel::loadPage($entityId, false);
                $templateName = $page->template ?? null;
            } catch (\Exception $e) {
                // Fall through
            }
        }

        return $templateName;
    }

    /**
     * AJAX: Return icon manifest JSON for the icon picker.
     * Parses FA5 _variables.less for icon names and icons.less for custom site icons.
     * Cached until either source file changes.
     */
    public function ajaxIconManifest()
    {
        $faVariablesPath = SITE_PATH . '/sites/default/css/vendors/font_awesome/_variables.less';
        $faBrandsPath = SITE_PATH . '/sites/default/css/vendors/font_awesome/_shims.less';
        $customIconsPath = SITE_PATH . '/sites/default/css/base/icons.less';

        $cacheKey = 'block_registry_icon_manifest_'
            . filemtime($faVariablesPath) . '_'
            . filemtime($customIconsPath);

        $manifest = Cache::remember($cacheKey, 86400, function () use ($faVariablesPath, $faBrandsPath, $customIconsPath) {
            // Parse FA5 icon names and unicode from _variables.less
            $faContent = file_get_contents($faVariablesPath);
            preg_match_all('/@fa-var-([a-z0-9-]+):\s*"\\\\([a-f0-9]+)";/', $faContent, $matches);
            $icons = [];
            for ($i = 0; $i < count($matches[1]); $i++) {
                $icons[] = ['n' => $matches[1][$i], 'u' => $matches[2][$i]];
            }

            // Parse brand icons from _shims.less (icons that map to Font Awesome 5 Brands)
            $shimsContent = file_get_contents($faBrandsPath);
            preg_match_all('/\.@\{fa-css-prefix\}\.@\{fa-css-prefix\}-([a-z0-9-]+)\s*\{\s*font-family:\s*\'Font Awesome 5 Brands\'/', $shimsContent, $brandMatches);
            $brands = $brandMatches[1] ?? [];

            // Parse custom site icons from icons.less, handling nested variants
            $customContent = file_get_contents($customIconsPath);
            $custom = [];
            $seen = [];
            $currentParent = null;
            $braceDepth = 0;

            foreach (explode("\n", $customContent) as $line) {
                $trimmed = trim($line);

                // Track brace depth to know when we exit a parent block
                $braceDepth += substr_count($trimmed, '{') - substr_count($trimmed, '}');
                if ($braceDepth <= 1) {
                    $currentParent = null;
                }

                // Match top-level icon: &--iconName {
                if (preg_match('/^&--([a-zA-Z][a-zA-Z0-9]*)\s*\{/', $trimmed, $m)) {
                    $currentParent = $m[1];
                }

                // Match nested variant: &-direction {
                if ($currentParent !== null && preg_match('/^&-([a-zA-Z]+)\s*\{/', $trimmed, $m)) {
                    $name = $currentParent . '-' . $m[1];
                    if (!isset($seen[$name])) {
                        $label = ucfirst($currentParent) . ' ' . ucfirst($m[1]);
                        $custom[] = ['n' => $name, 'l' => $label];
                        $seen[$name] = true;
                        $seen[$currentParent] = 'has_children';
                    }
                    continue;
                }
            }

            // Now add top-level icons that don't have children
            $currentParent = null;
            $braceDepth = 0;
            foreach (explode("\n", $customContent) as $line) {
                $trimmed = trim($line);
                $braceDepth += substr_count($trimmed, '{') - substr_count($trimmed, '}');
                if ($braceDepth <= 1) {
                    $currentParent = null;
                }
                if (preg_match('/^&--([a-zA-Z][a-zA-Z0-9]*)\s*\{/', $trimmed, $m)) {
                    $name = $m[1];
                    if (!isset($seen[$name])) {
                        $label = preg_replace('/([A-Z])/', ' $1', $name);
                        $label = ucfirst(str_replace('-', ' ', trim($label)));
                        $custom[] = ['n' => $name, 'l' => $label];
                        $seen[$name] = true;
                    }
                    $currentParent = $name;
                }
            }

            return ['icons' => $icons, 'brands' => $brands, 'custom' => $custom];
        });

        return response()->json($manifest);
    }
}
