package cn.lihongjie.coal.dbFunctions;

import com.google.common.cache.Cache;
import com.google.common.cache.CacheBuilder;

import io.hypersistence.utils.hibernate.type.array.StringArrayType;

import jakarta.annotation.PostConstruct;
import jakarta.persistence.EntityManager;
import jakarta.persistence.PersistenceContext;
import jakarta.persistence.Query;

import lombok.SneakyThrows;

import org.hibernate.internal.SessionImpl;
import org.hibernate.persister.entity.AbstractEntityPersister;
import org.hibernate.persister.entity.EntityPersister;
import org.hibernate.query.TypedParameterValue;
import org.springframework.stereotype.Service;

import java.util.*;

@Service
public class DbFunctionService {


    @PersistenceContext
    EntityManager entityManager;


    public List<String> selfAndChildrenIds(String tableName, List<String> ids) {
        return selfAndChildrenIds(tableName, ids, true);
    }

    public List<String> selfAndChildrenIds(String tableName, List<String> ids, Boolean includeSelf){


        Query nativeQuery = entityManager.createNativeQuery("""

                select self_and_children_ids(:tableName, :ids, :includeSelf)

                """);

        nativeQuery.setParameter("tableName", tableName);
        nativeQuery.unwrap(org.hibernate.query.Query.class).setParameter("ids", new TypedParameterValue(StringArrayType.INSTANCE, ids.toArray()));
        nativeQuery.setParameter("includeSelf", includeSelf);
        List resultList = nativeQuery.getResultList();

        if (resultList.isEmpty()){
            return Collections.emptyList();
        }
        return  Arrays.stream(((String[]) resultList.get(0))).toList();
    }


    public List<String> selfAndParentIds(String tableName, List<String> ids) {
        return selfAndParentIds(tableName, ids, true);
    }

    public List<String> selfAndParentIds(String tableName, List<String> ids, Boolean includeSelf){


        Query nativeQuery = entityManager.createNativeQuery("""

                select self_and_parent_ids(:tableName, :ids, :includeSelf)

                """);

        nativeQuery.setParameter("tableName", tableName);
        nativeQuery.unwrap(org.hibernate.query.Query.class).setParameter("ids", new TypedParameterValue(StringArrayType.INSTANCE, ids.toArray()));
        nativeQuery.setParameter("includeSelf", includeSelf);
        List resultList = nativeQuery.getResultList();

        if (resultList.isEmpty()){
            return Collections.emptyList();
        }
        return  Arrays.stream(((String[]) resultList.get(0))).toList();
    }

    private Cache<String, String> tableNameCache;

    @PostConstruct
    public void init(){
        tableNameCache = CacheBuilder.newBuilder().maximumSize(1000).build();
    }

    @SneakyThrows
    public String entityToTableName(Class<?> entityClass){

        return tableNameCache.get(entityClass.getCanonicalName(), () -> {

        Object entityExample;
        try {
            entityExample = entityClass.newInstance();
        } catch (ReflectiveOperationException e) {
            throw new RuntimeException(e);
        }

        SessionImpl session = entityManager.unwrap(SessionImpl.class);

        EntityPersister persister = session.getEntityPersister(null, entityExample);

        if (persister instanceof AbstractEntityPersister persisterImpl) {


            return persisterImpl.getRootTableName();

        } else {
            throw new RuntimeException("Unexpected persister type; a subtype of AbstractEntityPersister expected.");
        }

        });
    }
}
