<?php

namespace Mtc\Plugins\BlockManager\Tests\Feature;

use Illuminate\Support\Str;
use Mtc\Plugins\BlockManager\Models\BlockManagerVariantDraft;
use Tests\TestCase;

class PublishApiTest extends TestCase
{
    /** @var string[] */
    private array $createdUuids = [];

    /** @var string[] */
    private array $writtenFiles = [];

    /** @var array<string, string> path => original content */
    private array $manifestBackups = [];

    protected function tearDown(): void
    {
        foreach ($this->writtenFiles as $path) {
            @unlink($path);
        }
        foreach ($this->manifestBackups as $path => $content) {
            file_put_contents($path, $content);
        }
        // Publish regenerates the committed style aggregator. Now that the test
        // variant files are deleted, regenerate it from clean disk state so no
        // dangling @import (to a removed test variant) is left behind.
        app(\Mtc\Plugins\BlockManager\Services\VariantStyleAggregator::class)->rebuild();
        if (!empty($this->createdUuids)) {
            BlockManagerVariantDraft::whereIn('uuid', $this->createdUuids)->delete();
        }
        parent::tearDown();
    }

    public function test_publish_returns_404_for_unknown_id(): void
    {
        $this->postJson('/api/block-manager/variants/99999999/publish')->assertStatus(404);
    }

    public function test_publish_returns_200_with_audit_paths_envelope(): void
    {
        $draft = $this->seedDraft('button', 'phase11-pub-' . Str::random(6));
        $this->backupManifest('button');

        $response = $this->postJson("/api/block-manager/variants/{$draft->id}/publish");
        $response->assertStatus(200);

        $data = $response->json('data');
        foreach (['twig_path', 'less_path', 'manifest_path', 'last_published_at'] as $key) {
            $this->assertArrayHasKey($key, $data, "Publish response missing key '{$key}'.");
        }

        $this->writtenFiles[] = $data['twig_path'];
        $this->writtenFiles[] = $data['less_path'];
    }

    public function test_publish_writes_twig_file_to_disk(): void
    {
        $slug = 'phase11-pub-' . Str::random(6);
        $draft = $this->seedDraft('button', $slug);
        $this->backupManifest('button');

        $this->postJson("/api/block-manager/variants/{$draft->id}/publish")->assertStatus(200);

        $twigPath = base_path("plugins/BlockManager/elements/button/variants/{$slug}.twig");
        $this->writtenFiles[] = $twigPath;
        $this->writtenFiles[] = base_path("plugins/BlockManager/elements/button/styles/{$slug}.less");

        $this->assertFileExists($twigPath, "Twig file should be written at {$twigPath}.");
        $contents = file_get_contents($twigPath);
        $this->assertStringContainsString(
            "bm-button--{$slug}",
            $contents,
            "Published Twig must include the variant-scoped class."
        );
    }

    public function test_publish_writes_less_file_with_designer_meta_header(): void
    {
        $slug = 'phase11-pub-' . Str::random(6);
        $draft = $this->seedDraft('button', $slug);
        $this->backupManifest('button');

        $this->postJson("/api/block-manager/variants/{$draft->id}/publish")->assertStatus(200);

        $lessPath = base_path("plugins/BlockManager/elements/button/styles/{$slug}.less");
        $this->writtenFiles[] = $lessPath;
        $this->writtenFiles[] = base_path("plugins/BlockManager/elements/button/variants/{$slug}.twig");

        $this->assertFileExists($lessPath, "LESS file should be written at {$lessPath}.");
        $contents = file_get_contents($lessPath);
        $this->assertMatchesRegularExpression(
            '#^/\*\s*@designer-meta#',
            $contents,
            "Published LESS must begin with a /* @designer-meta header."
        );
    }

    public function test_publish_appends_variant_to_element_manifest(): void
    {
        $slug = 'phase11-pub-' . Str::random(6);
        $draft = $this->seedDraft('button', $slug);
        $this->backupManifest('button');

        $this->postJson("/api/block-manager/variants/{$draft->id}/publish")->assertStatus(200);

        $this->writtenFiles[] = base_path("plugins/BlockManager/elements/button/variants/{$slug}.twig");
        $this->writtenFiles[] = base_path("plugins/BlockManager/elements/button/styles/{$slug}.less");

        $manifest = json_decode(
            file_get_contents(base_path('plugins/BlockManager/elements/button/element.json')),
            true
        );

        $found = false;
        foreach ($manifest['variants'] ?? [] as $entry) {
            if (($entry['id'] ?? null) === $slug) {
                $found = true;
                break;
            }
        }
        $this->assertTrue($found, "Manifest variants[] must contain an entry with id={$slug}.");
    }

