<?php

namespace Mtc\Plugins\BlockManager\Tests\Unit;

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

class VariantPublishServiceBakRetentionTest extends TestCase
{
    private VariantPublishService $service;

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

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

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

    protected function setUp(): void
    {
        parent::setUp();
        $this->service = app(VariantPublishService::class);
    }

    protected function tearDown(): void
    {
        foreach ($this->writtenFiles as $path) {
            @unlink($path);
        }
        foreach ($this->manifestBackups as $path => $content) {
            file_put_contents($path, $content);
        }
        app(\Mtc\Plugins\BlockManager\Services\VariantStyleAggregator::class)->rebuild();
        if (!empty($this->createdUuids)) {
            BlockManagerVariantDraft::whereIn('uuid', $this->createdUuids)->delete();
        }
        parent::tearDown();
    }

    public function test_first_publish_does_not_create_bak_files(): void
    {
        $slug = 'phase16-bak-first-' . Str::random(6);
        $draft = $this->seedDraft('button', $slug, $this->propsWithColor('@primary_color'));
        $this->backupManifest('button');

        $this->service->publish($draft);

        $twigPath = $this->trackElementPaths('button', $slug);
        $lessPath = base_path("plugins/BlockManager/elements/button/styles/{$slug}.less");

        $this->assertFileDoesNotExist(
            "{$twigPath}.bak",
            "First publish should NOT create a .twig.bak — there was no prior file to retain."
        );
        $this->assertFileDoesNotExist(
            "{$lessPath}.bak",
            "First publish should NOT create a .less.bak — there was no prior file to retain."
        );
    }

    public function test_republish_renames_prior_twig_to_twig_bak(): void
    {
        $slug = 'phase16-bak-twig-' . Str::random(6);
        $draft = $this->seedDraft('button', $slug, $this->propsWithColor('@primary_color'));
        $this->backupManifest('button');

        $this->service->publish($draft);

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

        $firstRunTwig = file_get_contents($twigPath);

        // Mutate the properties so LESS visibly differs between publishes;
        // Twig is a pure function of (element_type, variant_slug) so it stays
        // identical — but the .bak file must still exist after the second publish.
        $draft->properties_json = $this->propsWithColor('@secondary_color');
        $draft->save();

        $this->service->publish($draft);

        $this->assertFileExists(
            "{$twigPath}.bak",
            "Re-publish must rename the prior .twig to .twig.bak."
        );
        $this->assertSame(
            $firstRunTwig,
            file_get_contents("{$twigPath}.bak"),
            "The .twig.bak must contain the previous publish's Twig content."
        );
    }

    public function test_republish_renames_prior_less_to_less_bak(): void
    {
        $slug = 'phase16-bak-less-' . Str::random(6);
        $draft = $this->seedDraft('button', $slug, $this->propsWithColor('@primary_color'));
        $this->backupManifest('button');

        $this->service->publish($draft);

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

        $firstRunLess = file_get_contents($lessPath);

        $draft->properties_json = $this->propsWithColor('@secondary_color');
        $draft->save();

        $this->service->publish($draft);

        $this->assertFileExists(
            "{$lessPath}.bak",
            "Re-publish must rename the prior .less to .less.bak."
        );
        $this->assertSame(
            $firstRunLess,
            file_get_contents("{$lessPath}.bak"),
            "The .less.bak must contain the previous publish's LESS content (including the @designer-meta header)."
        );

        // Sanity: the LESS body actually differs between the two publishes,
        // proving the .bak holds the EARLIER content, not the freshly-written one.
        $this->assertNotSame(
            file_get_contents($lessPath),
            file_get_contents("{$lessPath}.bak"),
            "Re-publish must replace the live .less with new content; .bak holds the prior."
        );
    }

    public function test_third_publish_overwrites_existing_bak_one_cycle_retention(): void
    {
        $slug = 'phase16-bak-cycle-' . Str::random(6);
        $draft = $this->seedDraft('button', $slug, $this->propsWithColor('@primary_color'));
        $this->backupManifest('button');

        // Run 1
        $this->service->publish($draft);

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

        $firstRunLess = file_get_contents($lessPath);

        // Run 2 — first run becomes .bak
        $draft->properties_json = $this->propsWithColor('@secondary_color');
        $draft->save();
        $this->service->publish($draft);
        $secondRunLess = file_get_contents($lessPath);

        // Run 3 — second run should become .bak, displacing first run
        $draft->properties_json = $this->propsWithColor('@tertiary_color');
        $draft->save();
        $this->service->publish($draft);

        $this->assertSame(
            $secondRunLess,
            file_get_contents("{$lessPath}.bak"),
            ".bak retention is one cycle — the third publish's .bak must hold the SECOND run's content, not the first."
        );
        $this->assertNotSame(
            $firstRunLess,
            file_get_contents("{$lessPath}.bak"),
            ".bak retention must roll forward — the first run's content must NOT survive into the third publish's .bak."
        );
    }

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

        return BlockManagerVariantDraft::create([
            'uuid'            => $uuid,
            'element_type'    => $elementType,
            'variant_slug'    => $variantSlug,
            'label'           => 'Phase 16 .bak Probe',
            'properties_json' => $properties,
        ]);
    }

    private function propsWithColor(string $token): array
    {
        return ['base' => ['default' => ['desktop' => [
            'typography' => ['color' => ['token' => $token]],
        ]]]];
    }

    private function trackElementPaths(string $elementType, string $slug): string
    {
        $twigPath = base_path("plugins/BlockManager/elements/{$elementType}/variants/{$slug}.twig");
        $lessPath = base_path("plugins/BlockManager/elements/{$elementType}/styles/{$slug}.less");
        $this->writtenFiles[] = $twigPath;
        $this->writtenFiles[] = $lessPath;
        return $twigPath;
    }

    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);
        }
    }
}
