package org.libelektra.plugin;

import java.io.IOException;
import java.net.MalformedURLException;
import java.net.URL;
import java.net.URLConnection;
import java.util.ArrayList;
import java.util.List;
import java.util.Optional;
import java.util.stream.Collectors;
import javax.annotation.Nonnull;
import org.libelektra.Key;
import org.libelektra.KeySet;
import org.libelektra.Plugin;
import org.libelektra.ReadableKey;

/**
 * This class can be used to validate link via the "check/link" meta key. Initially it uses a {@link
 * URLConnection} to check if the link is reachable. You can supply an optional timeout for the
 * reachability check. If no timeout or an empty timeout is provided, the plugins defaults to
 * checking the validity of the URL via {@link URL} and its {@link MalformedURLException}.
 *
 * <p>Only checks the connection during `kdbSet()` operations.
 */
public class LinkPlugin implements Plugin {
  private static final String PLUGIN_NAME = "linkchecker";

  @Override
  @Nonnull
  public KeySet getConfig() {
    return KeySet.create();
  }

  @Override
  public int open(KeySet conf, Key errorKey) {
    return STATUS_NO_UPDATE;
  }

  @Override
  public int get(KeySet ks, Key parentKey) {
    if (parentKey.isBelowOrSame(Key.create(JNI_MODULE_CONTRACT_ROOT))) {
      ks.append(
          Key.create(
              JNI_MODULE_CONTRACT_ROOT + "/infos",
              "Link Checker Java plugin, loaded by the JNI plugin"));
      ks.append(Key.create(JNI_MODULE_CONTRACT_ROOT + "/infos/provides", "check"));
      ks.append(Key.create(JNI_MODULE_CONTRACT_ROOT + "/infos/placements", "presetstorage"));
      ks.append(Key.create(JNI_MODULE_CONTRACT_ROOT + "/infos/author", "@aaronabebe"));
      ks.append(Key.create(JNI_MODULE_CONTRACT_ROOT + "/infos/metadata", "check/link"));
      ks.append(
          Key.create(
              JNI_MODULE_CONTRACT_ROOT + "/infos/description",
              "Check if a link is reachable or is a valid link"));
      ks.append(Key.create(JNI_MODULE_CONTRACT_ROOT + "/infos/status", "preview"));
      return STATUS_SUCCESS;
    }
    return STATUS_NO_UPDATE;
  }

  @Override
  public int set(KeySet ks, Key parentKey) {
    List<String> invalidKeys = new ArrayList<>(ks.size());

    boolean didUpdate = false;
    for (final Key key : ks) {
      Optional<ReadableKey> meta = key.getMeta("check/link");
      if (meta.isEmpty()) {
        continue;
      }

      didUpdate = true;

      String timeout = meta.get().getString();
      String keyValue = key.getString();

      if (timeout.isEmpty()) {
        if (!isValidURL(keyValue)) {
          invalidKeys.add(key.getName());
        }
      } else {
        if (!isReachable(keyValue, Integer.parseInt(timeout))) {
          if (!isValidURL(keyValue)) {
            invalidKeys.add(key.getName());
          }
        }
      }
    }

    if (invalidKeys.isEmpty()) {
      return didUpdate ? STATUS_SUCCESS : STATUS_NO_UPDATE;
    } else {
      parentKey.setError(
          "Found invalid or unreachable links: "
              + String.join(",", invalidKeys));
      return STATUS_ERROR;
    }
  }

  @Override
  public int error(KeySet ks, Key parentKey) {
    return 0;
  }

  @Override
  public int close(Key parentKey) {
    return 0;
  }

  @Override
  @Nonnull
  public String getName() {
    return PLUGIN_NAME;
  }

  boolean isReachable(String link, int timeout) {
    try {
      final URL url = new URL(link);
      final URLConnection conn = url.openConnection();
      conn.setConnectTimeout(timeout);
      conn.connect();
      conn.getInputStream().close();
      return true;
    } catch (IOException e) {
      // URLConnection.connect() throws IOException if the host is not reachable
      return false;
    }
  }

  private boolean isValidURL(String link) {
    try {
      new URL(link);
      return true;
    } catch (MalformedURLException e) {
      // new URL() throws MalformedURLException if the URL is not valid
      return false;
    }
  }
}

@@ -226,44 +226,8 @@
 al;%0A
-import java.util.stream.Collectors;%0A
 impo
@@ -2945,27 +2945,16 @@
 etError(
-%0A          
 %22Found i
@@ -2983,30 +2983,16 @@
 links: %22
-%0A             
  + Strin

fix formatting