<?php

namespace AydinHassan\MagentoCoreComposerInstallerTest;

use AydinHassan\MagentoCoreComposerInstaller\GitIgnore;
use org\bovigo\vfs\vfsStream;
use PHPUnit\Framework\TestCase;

class GitIgnoreTest extends TestCase
{
    private string $gitIgnoreFile;

    public function setUp(): void
    {
        vfsStream::setup('root');
        $this->gitIgnoreFile = vfsStream::url('root/.gitignore');
    }

    public function testIfFileNotExistsItIsCreated(): void
    {
        $gitIgnore = new GitIgnore($this->gitIgnoreFile, []);
        $gitIgnore->addEntry("file1");
        unset($gitIgnore);

        $this->assertFileExists($this->gitIgnoreFile);
    }

    public function testIfGitIgnoreDisabledNoFileIsWritten(): void
    {
        $gitIgnore = new GitIgnore($this->gitIgnoreFile, [], true, false);
        $gitIgnore->addEntry("file1");
        unset($gitIgnore);

        $this->assertFileDoesNotExist($this->gitIgnoreFile);
    }

    public function testIfFileExistsExistingLinesAreLoaded(): void
    {
        $lines = ['line1', 'line2'];
        file_put_contents($this->gitIgnoreFile, implode("\n", $lines));

        $gitIgnore = new GitIgnore($this->gitIgnoreFile, [], true);

        $this->assertFileExists($this->gitIgnoreFile);
        $this->assertSame($lines, $gitIgnore->getEntries());
    }

    public function testWhitespaceAndCommentsArePreservedWhenLoadingAndWritingLines(): void
    {
        $contents = <<<STRING
# ------------------- #
# Autogenerated below #
# ------------------- #

# ..
STRING;

        file_put_contents($this->gitIgnoreFile, $contents);

        $gitIgnore = new GitIgnore($this->gitIgnoreFile, [], true);

        $expected = <<<STRING
# ------------------- #
# Autogenerated below #
# ------------------- #

# ..
/foo
STRING;

        $gitIgnore->addEntry('/foo');
        unset($gitIgnore);
        $this->assertSame($expected, file_get_contents($this->gitIgnoreFile));
    }

    public function testWipeOutRemovesAllEntries(): void
    {
        $lines = ['line1', 'line2'];
        file_put_contents($this->gitIgnoreFile, implode("\n", $lines));

        $gitIgnore = new GitIgnore($this->gitIgnoreFile, [], true);
        $gitIgnore->wipeOut();

        $this->assertFileExists($this->gitIgnoreFile);
        $this->assertSame([], $gitIgnore->getEntries());
        unset($gitIgnore);
        $this->assertEquals("", file_get_contents($this->gitIgnoreFile));
    }

    public function testIgnoreDirectoriesAreAddedToGitIgnore(): void
    {
        $folders = ['folder1', 'folder2'];
        $gitIgnore = new GitIgnore($this->gitIgnoreFile, $folders, true);
        $gitIgnore->addEntry('folder1/file1.txt');
        $this->assertSame($folders, $gitIgnore->getEntries());
        unset($gitIgnore);
        $this->assertEquals("folder1\nfolder2", file_get_contents($this->gitIgnoreFile));
    }

    public function testAddEntryDoesNotAddDuplicates(): void
    {
        $gitIgnore = new GitIgnore($this->gitIgnoreFile, [], true);
        $gitIgnore->addEntry("file1.txt");
        $gitIgnore->addEntry("file1.txt");
        $this->assertCount(1, $gitIgnore->getEntries());
    }

    public function testAddEntryDoesNotAddFileOrDirectoryIfItIsInsideAnIgnoredDirectory(): void
    {
        $ignoreDirs = ["dir1", "dir2/lol/"];
        $gitIgnore = new GitIgnore($this->gitIgnoreFile, $ignoreDirs);
        $gitIgnore->addEntry("dir1/file1.txt");
        $gitIgnore->addEntry("dir2/lol/file2.txt");
        $gitIgnore->addEntry("dir2/file3.txt");

        $expected = [
            'dir1',
            'dir2/lol/',
            'dir2/file3.txt',
        ];

        $this->assertEquals($expected, $gitIgnore->getEntries());
    }

    public function testIgnoreDirectoriesAreNotWrittenIfNoEntriesAreAdded(): void
    {
        $folders = [];
        $gitIgnore = new GitIgnore($this->gitIgnoreFile, $folders, true);
        $this->assertSame($folders, $gitIgnore->getEntries());
        unset($gitIgnore);
        $this->assertFileDoesNotExist($this->gitIgnoreFile);
    }

    public function testIgnoreDirectoriesAreWrittenIfDirectoriesToIgnoreEntirelyDifferFromTheIgnoreFile(): void
    {
        $folders = ['folder1', 'folder2'];
        $gitIgnore = new GitIgnore($this->gitIgnoreFile, $folders, true);
        $this->assertSame($folders, $gitIgnore->getEntries());
        unset($gitIgnore);
        $this->assertFileExists($this->gitIgnoreFile);
    }

    public function testGitIgnoreIsNotWrittenIfNoAdditions(): void
    {
        $lines = ['line1', 'line2'];
        file_put_contents($this->gitIgnoreFile, implode("\n", $lines));
        $writeTime = filemtime($this->gitIgnoreFile);

        $folders = ['folder1', 'folder2'];
        $gitIgnore = new GitIgnore($this->gitIgnoreFile, $folders, true);
        unset($gitIgnore);

        clearstatcache();
        $this->assertEquals($writeTime, filemtime($this->gitIgnoreFile));
    }

    public function testCanRemoveEntry(): void
    {
        $lines = ['line1', 'line2'];
        file_put_contents($this->gitIgnoreFile, implode("\n", $lines));

        $gitIgnore = new GitIgnore($this->gitIgnoreFile, [], true);
        $gitIgnore->removeEntry('line1');

        $this->assertEquals(['line2'], $gitIgnore->getEntries());
    }

    public function testRemoveIgnoreDirectoriesSuccessfullyRemovesEntries(): void
    {
        $gitIgnore = new GitIgnore($this->gitIgnoreFile, ['line1', 'line2']);
        $this->assertEquals(['line1', 'line2'], $gitIgnore->getEntries());
        $gitIgnore->removeIgnoreDirectories();
        $this->assertEquals([], $gitIgnore->getEntries());
    }
}
