<?php

namespace Mtc\Plugins\BlockManager\Services;

use Mtc\Modules\BlockRegistry\Services\BlockSyncService;
use Mtc\Plugins\BlockManager\Models\BlockManagerBlock;
use Mtc\Plugins\BlockManager\Support\InstanceClass;

/**
 * Materialises a composed BlockManagerBlock draft into a BlockRegistry block:
 *   1. Generates a Twig template at sites/default/templates/includes/page_blocks/{slug}.twig
 *   2. Writes a BlockRegistry-shaped JSON at modules/BlockRegistry/blocks/{slug}.json
 *   3. Imports the JSON via BlockSyncService::importBlock() (the same code path
 *      that `php artisan blocks:import` uses at deploy time)
 *   4. Updates audit fields on the source draft
 *
 * Idempotent by slug — re-shipping rewrites the files and updates the existing
 * BlockRegistry row rather than creating a duplicate.
 *
 * Ships land in includes/page_blocks/ alongside the project's existing
 * hand-authored page-block partials (cta_banner, hero_banner, …) so the
 * directory layout stays consistent for reviewers and designers.
 */
class BlockShipService
{
    public function __construct(
        private TwigGenerator $generator,
        private BlockSyncService $sync,
        private ContainerCssCompiler $css,
        private BlockStyleAggregator $styleAggregator,
    ) {
    }

    /**
     * @return array{block_registry_id: int, slug: string, twig_path: string, json_path: string}
     */
    public function ship(BlockManagerBlock $block): array
    {
        $slug = $block->slug;

        $twigDir = base_path('sites/default/templates/includes/page_blocks');
        $twigPath = "{$twigDir}/{$slug}.twig";
        $jsonDir = $this->sync->getBlocksDirectory();
        $jsonPath = "{$jsonDir}/{$slug}.json";

        if (!is_dir($twigDir)) {
            @mkdir($twigDir, 0775, true);
        }
        if (!is_dir($jsonDir)) {
            @mkdir($jsonDir, 0775, true);
        }

        file_put_contents($twigPath, $this->generator->generate($block));

        // Per-instance container styling (token-preserved LESS) → its own file,
        // wired into app.css via the blocks aggregator. The live site needs a
        // frontend rebuild (`npm run build`) after shipping for this to apply.
        $this->writeBlockStyles($block, $slug);

        $json = [
            'name'                 => $block->name,
            'slug'                 => $block->slug,
            'description'          => $block->description,
            'icon'                 => $block->icon ?? 'fa-cube',
            'template'             => "page_blocks/{$block->slug}.twig",
            'is_system'            => false,
            'is_active'            => true,
            'order_index'          => 0,
            'category'             => null,
            'fields'               => $this->generator->deriveFields($block),
            'template_assignments' => [],
        ];
        file_put_contents(
            $jsonPath,
            json_encode($json, JSON_PRETTY_PRINT | JSON_UNESCAPED_SLASHES) . "\n"
        );

        $registered = $this->sync->importBlock($json);

        $block->last_shipped_at = now();
        $block->shipped_block_registry_id = $registered->id;
        $block->save();

        return [
            'block_registry_id' => (int) $registered->id,
            'slug'              => $block->slug,
            'twig_path'         => $twigPath,
            'json_path'         => $jsonPath,
        ];
    }

    /**
     * Generate token-preserved LESS for every container instance in the block
     * and write it to sites/default/css/block_manager_blocks/{slug}.less, then
     * rebuild the blocks aggregator. Written even when empty (idempotent
     * re-ship: a previously-styled block that loses its styling clears cleanly).
     */
    private function writeBlockStyles(BlockManagerBlock $block, string $slug): void
    {
        $stylesDir = base_path('sites/default/css/block_manager_blocks');
        $lessPath = "{$stylesDir}/{$slug}.less";

        $structure = is_array($block->structure_json) ? $block->structure_json : [];
        $elements = is_array($structure['elements'] ?? null) ? $structure['elements'] : [];

        $parts = [];
        $this->collectStyles($elements, $parts);

        if ($parts === []) {
            // No container styling: don't litter a header-only stylesheet, and
            // clear any stale one from a previous ship that did have styles.
            @unlink($lessPath);
            $this->styleAggregator->rebuild();
            return;
        }

        if (!is_dir($stylesDir)) {
            @mkdir($stylesDir, 0775, true);
        }
        $header = "/* AUTO-GENERATED by BlockShipService for block '{$slug}'. */\n";
        file_put_contents($lessPath, $header . implode("\n", $parts) . "\n");

        $this->styleAggregator->rebuild();
    }

    /**
     * @param array<int, mixed>  $elements
     * @param array<int, string> $parts  appended to by reference
     */
    private function collectStyles(array $elements, array &$parts): void
    {
        foreach ($elements as $instance) {
            if (!is_array($instance)) {
                continue;
            }
            $styleProps = is_array($instance['style_props'] ?? null) ? $instance['style_props'] : [];
            $children = is_array($instance['children'] ?? null) ? $instance['children'] : [];

            // Only containers carry children + style_props; gate on style_props
            // being present to avoid emitting empty rules.
            if ($styleProps !== []) {
                $selector = '.' . InstanceClass::for((string) ($instance['instance_id'] ?? ''));
                $compiled = $this->css->compile($styleProps, $selector, false);
                if ($compiled['css'] !== '') {
                    $parts[] = $compiled['css'];
                }
            }

            if ($children !== []) {
                $this->collectStyles($children, $parts);
            }
        }
    }
}
