// Copyright Verizon Media. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root.
package com.yahoo.vespa.hosted.provision.maintenance;

import com.yahoo.config.provision.NodeType;
import com.yahoo.jdisc.Metric;
import com.yahoo.vespa.hosted.provision.Node;
import com.yahoo.vespa.hosted.provision.NodeRepository;
import com.yahoo.vespa.hosted.provision.node.Agent;
import com.yahoo.vespa.hosted.provision.node.History;

import java.time.Duration;
import java.time.Instant;
import java.util.ArrayList;
import java.util.Comparator;
import java.util.logging.Logger;

/**
 *
 * Expires parked nodes in dynamically provisioned zones.
 * If number of parked hosts exceed MAX_ALLOWED_PARKED_HOSTS, recycle in a queue order
 *
 * @author olaa
 */
public class ParkedExpirer extends NodeRepositoryMaintainer {

    private static final int MAX_ALLOWED_PARKED_HOSTS = 20;
    private static final Logger log = Logger.getLogger(ParkedExpirer.class.getName());


    private final NodeRepository nodeRepository;

    ParkedExpirer(NodeRepository nodeRepository, Duration interval, Metric metric) {
        super(nodeRepository, interval, metric);
        this.nodeRepository = nodeRepository;
    }

    @Override
    protected double maintain() {
        if (!nodeRepository.zone().getCloud().dynamicProvisioning())
            return 1.0;

        var parkedHosts = new ArrayList<>(nodeRepository.nodes().list(Node.State.parked)
                                                        .nodeType(NodeType.host)
                                                        .asList());

        parkedHosts.sort(Comparator.comparing(this::getParkedTime));
        var hostsToExpire = parkedHosts.size() - MAX_ALLOWED_PARKED_HOSTS;
        for(int i = 0; i < hostsToExpire; i++) {
            var parkedHost = parkedHosts.get(i);
            log.info("Allowed number of parked nodes exceeded. Recycling " + parkedHost.hostname());
            nodeRepository.nodes().deallocate(parkedHost, Agent.ParkedExpirer, "Expired by ParkedExpirer");
        }

        return 1.0;
    }

    private Instant getParkedTime(Node node) {
        return node.history().event(History.Event.Type.parked)
                .map(History.Event::at)
                .orElse(Instant.EPOCH); // Should not happen
    }
}

@@ -1606,80 +1606,11 @@
     
-parkedHosts.sort(Comparator.comparing(this::getParkedTime));%0A        var
+int
  hos
@@ -1621,16 +1621,28 @@
 Expire =
+ Math.max(0,
  parkedH
@@ -1679,16 +1679,17 @@
 ED_HOSTS
+)
 ;%0A      
@@ -1694,27 +1694,171 @@
     
-for(int i = 0; i %3C 
+nodeRepository.nodes().list(Node.State.parked).nodeType(NodeType.host)%0A                .sortedBy(Comparator.comparing(this::getParkedTime))%0A                .first(
 host
@@ -1870,17 +1870,14 @@
 pire
-; i++) %7B%0A
+)%0A    
     
@@ -1888,45 +1888,28 @@
     
-var parkedHost = parkedHosts.get(i);%0A
+.forEach(host -%3E %7B%0A 
     
@@ -1916,16 +1916,23 @@
         
+       
 log.info
@@ -1988,23 +1988,17 @@
 ing %22 + 
-parkedH
+h
 ost.host
@@ -2010,32 +2010,40 @@
 ));%0A            
+      
+  
 nodeRepository.n
@@ -2060,23 +2060,17 @@
 llocate(
-parkedH
+h
 ost, Age
@@ -2120,25 +2120,35 @@
 %22);%0A        
-%7D
+        %7D);
 %0A%0A        re

Use NodeList functions to find expirable hosts