// Copyright 2019 Oath Inc. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root.
package com.yahoo.vespa.hosted.provision.provisioning;

import com.yahoo.component.Version;
import com.yahoo.config.provision.ApplicationId;
import com.yahoo.config.provision.Flavor;
import com.yahoo.config.provision.NodeResources;
import com.yahoo.config.provision.NodeType;
import com.yahoo.vespa.hosted.provision.Node;
import com.yahoo.vespa.hosted.provision.node.Address;
import com.yahoo.vespa.hosted.provision.node.IP;
import com.yahoo.vespa.hosted.provision.node.OsVersion;
import com.yahoo.vespa.hosted.provision.node.Status;

import java.util.List;
import java.util.Objects;
import java.util.Optional;
import java.util.Set;

/**
 * Describes a single newly provisioned host by {@link HostProvisioner}.
 *
 * @author freva
 */
public class ProvisionedHost {

    private final String id;
    private final String hostHostname;
    private final Flavor hostFlavor;
    private final NodeType hostType;
    private final Optional<ApplicationId> exclusiveTo;
    private final List<Address> nodeAddresses;
    private final NodeResources nodeResources;
    private final Version osVersion;

    public ProvisionedHost(String id, String hostHostname, Flavor hostFlavor, NodeType hostType, Optional<ApplicationId> exclusiveTo,
                           List<Address> nodeAddresses, NodeResources nodeResources, Version osVersion) {
        this.id = Objects.requireNonNull(id, "Host id must be set");
        this.hostHostname = Objects.requireNonNull(hostHostname, "Host hostname must be set");
        this.hostFlavor = Objects.requireNonNull(hostFlavor, "Host flavor must be set");
        this.hostType = Objects.requireNonNull(hostType, "Host type must be set");
        this.exclusiveTo = Objects.requireNonNull(exclusiveTo, "exclusiveTo must be set");
        this.nodeAddresses = validateNodeAddresses(nodeAddresses);
        this.nodeResources = Objects.requireNonNull(nodeResources, "Node resources must be set");
        this.osVersion = Objects.requireNonNull(osVersion, "OS version must be set");
        if (!hostType.isHost()) throw new IllegalArgumentException(hostType + " is not a host");
    }

    private static List<Address> validateNodeAddresses(List<Address> nodeAddresses) {
        Objects.requireNonNull(nodeAddresses, "Node addresses must be set");
        if (nodeAddresses.isEmpty()) {
            throw new IllegalArgumentException("There must be at least one node address");
        }
        return nodeAddresses;
    }

    /** Generate {@link Node} instance representing the provisioned physical host */
    public Node generateHost() {
        Node.Builder builder = Node
                .create(id, IP.Config.of(Set.of(), Set.of(), nodeAddresses), hostHostname, hostFlavor, hostType)
                .status(Status.initial().withOsVersion(OsVersion.EMPTY.withCurrent(Optional.of(osVersion))));
        exclusiveTo.ifPresent(builder::exclusiveToApplicationId);
        return builder.build();
    }

    /** Generate {@link Node} instance representing the node running on this physical host */
    public Node generateNode() {
        return Node.reserve(Set.of(), nodeHostname(), hostHostname, nodeResources, hostType.childNodeType()).build();
    }

    public String getId() {
        return id;
    }

    public String hostHostname() {
        return hostHostname;
    }

    public Flavor hostFlavor() {
        return hostFlavor;
    }

    public String nodeHostname() {
        return nodeAddresses.get(0).hostname();
    }

    public List<Address> nodeAddresses() {
        return nodeAddresses;
    }

    public NodeResources nodeResources() { return nodeResources; }

    @Override
    public boolean equals(Object o) {
        if (this == o) return true;
        if (o == null || getClass() != o.getClass()) return false;
        ProvisionedHost that = (ProvisionedHost) o;
        return id.equals(that.id) &&
               hostHostname.equals(that.hostHostname) &&
               hostFlavor.equals(that.hostFlavor) &&
               nodeAddresses.equals(that.nodeAddresses) &&
               nodeResources.equals(that.nodeResources) &&
               osVersion.equals(that.osVersion);
    }

    @Override
    public int hashCode() {
        return Objects.hash(id, hostHostname, hostFlavor, nodeAddresses, nodeResources, osVersion);
    }

    @Override
    public String toString() {
        return "ProvisionedHost{" +
               "id='" + id + '\'' +
               ", hostHostname='" + hostHostname + '\'' +
               ", hostFlavor=" + hostFlavor +
               ", nodeAddresses='" + nodeAddresses + '\'' +
               ", nodeResources=" + nodeResources +
               ", osVersion=" + osVersion +
               '}';
    }

}

@@ -247,16 +247,63 @@
 tionId;%0A
+import com.yahoo.config.provision.ClusterSpec;%0A
 import c
@@ -1115,16 +1115,98 @@
 lusiveTo
+ApplicationId;%0A    private final Optional%3CClusterSpec.Type%3E exclusiveToClusterType
 ;%0A    pr
@@ -1427,16 +1427,43 @@
 ostType,
+%0A                          
  Optiona
@@ -1490,16 +1490,80 @@
 lusiveTo
+ApplicationId, Optional%3CClusterSpec.Type%3E exclusiveToClusterType
 ,%0A      
@@ -2022,16 +2022,29 @@
 lusiveTo
+ApplicationId
  = Objec
@@ -2072,16 +2072,29 @@
 lusiveTo
+ApplicationId
 , %22exclu
@@ -2099,16 +2099,153 @@
 lusiveTo
+ApplicationId must be set%22);%0A        this.exclusiveToClusterType = Objects.requireNonNull(exclusiveToClusterType, %22exclusiveToClusterType
  must be
@@ -3343,16 +3343,29 @@
 lusiveTo
+ApplicationId
 .ifPrese
@@ -3403,16 +3403,91 @@
 ionId);%0A
+        exclusiveToClusterType.ifPresent(builder::exclusiveToClusterType);%0A
         
@@ -4549,24 +4549,226 @@
 tFlavor) &&%0A
+               hostType == that.hostType &&%0A               exclusiveToApplicationId.equals(that.exclusiveToApplicationId) &&%0A               exclusiveToClusterType.equals(that.exclusiveToClusterType) &&%0A
             
@@ -5028,16 +5028,76 @@
 tFlavor,
+ hostType, exclusiveToApplicationId, exclusiveToClusterType,
  nodeAdd
@@ -5359,16 +5359,202 @@
 lavor +%0A
+               %22, hostType=%22 + hostType +%0A               %22, exclusiveToApplicationId=%22 + exclusiveToApplicationId +%0A               %22, exclusiveToClusterType=%22 + exclusiveToClusterType +%0A
         
@@ -5577,17 +5577,16 @@
 dresses=
-'
 %22 + node
@@ -5596,23 +5596,16 @@
 resses +
- '%5C'' +
 %0A       

Set exclusiveToClusterType when creating provisioned host Node instance