package p.studio.projects;

import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.io.TempDir;

import java.nio.file.Files;
import java.nio.file.Path;

import static org.junit.jupiter.api.Assertions.*;

final class ProjectCatalogServiceTest {
    @TempDir
    Path tempDir;

    @Test
    void listsExistingProjectDirectories() throws Exception {
        Files.createDirectories(tempDir.resolve("alpha"));
        Files.createDirectories(tempDir.resolve("beta"));
        Files.createDirectories(tempDir.resolve("scratch"));
        Files.writeString(tempDir.resolve("alpha").resolve("prometeu.json"), manifest("Alpha Project"));
        Files.writeString(tempDir.resolve("beta").resolve("prometeu.json"), manifest("Beta Project"));
        Files.createDirectories(tempDir.resolve("alpha").resolve("src"));

        final ProjectCatalogService service = new ProjectCatalogService(tempDir);

        assertEquals(
                java.util.List.of("Alpha Project", "Beta Project"),
                service.listProjects().stream().map(ProjectReference::name).toList());
    }

    @Test
    void createsProjectWithExpectedStructure() {
        final ProjectCatalogService service = new ProjectCatalogService(tempDir);

        final ProjectReference project = service.createProject("My New Project");

        assertEquals("My New Project", project.name());
        assertEquals("1.0.0", project.version());
        assertEquals("pbs", project.languageId());
        assertEquals(1, project.stdlibVersion());
        assertTrue(Files.isDirectory(project.rootPath()));
        assertTrue(Files.isRegularFile(project.rootPath().resolve("prometeu.json")));
        assertTrue(Files.isRegularFile(project.rootPath().resolve(".gitignore")));
        assertTrue(Files.isDirectory(project.rootPath().resolve(".workspace")));
        assertTrue(Files.isDirectory(project.rootPath().resolve(".studio")));
        assertTrue(Files.isRegularFile(project.rootPath().resolve(".studio").resolve("setup.json")));
        assertTrue(Files.isDirectory(project.rootPath().resolve("src")));
        assertTrue(Files.isDirectory(project.rootPath().resolve("build")));
        assertTrue(Files.isDirectory(project.rootPath().resolve("cartridge")));
        final String gitIgnore = assertDoesNotThrow(() -> Files.readString(project.rootPath().resolve(".gitignore")));
        assertTrue(gitIgnore.contains(".studio/"));
        assertTrue(gitIgnore.contains(".workspace/"));
        assertTrue(gitIgnore.contains("build/"));
        assertTrue(gitIgnore.contains(".DS_Store"));
        assertTrue(gitIgnore.contains("Thumbs.db"));
        assertTrue(gitIgnore.contains(".directory"));
    }

    @Test
    void createsProjectInsideExplicitParentLocation() {
        final ProjectCatalogService service = new ProjectCatalogService(tempDir.resolve("ignored-default-root"));
        final Path customParent = tempDir.resolve("custom-projects");

        final ProjectReference project = service.createProject("Wizard Project", customParent);

        assertEquals("Wizard Project", project.name());
        assertEquals("1.0.0", project.version());
        assertEquals("pbs", project.languageId());
        assertEquals(1, project.stdlibVersion());
        assertEquals(customParent.resolve("wizard-project"), project.rootPath());
        assertTrue(Files.isDirectory(project.rootPath()));
        assertTrue(Files.isRegularFile(project.rootPath().resolve("prometeu.json")));
        assertTrue(Files.isDirectory(project.rootPath().resolve(".workspace")));
        assertTrue(Files.isDirectory(project.rootPath().resolve(".studio")));
        assertTrue(Files.isDirectory(project.rootPath().resolve("src")));
    }

    @Test
    void createsProjectUsingRequestedLanguageStdlibAndSourceRoot() throws Exception {
        final ProjectCatalogService service = new ProjectCatalogService(tempDir);

        final ProjectReference project = service.createProject(new ProjectCreationRequest(
                "Wizard Layout Project",
                tempDir,
                "pbs",
                7,
                "code",
                8,
                "/opt/prometeu/runtime"));

        assertEquals("Wizard Layout Project", project.name());
        assertEquals("1.0.0", project.version());
        assertEquals("pbs", project.languageId());
        assertEquals(7, project.stdlibVersion());
        assertTrue(Files.isDirectory(project.rootPath().resolve("code")));
        assertTrue(Files.notExists(project.rootPath().resolve("src")));
        final String manifestJson = Files.readString(project.rootPath().resolve("prometeu.json"));
        final String setupJson = Files.readString(project.rootPath().resolve(".studio").resolve("setup.json"));
        assertTrue(manifestJson.contains("\"language\": \"pbs\""));
        assertTrue(manifestJson.contains("\"stdlib\": \"7\""));
        assertTrue(setupJson.contains("\"prometeuRuntimePath\" : \"/opt/prometeu/runtime\""));
        assertTrue(setupJson.contains("\"mode\" : \"spaces\""));
        assertTrue(setupJson.contains("\"width\" : 8"));
    }

    @Test
    void openProjectRejectsDirectoryWithoutPrometeuManifest() throws Exception {
        final Path invalidProject = tempDir.resolve("invalid-project");
        Files.createDirectories(invalidProject);
        final ProjectCatalogService service = new ProjectCatalogService(tempDir);

        assertThrows(IllegalArgumentException.class, () -> service.openProject(invalidProject));
    }

    @Test
    void openProjectUsesManifestNameInsteadOfDirectoryName() throws Exception {
        final Path projectRoot = tempDir.resolve("folder-name");
        Files.createDirectories(projectRoot);
        Files.writeString(projectRoot.resolve("prometeu.json"), manifest("Display Name"));
        final ProjectCatalogService service = new ProjectCatalogService(tempDir);

        final ProjectReference project = service.openProject(projectRoot);

        assertEquals("Display Name", project.name());
        assertEquals("1.0.0", project.version());
        assertEquals("pbs", project.languageId());
        assertEquals(1, project.stdlibVersion());
        assertEquals(projectRoot, project.rootPath());
    }

    private String manifest(String name) {
        return """
                {
                  "name": "%s",
                  "version": "1.0.0",
                  "language": "pbs",
                  "stdlib": "1",
                  "dependencies": []
                }
                """.formatted(name);
    }
}
