<?php

namespace App\Tests\Twig;

use App\Twig\ViteAssetExtension;
use PHPUnit\Framework\TestCase;
use Psr\Cache\CacheItemInterface;
use Psr\Cache\CacheItemPoolInterface;

class ViteAssetExtensionTest extends TestCase
{
    private CacheItemPoolInterface $cache;

    protected function setUp(): void
    {
        $this->cache = $this->createMock(CacheItemPoolInterface::class);
        $_ENV['VITE_LOAD'] = '1';
    }

    private function createExtension(string $manifestPath = '/tmp/nonexistent'): ViteAssetExtension
    {
        return new ViteAssetExtension($manifestPath, $this->cache);
    }

    private function mockCacheItem(bool $isHit, mixed $value = null): CacheItemInterface
    {
        $item = $this->createMock(CacheItemInterface::class);
        $item->method('isHit')->willReturn($isHit);
        $item->method('get')->willReturn($value);
        $item->method('set')->willReturnSelf();
        $item->method('expiresAfter')->willReturnSelf();

        return $item;
    }

    public function testGetFunctionsReturnsExpectedNames(): void
    {
        $extension = $this->createExtension();
        $functions = $extension->getFunctions();

        $names = array_map(fn ($f) => $f->getName(), $functions);

        self::assertContains('vite_asset', $names);
        self::assertContains('isMobile', $names);
        self::assertContains('vite_favicons', $names);
    }

    public function testAssetDevReturnsScriptTags(): void
    {
        $_ENV['VITE_LOAD'] = '0';
        $extension = $this->createExtension();

        $html = $extension->asset('app.js', []);

        self::assertStringContainsString('localhost:5173', $html);
        self::assertStringContainsString('app.js', $html);
    }

    public function testAssetProdUsesManifest(): void
    {
        $manifest = [
            'app.js' => [
                'file' => 'assets/app.abc123.js',
                'css' => ['assets/app.def456.css'],
            ],
        ];

        $item = $this->mockCacheItem(true, $manifest);
        $this->cache->method('getItem')->willReturn($item);

        $extension = $this->createExtension();
        $html = $extension->assetProd('app.js');

        self::assertStringContainsString('assets/app.abc123.js', $html);
        self::assertStringContainsString('assets/app.def456.css', $html);
    }

    public function testAssetProdHandlesMissingManifest(): void
    {
        $item = $this->mockCacheItem(false);
        $this->cache->method('getItem')->willReturn($item);

        $extension = $this->createExtension('/tmp/nonexistent_manifest.json');
        $html = $extension->assetProd('missing.js');

        self::assertStringContainsString('script', $html);
    }

    public function testAssetProdLoadsManifestFromFile(): void
    {
        $manifest = ['app.js' => ['file' => 'assets/app.xyz.js', 'css' => []]];
        $tmpFile = tempnam(sys_get_temp_dir(), 'manifest');
        file_put_contents($tmpFile, json_encode($manifest));

        $item = $this->mockCacheItem(false);
        $this->cache->method('getItem')->willReturn($item);
        $this->cache->method('save')->willReturn(true);

        $extension = $this->createExtension($tmpFile);
        $html = $extension->assetProd('app.js');

        self::assertStringContainsString('assets/app.xyz.js', $html);
        unlink($tmpFile);
    }

    public function testFaviconsDevReturnsFaviconLink(): void
    {
        $_ENV['VITE_LOAD'] = '0';
        $extension = $this->createExtension();

        $html = $extension->favicons();

        self::assertStringContainsString('favicon.ico', $html);
    }

    public function testFaviconsProdReturnsIconLinks(): void
    {
        $manifest = [
            'favicon.png' => ['file' => 'assets/favicon.abc.png'],
            'app.js' => ['file' => 'assets/app.js'],
        ];

        $item = $this->mockCacheItem(true, $manifest);
        $this->cache->method('getItem')->willReturn($item);

        $extension = $this->createExtension();
        $html = $extension->favicons();

        self::assertStringContainsString('assets/favicon.abc.png', $html);
        self::assertStringNotContainsString('app.js', $html);
    }

    public function testIsMobileReturnsBool(): void
    {
        $extension = $this->createExtension();

        self::assertIsBool($extension->isMobile());
    }

    public function testGetNonceReturnsEmptyWithoutCspListener(): void
    {
        $extension = $this->createExtension();
        $html = $extension->assetDev('app.js', []);

        self::assertStringContainsString('nonce=""', $html);
    }
}
