<?php

namespace Mtc\Modules\BlockRegistry\Services;

use Mtc\Modules\BlockRegistry\Models\BlockTemplate;
use Mtc\Modules\BlockRegistry\Models\BlockTemplateItem;
use Mtc\Modules\BlockRegistry\Models\BlockTemplateAssignment;
use Mtc\Modules\BlockRegistry\Models\EntityBlock;
use Illuminate\Support\Collection;
use Illuminate\Support\Facades\DB;

class BlockTemplateService
{
    /**
     * Get all block templates with item counts
     */
    public function getAllTemplates(): Collection
    {
        return BlockTemplate::withCount('items')
            ->with('templateAssignments')
            ->orderBy('name')
            ->get();
    }

    /**
     * Get a single template with items and their block definitions
     */
    public function getTemplate(int $id): ?BlockTemplate
    {
        return BlockTemplate::with([
                'items' => function ($q) { $q->orderBy('order_index'); },
                'items.block.category',
                'items.sourceBlock.block',
                'templateAssignments',
            ])
            ->find($id);
    }

    /**
     * Create a block template with items and assignments
     */
    public function createTemplate(array $data): BlockTemplate
    {
        return DB::transaction(function () use ($data) {
            $template = BlockTemplate::create([
                'name' => $data['name'],
                'description' => $data['description'] ?? null,
            ]);

            $this->syncItems($template, $data['items'] ?? []);
            $this->syncAssignments($template, $data['templates'] ?? []);

            return $template;
        });
    }

    /**
     * Update a block template with items and assignments
     */
    public function updateTemplate(int $id, array $data): BlockTemplate
    {
        return DB::transaction(function () use ($id, $data) {
            $template = BlockTemplate::findOrFail($id);
            $template->update([
                'name' => $data['name'],
                'description' => $data['description'] ?? null,
            ]);

            $this->syncItems($template, $data['items'] ?? []);
            $this->syncAssignments($template, $data['templates'] ?? []);

            return $template->fresh(['items.block', 'items.sourceBlock.block', 'templateAssignments']);
        });
    }

    /**
     * Delete a block template
     */
    public function deleteTemplate(int $id): bool
    {
        $template = BlockTemplate::findOrFail($id);
        return $template->delete();
    }

    /**
     * Get block templates assigned to a page template name
     */
    public function getTemplatesForTemplateName(string $templateName): Collection
    {
        $templateIds = BlockTemplateAssignment::where('template_name', $templateName)
            ->pluck('block_template_id');

        return BlockTemplate::whereIn('id', $templateIds)
            ->with([
                'items' => function ($q) { $q->orderBy('order_index'); },
                'items.block.category',
                'items.sourceBlock.block',
            ])
            ->withCount('items')
            ->orderBy('name')
            ->get();
    }

    /**
     * Check if any block templates exist for a page template name
     */
    public function hasTemplatesForTemplateName(string $templateName): bool
    {
        return BlockTemplateAssignment::where('template_name', $templateName)->exists();
    }

    /**
     * Apply a block template to an entity, creating EntityBlock instances.
     * Items with source_block_id become global-block references (published
     * immediately — the global is the source of truth, matching the behaviour
     * of EntityBlockService::addGlobalBlockReference); plain items become
     * fresh EntityBlocks with default field data in draft status, waiting
     * for the admin to fill them in.
     */
    public function applyTemplateToEntity(int $blockTemplateId, string $entityType, int $entityId, ?string $region = null): int
    {
        $template = BlockTemplate::with(['items' => function ($q) {
            $q->orderBy('order_index');
        }])->findOrFail($blockTemplateId);
        $count = 0;

        DB::transaction(function () use ($template, $entityType, $entityId, $region, &$count) {
            foreach ($template->items as $item) {
                $isGlobalRef = (bool) $item->source_block_id;

                $attributes = [
                    'entity_type' => $entityType,
                    'entity_id' => $entityId,
                    'block_id' => $item->block_id,
                    'region' => $region,
                    'status' => $isGlobalRef
                        ? EntityBlock::STATUS_PUBLISHED
                        : EntityBlock::STATUS_DRAFT,
                ];

                if ($isGlobalRef) {
                    $attributes['source_block_id'] = $item->source_block_id;
                }

                EntityBlock::create($attributes);
                $count++;
            }
        });

        return $count;
    }

    /**
     * Sync template items (replace all with new ordered list).
     *
     * @param array $items Ordered list of items, each shaped as
     *                     ['type' => 'block'|'global', 'id' => int].
     *                     For 'block' the id is a BlockRegistry id; for
     *                     'global' the id is an EntityBlock id (global source).
     */
    protected function syncItems(BlockTemplate $template, array $items): void
    {
        $template->items()->delete();

        $seen = [];

        foreach ($items as $index => $item) {
            $type = $item['type'] ?? 'block';
            $id = (int) ($item['id'] ?? 0);
            if ($id <= 0) {
                continue;
            }

            $key = $type . ':' . $id;
            if (isset($seen[$key])) {
                continue;
            }
            $seen[$key] = true;

            if ($type === 'global') {
                $global = EntityBlock::where('entity_type', 'global')
                    ->whereNull('source_block_id')
                    ->find($id);
                if (!$global) {
                    continue;
                }
                BlockTemplateItem::create([
                    'block_template_id' => $template->id,
                    'block_id' => $global->block_id,
                    'source_block_id' => $global->id,
                    'order_index' => $index,
                ]);
            } else {
                BlockTemplateItem::create([
                    'block_template_id' => $template->id,
                    'block_id' => $id,
                    'source_block_id' => null,
                    'order_index' => $index,
                ]);
            }
        }
    }

    /**
     * Sync template assignments (replace all with new list)
     */
    protected function syncAssignments(BlockTemplate $template, array $templateNames): void
    {
        $template->templateAssignments()->delete();

        foreach ($templateNames as $templateName) {
            BlockTemplateAssignment::create([
                'block_template_id' => $template->id,
                'template_name' => $templateName,
            ]);
        }
    }
}
