package link;

import abstraction.CustomSetPersistenceDecorator;
import abstraction.PersistenceAdapter;
import category.CategoryId;
import datastructures.set.CustomSet;
import exeptions.LinkAlreadyExists;
import exeptions.LinkDoesNotExist;
import tag.TagName;
import user.Username;

import java.util.ArrayList;
import java.util.List;
import java.util.Optional;
import java.util.Set;
import java.util.stream.Collectors;

public class LinkRepository {

	final private CustomSet<Link> links;

	public LinkRepository(PersistenceAdapter<Link> linkPersistenceAdapter) {
		this.links = new CustomSetPersistenceDecorator<>(linkPersistenceAdapter);
	}

	public Optional<Link> getById(LinkId id) {
		return links.find(link -> link.getId().equals(id));
	}

	public Optional<Link> getByUrl(LinkUrl url) {
		return links.find(link -> link.getUrl().equals(url));
	}

	public void removeById(LinkId id) {
		getById(id).ifPresentOrElse(this::remove, () -> {
			throw new LinkDoesNotExist("Tried removing a link with id " + id + " but it does not exist.");
		});
	}

	public void removeByUrl(LinkUrl url) {
		getByUrl(url).ifPresentOrElse(this::remove, () -> {
			throw new LinkDoesNotExist("Tried removing a link with url " + url + " but it does not exist.");
		});
	}

	public void add(Link link) {
		checkDuplicates(link);
		links.add(link);
	}

	public Set<Link> getAll() {
		return links.getSet();
	}

	private void remove(Link link) {
		links.remove(link);
	}

	private void checkDuplicates(Link newLink) {
		if (this.getById(newLink.getId()).isPresent()) {
			throw new LinkAlreadyExists("A link with the id " + newLink.getId() + " already exitsts");
		}

		if (this.getByUrl(newLink.getUrl()).isPresent()) {
			throw new LinkAlreadyExists("A link with the url " + newLink.getUrl() + " already exitsts");
		}
	}

	public Set<Link> getByCategory(CategoryId id) {
		return links.stream().filter(link -> link.hasCategoryId(id)).collect(Collectors.toSet());
	}

	public Set<Link> getByTag(TagName tagName) {
		return links.stream().filter(link -> link.hasTagName(tagName)).collect(Collectors.toSet());
	}

	public Set<Link> getByUser(Username username) {
		return links.stream().filter(link -> link.wasCreatedBy(username)).collect(Collectors.toSet());
	}

	public List<Link> groupByHosts() {
		var input = new ArrayList<>(links.stream().toList());
		var result = new ArrayList<Link>();

		boolean changed = false;
		while (!input.isEmpty()) {
			input.removeAll(result);
			changed = false;
			for (Link link : input) {
				if (result.stream().anyMatch(resultLink -> resultLink.getUrl().hostEquals(link.getUrl()))) {
					result.add(link);
					changed = true;
				}
			}
			if (!changed && !input.isEmpty()) {
				result.add(input.get(0));
			}
		}
		return result;
	}
}
