<?php

namespace MintyPHP\Tests\App\Module;

use MintyPHP\App\Module\ModuleManifest;
use MintyPHP\App\Module\ModuleRuntimeAssetPublisher;
use PHPUnit\Framework\TestCase;

final class ModuleRuntimeAssetPublisherTest extends TestCase
{
    private string $tmpDir;

    protected function setUp(): void
    {
        $this->tmpDir = sys_get_temp_dir() . '/corecore-module-assets-test-' . uniqid();
        mkdir($this->tmpDir, 0777, true);
    }

    protected function tearDown(): void
    {
        $this->removePath($this->tmpDir);
    }

    public function testPublishSymlinkCreatesManagedModuleLinksAndRemovesStaleEntries(): void
    {
        if (!function_exists('symlink')) {
            self::markTestSkipped('Symlink support is required for this test.');
        }

        $moduleA = $this->createModuleWithWebAssets('module-a', ['js/a.js' => 'console.log("a");']);
        $moduleB = $this->createModuleWithoutWebAssets('module-b');
        $runtimeModulesDir = $this->tmpDir . '/web/modules';
        mkdir($runtimeModulesDir, 0777, true);
        file_put_contents($runtimeModulesDir . '/stale.txt', 'old');

        $publisher = new ModuleRuntimeAssetPublisher();
        $result = $publisher->publish($runtimeModulesDir, [
            'module-a' => $moduleA,
            'module-b' => $moduleB,
        ], 'symlink');

        self::assertTrue(is_link($runtimeModulesDir . '/module-a'));
        self::assertSame(
            realpath($this->tmpDir . '/modules/module-a/web'),
            realpath($runtimeModulesDir . '/module-a')
        );
        self::assertFileDoesNotExist($runtimeModulesDir . '/module-b');
        self::assertFileDoesNotExist($runtimeModulesDir . '/stale.txt');
        self::assertSame(1, $result['created']);
        self::assertSame(1, $result['removed']);
    }

    public function testPublishSymlinkUpdatesWrongExistingLink(): void
    {
        if (!function_exists('symlink')) {
            self::markTestSkipped('Symlink support is required for this test.');
        }

        $moduleA = $this->createModuleWithWebAssets('module-a', ['css/a.css' => '.a{}']);
        $runtimeModulesDir = $this->tmpDir . '/web/modules';
        mkdir($runtimeModulesDir, 0777, true);

        $wrongTarget = $this->tmpDir . '/wrong-target';
        mkdir($wrongTarget, 0777, true);
        symlink($wrongTarget, $runtimeModulesDir . '/module-a');

        $publisher = new ModuleRuntimeAssetPublisher();
        $result = $publisher->publish($runtimeModulesDir, ['module-a' => $moduleA], 'symlink');

        self::assertTrue(is_link($runtimeModulesDir . '/module-a'));
        self::assertSame(
            realpath($this->tmpDir . '/modules/module-a/web'),
            realpath($runtimeModulesDir . '/module-a')
        );
        self::assertSame(1, $result['updated']);
    }

    public function testPublishCopyModeCopiesModuleAssets(): void
    {
        $moduleA = $this->createModuleWithWebAssets('module-a', [
            'js/app.js' => 'console.log("copy-mode");',
            'css/app.css' => '.copy-mode{}',
        ]);
        $runtimeModulesDir = $this->tmpDir . '/web/modules';

        $publisher = new ModuleRuntimeAssetPublisher();
        $result = $publisher->publish($runtimeModulesDir, ['module-a' => $moduleA], 'copy');

        self::assertDirectoryExists($runtimeModulesDir . '/module-a');
        self::assertFalse(is_link($runtimeModulesDir . '/module-a'));
        self::assertFileExists($runtimeModulesDir . '/module-a/js/app.js');
        self::assertFileExists($runtimeModulesDir . '/module-a/css/app.css');
        self::assertSame(1, $result['created']);
    }

    private function createModuleWithWebAssets(string $id, array $files): ModuleManifest
    {
        $moduleDir = $this->tmpDir . '/modules/' . $id;
        mkdir($moduleDir . '/web', 0777, true);
        foreach ($files as $relativePath => $content) {
            $target = $moduleDir . '/web/' . ltrim($relativePath, '/');
            $targetDir = dirname($target);
            if (!is_dir($targetDir)) {
                mkdir($targetDir, 0777, true);
            }
            file_put_contents($target, $content);
        }

        return ModuleManifest::fromArray(['id' => $id], $moduleDir);
    }

    private function createModuleWithoutWebAssets(string $id): ModuleManifest
    {
        $moduleDir = $this->tmpDir . '/modules/' . $id;
        mkdir($moduleDir, 0777, true);
        return ModuleManifest::fromArray(['id' => $id], $moduleDir);
    }

    private function removePath(string $path): void
    {
        if (!file_exists($path) && !is_link($path)) {
            return;
        }
        if (is_link($path) || is_file($path)) {
            @unlink($path);
            return;
        }
        if (!is_dir($path)) {
            return;
        }
        $entries = scandir($path);
        if ($entries !== false) {
            foreach ($entries as $entry) {
                if ($entry === '.' || $entry === '..') {
                    continue;
                }
                $this->removePath($path . '/' . $entry);
            }
        }
        @rmdir($path);
    }
}
