<?php

namespace Mtc\Plugins\BlockManager\Tests\Unit;

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

class VariantConflictDetectorTest extends TestCase
{
    private VariantConflictDetector $detector;

    private VariantPublishService $publisher;

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

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

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

    protected function setUp(): void
    {
        parent::setUp();
        $this->detector = app(VariantConflictDetector::class);
        $this->publisher = 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_no_conflict_when_no_file_on_disk(): void
    {
        $slug = 'phase16-conflict-nofile-' . Str::random(6);
        $draft = $this->seedDraft('button', $slug);

        // No publish — there is no on-disk LESS to diverge from.
        $result = $this->detector->detect($draft);

        $this->assertIsArray($result);
        $this->assertArrayHasKey('has_conflict', $result);
        $this->assertArrayHasKey('reason', $result);
        $this->assertFalse(
            $result['has_conflict'],
            "When no published file exists, detector must report no conflict."
        );
        $this->assertNull(
            $result['reason'],
            "When no conflict is reported, reason must be null."
        );
    }

    public function test_no_conflict_when_disk_matches_generator_output(): void
    {
        $slug = 'phase16-conflict-match-' . Str::random(6);
        $draft = $this->seedDraft('button', $slug);
        $this->backupManifest('button');

        $this->publisher->publish($draft);
        $this->trackElementPaths('button', $slug);

        // Fresh publish — disk content equals generator output by construction.
        $result = $this->detector->detect($draft);

        $this->assertFalse(
            $result['has_conflict'],
            "Immediately after publish, disk content matches the generator — no conflict expected."
        );
    }

    public function test_conflict_when_disk_body_diverges_from_designer_meta(): void
    {
        $slug = 'phase16-conflict-divergent-' . Str::random(6);
        $draft = $this->seedDraft('button', $slug);
        $this->backupManifest('button');

        $this->publisher->publish($draft);
        $lessPath = $this->trackElementPaths('button', $slug);

        // Simulate a hand-edit: append a stray rule inside the variant block.
        $original = file_get_contents($lessPath);
        $tampered = preg_replace(
            '/\}\s*$/',
            "  outline: 2px dashed magenta;\n}\n",
            $original,
            1
        );
        file_put_contents($lessPath, $tampered);

        $result = $this->detector->detect($draft);

        $this->assertTrue(
            $result['has_conflict'],
            "When the on-disk LESS body diverges from what the generator would emit, detector must report a conflict."
        );
        $this->assertIsString(
            $result['reason'],
            "When has_conflict=true, reason must be a non-empty string describing the mismatch."
        );
        $this->assertNotEmpty($result['reason']);
    }

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

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

    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;
        $this->writtenFiles[] = "{$twigPath}.bak";
        $this->writtenFiles[] = "{$lessPath}.bak";
        return $lessPath;
    }

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