package com.pingcap.community.blogserver.util;

import javax.persistence.ColumnResult;
import javax.persistence.ConstructorResult;
import javax.persistence.Query;
import javax.persistence.SqlResultSetMapping;
import java.lang.reflect.Constructor;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.LinkedList;
import java.util.List;

public class ResultSetUtils {

  public static <T> T map(Class<T> type, Object[] tuple, Constructor<T> constructor){
    try {
      return constructor.newInstance(tuple);
    } catch (Exception e) {
      throw new RuntimeException(e);
    }
  }

  public static <T> List<T> map(Class<T> type, List<Object[]> records, Constructor<T> constructor){
    List<T> result = new LinkedList<>();
    for(Object[] record : records){
      result.add(map(type, record, constructor));
    }
    return result;
  }

  public static <T> List<T> getResultList(Query query, Class<T> type) {
    List<Class<?>> tupleTypes = new ArrayList<>();
    SqlResultSetMapping annotation = type.getAnnotation(SqlResultSetMapping.class);
    List<ConstructorResult> constructorResults = Arrays.stream(annotation.classes()).toList();

    List<Constructor<T>> constructors = new ArrayList<>();
    for (ConstructorResult constructorResult : constructorResults) {
      ColumnResult[] columns = constructorResult.columns();
      for (ColumnResult column : columns) {
        tupleTypes.add(column.type());
      }

      try{
        Constructor<T> constructor = type.getConstructor(tupleTypes.toArray(new Class<?>[columns.length]));
        constructors.add(constructor);
      } catch (NoSuchMethodException ignored) {
      }
    }

    @SuppressWarnings("unchecked")
    List<Object[]> records = query.getResultList();

    if (constructors.isEmpty()) {
      throw new RuntimeException("no corresponding constructor is provided.");
    } else if (constructors.size() == 1) {
      return map(type, records, constructors.get(0));
    } else {
      throw new RuntimeException("multiple constructors are not supported for now.");
    }
  }
}
