<?php

declare(strict_types=1);

use Builder\Core\Foundation\Definition\JsModule;
use Builder\Core\Foundation\Registry;

function uniqueTarget(): string
{
    return sys_get_temp_dir().'/builder-target-'.uniqid('', true);
}

it('creates a symlink when the target does not exist', function (): void {
    $source = sys_get_temp_dir().'/builder-source-'.uniqid('', true);
    mkdir($source, 0755, true);
    $target = uniqueTarget();

    $this->artisan('builder:link', ['--source' => $source, '--target' => $target])
        ->assertSuccessful();

    expect(is_link($target))->toBeTrue()
        ->and(readlink($target))->toBe($source);

    unlink($target);
    rmdir($source);
});

it('replaces an existing symlink with a new one', function (): void {
    $oldSource = sys_get_temp_dir().'/builder-old-'.uniqid('', true);
    mkdir($oldSource, 0755, true);
    $newSource = sys_get_temp_dir().'/builder-new-'.uniqid('', true);
    mkdir($newSource, 0755, true);
    $target = uniqueTarget();

    symlink($oldSource, $target);

    $this->artisan('builder:link', ['--source' => $newSource, '--target' => $target])
        ->assertSuccessful();

    expect(is_link($target))->toBeTrue()
        ->and(readlink($target))->toBe($newSource);

    unlink($target);
    rmdir($oldSource);
    rmdir($newSource);
});

it('fails when the target is an existing directory', function (): void {
    $source = sys_get_temp_dir().'/builder-source-'.uniqid('', true);
    mkdir($source, 0755, true);
    $target = uniqueTarget();
    mkdir($target, 0755, true);

    $this->artisan('builder:link', ['--source' => $source, '--target' => $target])
        ->assertFailed();

    rmdir($target);
    rmdir($source);
});

it('fails when the source directory does not exist', function (): void {
    $target = uniqueTarget();

    $this->artisan('builder:link', ['--source' => '/nonexistent/'.uniqid('', true), '--target' => $target])
        ->assertFailed();
});

it('auto-detects the source from the package location when no option is provided', function (): void {
    // packages/core/public is three levels up from packages/core/src/Console/Commands/
    $defaultSource = __DIR__.'/../../public'; // packages/core/tests/Console/../../public = packages/core/public

    if (! is_dir($defaultSource)) {
        mkdir($defaultSource, 0755, true);
        $createdDefaultSource = true;
    }

    $target = uniqueTarget();

    $this->artisan('builder:link', ['--target' => $target])
        ->assertSuccessful();

    expect(is_link($target))->toBeTrue();

    unlink($target);

    if (isset($createdDefaultSource)) {
        rmdir($defaultSource);
    }
});

it('also links registered modules that have a publicPath', function (): void {
    $source = sys_get_temp_dir().'/builder-module-'.uniqid('', true);
    mkdir($source, 0755, true);

    $coreSource = sys_get_temp_dir().'/builder-core-'.uniqid('', true);
    mkdir($coreSource, 0755, true);

    $moduleTarget = uniqueTarget();

    // Register a module with a publicPath via the Registry
    $registry = app(Registry::class);
    $registry->register(
        JsModule::make()
            ->setKey('test-mod')
            ->setPublicPath($source),
    );

    $this->artisan('builder:link', ['--source' => $coreSource, '--target' => uniqueTarget()])
        ->assertSuccessful();

    // The module target is public/vendor/builder/test-mod — check it was linked
    $expectedTarget = public_path('vendor/builder/test-mod');
    expect(is_link($expectedTarget))->toBeTrue()
        ->and(readlink($expectedTarget))->toBe($source);

    if (is_link($expectedTarget)) {
        unlink($expectedTarget);
    }

    rmdir($source);
    rmdir($coreSource);
});

it('skips modules that have no publicPath set', function (): void {
    $coreSource = sys_get_temp_dir().'/builder-core-'.uniqid('', true);
    mkdir($coreSource, 0755, true);

    $registry = app(Registry::class);
    // Register a module WITHOUT calling setPublicPath — getPublicPath() returns null
    $registry->register(
        JsModule::make()->setKey('null-pub-path'),
    );

    $this->artisan('builder:link', ['--source' => $coreSource, '--target' => uniqueTarget()])
        ->assertSuccessful();

    rmdir($coreSource);
});

it('creates parent directory for target symlink when parent does not exist', function (): void {
    $source = sys_get_temp_dir().'/builder-source-'.uniqid('', true);
    mkdir($source, 0755, true);

    // Use a target inside a non-existent parent directory
    $parentDir = sys_get_temp_dir().'/builder-newparent-'.uniqid('', true);
    $target = $parentDir.'/core';

    $this->artisan('builder:link', ['--source' => $source, '--target' => $target])
        ->assertSuccessful();

    expect(is_link($target))->toBeTrue();

    unlink($target);
    rmdir($parentDir);
    rmdir($source);
});

it('warns and skips modules whose publicPath does not exist', function (): void {
    $coreSource = sys_get_temp_dir().'/builder-core-'.uniqid('', true);
    mkdir($coreSource, 0755, true);

    $registry = app(Registry::class);
    $registry->register(
        JsModule::make()
            ->setKey('missing-mod')
            ->setPublicPath('/nonexistent/path/'.uniqid('', true)),
    );

    $this->artisan('builder:link', ['--source' => $coreSource, '--target' => uniqueTarget()])
        ->assertSuccessful();

    rmdir($coreSource);
});
