package life.catalogue.db;

import java.io.IOException;
import java.sql.Array;
import java.sql.Connection;
import java.sql.PreparedStatement;
import java.sql.Statement;
import java.util.ArrayList;
import java.util.Comparator;
import java.util.List;

import com.google.common.base.Joiner;
import com.google.common.reflect.ClassPath;
import life.catalogue.api.model.ColUser;
import life.catalogue.api.model.EditorialDecision;
import life.catalogue.api.model.Sector;
import life.catalogue.api.model.SectorImport;
import life.catalogue.api.vocab.*;
import life.catalogue.db.type.BaseEnumSetTypeHandler;
import org.gbif.nameparser.api.Rank;
import org.junit.ClassRule;
import org.junit.Test;

import static junit.framework.TestCase.assertEquals;

public class PgSetupRuleTest {
  
  @ClassRule
  public static PgSetupRule pgSetupRule = new PgSetupRule();
  
  
  @Test
  public void pgEnums() throws Exception {
    try (Connection c = pgSetupRule.connect()) {
      for (Class<? extends Enum<?>> cl : listEnums()) {
        final String pgEnumName = BaseEnumSetTypeHandler.pgEnumName(cl);
        // make sure all ranks from our enum are present in postgres
        PreparedStatement pst = c.prepareStatement("SELECT ?::"+pgEnumName);
        for (Enum<?> e : cl.getEnumConstants()){
          pst.setString(1, e.name());
          pst.execute();
        }
        // ... and vice versa
        String expected = "{" + Joiner.on(",").join(cl.getEnumConstants()) + "}";
        Statement st = c.createStatement();
        st.execute("select enum_range(null::"+pgEnumName+")");
        st.getResultSet().next();
        Array arr = st.getResultSet().getArray(1);
        assertEquals(expected, arr.toString() );
      }
    }
  }
  
  private static List<Class<? extends Enum<?>>> listEnums() throws IOException {
    ClassPath cp = ClassPath.from(DatasetType.class.getClassLoader());
    List<Class<? extends Enum<?>>> enums = new ArrayList<>();
    addPackageEnums(cp, enums, DatasetType.class.getPackage());
    addPackageEnums(cp, enums, Rank.class.getPackage());
    enums.add(Sector.Mode.class);
    enums.add(SectorImport.State.class);
    enums.add(EditorialDecision.Mode.class);
    enums.add(ColUser.Role.class);
    // not needed for persistency
    enums.remove(Country.class);
    enums.remove(ColDwcTerm.class);
    enums.remove(CSLRefType.class);
    enums.remove(Frequency.class);
    enums.remove(GeoTimeType.class);
    // sort and print
    enums.sort(Comparator.comparing(cl -> BaseEnumSetTypeHandler.pgEnumName(cl)));
    return enums;
  }
  
  @Test
  public void pgEnumSql() throws Exception {
    for (Class<? extends Enum<?>> cle : listEnums()) {
      printType(cle);
    }
  }
  
  private static void addPackageEnums(ClassPath cp, List<Class<? extends Enum<?>>> enums, Package pack){
    for (ClassPath.ClassInfo cli : cp.getTopLevelClasses(pack.getName())) {
      Class<?> cl = cli.load();
      if (cl.isEnum()) {
        enums.add((Class<Enum<?>>) cl);
      }
    }
  }
  
  private <T extends Enum<?>> void printType(Class<T> clazz) throws Exception {
    StringBuilder sb = new StringBuilder();
    sb.append("CREATE TYPE " + BaseEnumSetTypeHandler.pgEnumName(clazz) + " AS ENUM (\n");
    boolean first = true;
    for (T e : clazz.getEnumConstants()){
      if (first) {
        first = false;
      } else {
        sb.append(",\n");
      }
      sb.append("  '");
      sb.append(e.name());
      sb.append("'");
    }
    sb.append("\n);\n");
    System.out.println(sb);
  }
  
}
@@ -25,230 +25,8 @@
 b;%0A%0A
-import java.io.IOException;%0Aimport java.sql.Array;%0Aimport java.sql.Connection;%0Aimport java.sql.PreparedStatement;%0Aimport java.sql.Statement;%0Aimport java.util.ArrayList;%0Aimport java.util.Comparator;%0Aimport java.util.List;%0A%0A
 impo
@@ -459,16 +459,238 @@
 .Test;%0A%0A
+import java.io.IOException;%0Aimport java.sql.Array;%0Aimport java.sql.Connection;%0Aimport java.sql.PreparedStatement;%0Aimport java.sql.Statement;%0Aimport java.util.ArrayList;%0Aimport java.util.Comparator;%0Aimport java.util.List;%0A%0A
 import s
@@ -2417,24 +2417,65 @@
 ype.class);%0A
+    enums.remove(DatasetSettings.class);%0A
     // sort 

Exclude settings enum from persistency test