    public function test_publish_wires_variant_into_style_aggregator(): void
    {
        $slug = 'phase11-pub-' . Str::random(6);
        $draft = $this->seedDraft('button', $slug);
        $this->backupManifest('button');

        $this->postJson("/api/block-manager/variants/{$draft->id}/publish")->assertStatus(200);

        $this->writtenFiles[] = base_path("plugins/BlockManager/elements/button/variants/{$slug}.twig");
        $this->writtenFiles[] = base_path("plugins/BlockManager/elements/button/styles/{$slug}.less");

        $aggregator = file_get_contents(base_path('sites/default/css/block_manager_variants.less'));
        $this->assertStringContainsString(
            "plugins/BlockManager/elements/button/styles/{$slug}.less",
            $aggregator,
            'Publishing a variant must regenerate the style aggregator with an @import for its stylesheet.'
        );
    }

    public function test_publish_sets_audit_fields_on_draft(): void
    {
        $slug = 'phase11-pub-' . Str::random(6);
        $draft = $this->seedDraft('button', $slug);
        $this->backupManifest('button');

        $response = $this->postJson("/api/block-manager/variants/{$draft->id}/publish");
        $response->assertStatus(200);

        $this->writtenFiles[] = base_path("plugins/BlockManager/elements/button/variants/{$slug}.twig");
        $this->writtenFiles[] = base_path("plugins/BlockManager/elements/button/styles/{$slug}.less");

        $fresh = BlockManagerVariantDraft::find($draft->id);
        $this->assertNotNull($fresh->last_published_at, "last_published_at must be set after publish.");
        $this->assertNotEmpty($fresh->published_twig_path);
        $this->assertNotEmpty($fresh->published_less_path);

        $this->assertSame(
            $response->json('data.twig_path'),
            $fresh->published_twig_path,
            "Persisted published_twig_path must match the publish response."
        );
        $this->assertSame(
            $response->json('data.less_path'),
            $fresh->published_less_path
        );
    }

    public function test_publish_is_idempotent_on_re_publish(): void
    {
        $slug = 'phase11-pub-' . Str::random(6);
        $draft = $this->seedDraft('button', $slug);
        $this->backupManifest('button');

        $this->postJson("/api/block-manager/variants/{$draft->id}/publish")->assertStatus(200);
        $this->postJson("/api/block-manager/variants/{$draft->id}/publish")->assertStatus(200);

        $this->writtenFiles[] = base_path("plugins/BlockManager/elements/button/variants/{$slug}.twig");
        $this->writtenFiles[] = base_path("plugins/BlockManager/elements/button/styles/{$slug}.less");

        $manifest = json_decode(
            file_get_contents(base_path('plugins/BlockManager/elements/button/element.json')),
            true
        );

        $matches = 0;
        foreach ($manifest['variants'] ?? [] as $entry) {
            if (($entry['id'] ?? null) === $slug) {
                $matches++;
            }
        }
        $this->assertSame(1, $matches, "Re-publish must not duplicate the manifest entry.");
    }

    private function seedDraft(string $elementType, string $variantSlug, ?array $properties = null): BlockManagerVariantDraft
    {
        $uuid = (string) Str::uuid();
        $this->createdUuids[] = $uuid;

        return BlockManagerVariantDraft::create([
            'uuid'            => $uuid,
            'element_type'    => $elementType,
            'variant_slug'    => $variantSlug,
            'label'           => 'Phase 11 Publish Probe',
            'properties_json' => $properties ?? [
                'base' => ['default' => ['desktop' => [
                    'typography' => ['color' => ['token' => '@primary_color']],
                    'box'        => ['padding_x' => ['raw' => '24px']],
                ]]],
            ],
        ]);
    }

    private function backupManifest(string $elementType): void
    {
        $path = base_path("plugins/BlockManager/elements/{$elementType}/element.json");
        if (!isset($this->manifestBackups[$path]) && is_file($path)) {
            $this->manifestBackups[$path] = file_get_contents($path);
        }
    }
}
