package com.atlassian.bitbucket.mesh.repository;

import com.atlassian.bitbucket.mesh.io.MoreFiles;

import javax.annotation.Nonnull;
import java.nio.file.Path;
import java.util.Objects;

import static com.atlassian.bitbucket.mesh.git.GitConstants.PATH_APPLICATION_INFO;
import static java.util.Objects.requireNonNull;

/**
 * Represents the {@code shared-objects} repository that backs all {@link ManagedRepository managed repositories} in
 * a repository hierarchy.
 */
class DefaultSharedObjectsRepository extends AbstractRepository implements SharedObjectsRepository {

    static final String SHARED_OBJECTS = "shared-objects";

    private final String hierarchyId;
    private final String id;
    private final String partition;
    private final Path path;
    private final ManagedShard shard;

    DefaultSharedObjectsRepository(@Nonnull ManagedShard shard, @Nonnull String partition,
                                   @Nonnull String hierarchyId) {
        this.hierarchyId = requireNonNull(hierarchyId, "hierarchyId");
        this.partition = requireNonNull(partition, "partition");
        this.shard = requireNonNull(shard, "shard");

        id = ManagedRepository.formatId(partition, hierarchyId, SHARED_OBJECTS);
        path = shard.getRepositoriesDir(partition, hierarchyId).resolve(SHARED_OBJECTS);
    }

    @Override
    public <T> T accept(@Nonnull RepositoryVisitor<T> visitor) {
        return requireNonNull(visitor, "visitor").visit(this);
    }

    @Override
    public boolean equals(Object o) {
        if (this == o) {
            return true;
        }
        if (!(o instanceof DefaultSharedObjectsRepository)) {
            return false;
        }

        DefaultSharedObjectsRepository that = (DefaultSharedObjectsRepository) o;
        return hierarchyId.equals(that.hierarchyId) &&
                partition.equals(that.partition);
    }

    @Nonnull
    @Override
    public Path getDirtyMarker(@Nonnull ManagedRepository member) {
        return MoreFiles.resolve(path, PATH_APPLICATION_INFO, "dirty", requireNonNull(member, "member").getLocalId());
    }

    @Nonnull
    @Override
    public String getHierarchyId() {
        return hierarchyId;
    }

    @Nonnull
    @Override
    public String getId() {
        return id;
    }

    @Nonnull
    @Override
    public Path getLfsDir() {
        return shard.getLfsDir(partition, hierarchyId);
    }

    @Override
    public ManagedRepository getMember(@Nonnull String localId) {
        DefaultManagedRepository repository = shard.getRepository(partition, hierarchyId, localId);
        if (repository != null && attributeManager != null) {
            repository.attachAttributes(attributeManager);
        }

        return repository;
    }

    @Nonnull
    @Override
    public String getPartition() {
        return partition;
    }

    @Nonnull
    @Override
    public Path getPath() {
        return path;
    }

    @Nonnull
    @Override
    public Path getReflogDir(@Nonnull ManagedRepository member) {
        return MoreFiles.resolve(getPath(), "logs", "r", requireNonNull(member, "member").getLocalId());
    }

    @Nonnull
    @Override
    public String getSlug() {
        return hierarchyId;
    }

    @Nonnull
    @Override
    public Path getWorkTreesDir() {
        return shard.getWorkTreesDir();
    }

    @Override
    public int hashCode() {
        return Objects.hash(hierarchyId, partition);
    }

    @Override
    public String toString() {
        return getSlug();
    }

    @Nonnull
    @Override
    Path getTemplateDir() {
        return shard.getSharedObjectsTemplateDir();
    }
}
