// Copyright 2017 Yahoo Holdings. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root.
package com.yahoo.vespa.hosted.controller.api.integration.organization;

import com.google.inject.Inject;
import com.yahoo.vespa.hosted.controller.api.identifiers.PropertyId;

import java.net.URI;
import java.time.Clock;
import java.time.Duration;
import java.time.Instant;
import java.util.Collections;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Optional;
import java.util.concurrent.atomic.AtomicLong;

public class MockOrganization implements Organization {

    private final Clock clock;
    private final AtomicLong counter;
    private final HashMap<IssueId, WrappedIssue> issues;
    private final HashMap<PropertyId, PropertyInfo> properties;

    @Inject
    @SuppressWarnings("unused")
    public MockOrganization() {
        this(Clock.systemUTC());
    }

    public MockOrganization(Clock clock) {
        this.clock = clock;

        counter = new AtomicLong();
        issues = new HashMap<>();
        properties = new HashMap<>();
    }

    @Override
    public IssueId file(Issue issue) {
        IssueId issueId = IssueId.from("" + counter.incrementAndGet());
        issues.put(issueId, new WrappedIssue(issue));
        return issueId;
    }

    @Override
    public Optional<IssueId> findBySimilarity(Issue issue) {
        return issues.entrySet().stream()
                .filter(entry -> entry.getValue().issue.summary().equals(issue.summary()))
                .findFirst()
                .map(Map.Entry::getKey);
    }

    @Override
    public void update(IssueId issueId, String description) {
        touch(issueId);
    }

    @Override
    public void commentOn(IssueId issueId, String comment) {
        touch(issueId);
    }

    @Override
    public boolean isOpen(IssueId issueId) {
        return issues.get(issueId).open;
    }

    @Override
    public boolean isActive(IssueId issueId, Duration maxInactivity) {
        return issues.get(issueId).updated.isAfter(clock.instant().minus(maxInactivity));
    }

    @Override
    public Optional<User> assigneeOf(IssueId issueId) {
        return Optional.ofNullable(issues.get(issueId).assignee);
    }

    @Override
    public boolean reassign(IssueId issueId, User assignee) {
        issues.get(issueId).assignee = assignee;
        touch(issueId);
        return true;
    }

    @Override
    public List<? extends List<? extends User>> contactsFor(PropertyId propertyId) {
        return properties.getOrDefault(propertyId, new PropertyInfo()).contacts;
    }

    @Override
    public URI issueCreationUri(PropertyId propertyId) {
        return URI.create("www.issues.tld/" + propertyId.id());
    }

    @Override
    public URI contactsUri(PropertyId propertyId) {
        return URI.create("www.contacts.tld/" + propertyId.id());
    }

    @Override
    public URI propertyUri(PropertyId propertyId) {
        return URI.create("www.properties.tld/" + propertyId.id());
    }

    public void close(IssueId issueId) {
        issues.get(issueId).open = false;
        touch(issueId);
    }

    public void setDefaultAssigneeFor(PropertyId propertyId, User defaultAssignee) {
        properties.get(propertyId).defaultAssignee = defaultAssignee;
    }

    public void setContactsFor(PropertyId propertyId, List<List<User>> contacts) {
        properties.get(propertyId).contacts = contacts;
    }

    public void addProperty(PropertyId propertyId) {
        properties.put(propertyId, new PropertyInfo());
    }

    private void touch(IssueId issueId) {
        issues.get(issueId).updated = clock.instant();
    }


    private class WrappedIssue {

        private Issue issue;
        private Instant updated;
        private boolean open;
        private User assignee;

        private WrappedIssue(Issue issue) {
            this.issue = issue;

            updated = clock.instant();
            open = true;
            assignee = issue.assignee().orElse(properties.get(issue.propertyId()).defaultAssignee);
        }

    }


    private class PropertyInfo {

        private User defaultAssignee;
        private List<List<User>> contacts = Collections.emptyList();

    }

}


@@ -562,16 +562,44 @@
 cLong;%0A%0A
+/**%0A * @author jvenstad%0A */%0A
 public c
@@ -714,16 +714,35 @@
  counter
+ = new AtomicLong()
 ;%0A    pr
@@ -749,28 +749,24 @@
 ivate final 
-Hash
 Map%3CIssueId,
@@ -766,23 +766,20 @@
 ssueId, 
-Wrapped
+Mock
 Issue%3E i
@@ -783,16 +783,34 @@
 %3E issues
+ = new HashMap%3C%3E()
 ;%0A    pr
@@ -821,20 +821,16 @@
 e final 
-Hash
 Map%3CProp
@@ -861,16 +861,34 @@
 operties
+ = new HashMap%3C%3E()
 ;%0A%0A    @
@@ -1072,117 +1072,8 @@
 ock;
-%0A%0A        counter = new AtomicLong();%0A        issues = new HashMap%3C%3E();%0A        properties = new HashMap%3C%3E();
 %0A   
@@ -1233,23 +1233,20 @@
 Id, new 
-Wrapped
+Mock
 Issue(is
@@ -1416,16 +1416,21 @@
         
+     
 .filter(
@@ -1512,16 +1512,21 @@
         
+     
 .findFir
@@ -1530,16 +1530,21 @@
 First()%0A
+     
         
@@ -3006,32 +3006,137 @@
 d.id());%0A    %7D%0A%0A
+    public Map%3CIssueId, MockIssue%3E issues() %7B%0A        return Collections.unmodifiableMap(issues);%0A    %7D%0A%0A
     public void 
@@ -3771,28 +3771,24 @@
    p
-rivate
+ublic
  class 
-Wrapped
+Mock
 Issu
@@ -3936,15 +3936,12 @@
 ate 
-Wrapped
+Mock
 Issu
@@ -3989,17 +3989,16 @@
  issue;%0A
-%0A
         
@@ -4001,16 +4001,21 @@
         
+this.
 updated 
@@ -4045,16 +4045,21 @@
         
+this.
 open = t
@@ -4075,16 +4075,21 @@
         
+this.
 assignee
@@ -4175,24 +4175,173 @@
 %0A        %7D%0A%0A
+        public Issue issue() %7B return issue; %7D%0A        public User assignee() %7B return assignee; %7D%0A        public boolean isOpen() %7B return open; %7D%0A%0A
     %7D%0A%0A%0A    

Allow issues in mock to be inspected