package p.studio.projects;

import java.io.IOException;
import java.io.UncheckedIOException;
import java.nio.charset.StandardCharsets;
import java.nio.file.Files;
import java.nio.file.Path;
import java.util.*;

public final class KnownProjectsService {
    private final Path storageFile;
    private final ProjectCatalogService projectCatalogService;

    public KnownProjectsService(Path storageFile, ProjectCatalogService projectCatalogService) {
        this.storageFile = Objects.requireNonNull(storageFile, "storageFile").toAbsolutePath().normalize();
        this.projectCatalogService = Objects.requireNonNull(projectCatalogService, "projectCatalogService");
    }

    public List<ProjectReference> listKnownProjects() {
        final boolean storageExists = Files.isRegularFile(storageFile);
        final Set<Path> knownPaths = readStoredPaths();
        if (!storageExists && knownPaths.isEmpty()) {
            final List<ProjectReference> discovered = projectCatalogService.listProjects();
            writeStoredPaths(discovered.stream().map(ProjectReference::rootPath).toList());
            return discovered;
        }

        final List<ProjectReference> knownProjects = new ArrayList<>();
        boolean mutated = false;
        for (Path knownPath : knownPaths) {
            if (!Files.isDirectory(knownPath)) {
                mutated = true;
                continue;
            }
            knownProjects.add(projectCatalogService.openProject(knownPath));
        }

        knownProjects.sort(Comparator.comparing(ProjectReference::name, String.CASE_INSENSITIVE_ORDER));
        if (mutated) {
            writeStoredPaths(knownProjects.stream().map(ProjectReference::rootPath).toList());
        }
        return List.copyOf(knownProjects);
    }

    public void remember(ProjectReference projectReference) {
        final Set<Path> knownPaths = readStoredPaths();
        knownPaths.add(normalize(projectReference.rootPath()));
        writeStoredPaths(knownPaths);
    }

    public void forget(ProjectReference projectReference) {
        final Set<Path> knownPaths = readStoredPaths();
        knownPaths.remove(normalize(projectReference.rootPath()));
        writeStoredPaths(knownPaths);
    }

    private Set<Path> readStoredPaths() {
        if (!Files.isRegularFile(storageFile)) {
            return new LinkedHashSet<>();
        }

        try {
            final LinkedHashSet<Path> paths = new LinkedHashSet<>();
            for (String line : Files.readAllLines(storageFile, StandardCharsets.UTF_8)) {
                final String trimmed = line.trim();
                if (trimmed.isBlank()) {
                    continue;
                }
                paths.add(normalize(Path.of(trimmed)));
            }
            return paths;
        } catch (IOException ioException) {
            throw new UncheckedIOException(ioException);
        }
    }

    private void writeStoredPaths(Iterable<Path> paths) {
        try {
            Files.createDirectories(storageFile.getParent());
            final List<String> lines = new ArrayList<>();
            for (Path path : paths) {
                lines.add(normalize(path).toString());
            }
            Files.write(storageFile, lines, StandardCharsets.UTF_8);
        } catch (IOException ioException) {
            throw new UncheckedIOException(ioException);
        }
    }

    private Path normalize(Path path) {
        return Objects.requireNonNull(path, "path").toAbsolutePath().normalize();
    }
}
