@file:Suppress("unused")

package gaf3.core.jpa.extension

import gaf3.core.jpa.annotation.*
import gaf3.core.jpa.converter.AttributeConverter
import org.springframework.data.domain.*
import org.springframework.data.jpa.convert.QueryByExamplePredicateBuilder
import org.springframework.data.jpa.domain.Specification
import org.springframework.data.jpa.repository.JpaRepository
import org.springframework.data.jpa.repository.JpaSpecificationExecutor
import org.springframework.data.repository.query.QueryByExampleExecutor
import java.lang.reflect.Field
import java.util.*
import javax.persistence.criteria.CriteriaBuilder
import javax.persistence.criteria.Predicate
import javax.persistence.criteria.Root

/*
inline fun <T, reified S : T> QueryByExampleExecutor<T>.findOne(example: S?, exact: Boolean = true): Optional<S> {
    return example?.let { findOne(Example.of(example, matching(example, exact))) } ?: Optional.empty()
}

inline fun<T, reified S: T> JpaRepository<S, Any>.findAll(example: S, exact: Boolean = true): List<S>  {
    return (this as? JpaSpecificationExecutor<S>)?.let {
        it.findAll(getSpec(example, exact))
    } ?: (this as? QueryByExampleExecutor<T>)?.let {
        it.findAll(Example.of(example, matching(example, exact))) as List<S>
    } ?: TODO("not support")
}

inline fun<T, reified S: T> JpaRepository<S, Any>.findAll(example: S, sort: Sort, exact: Boolean = true): List<S>  {
    return (this as? JpaSpecificationExecutor<S>)?.let {
        it.findAll(getSpec(example, exact), sort)
    } ?: (this as? QueryByExampleExecutor<T>)?.let {
        it.findAll(Example.of(example, matching(example, exact)), sort) as List<S>
    } ?: TODO("not support")
}

inline fun<T, reified S: T> JpaRepository<S, Any>.findAll(example: S, pageable: Pageable, exact: Boolean = true): Page<S> {
    return (this as? JpaSpecificationExecutor<S>)?.let {
        it.findAll(getSpec(example, exact), pageable)
    } ?: (this as? QueryByExampleExecutor<T>)?.let {
        it.findAll(Example.of(example, matching(example, exact)), pageable)
    } ?: TODO("not support")
}

inline fun<T, reified S: T> JpaRepository<S, Any>.count(example: S, exact: Boolean = true): Long  {
    return (this as? JpaSpecificationExecutor<S>)?.let {
        it.count(getSpec(example, exact))
    } ?: (this as? QueryByExampleExecutor<T>)?.let {
        it.count(Example.of(example, matching(example, exact)))
    } ?: TODO("not support")
}

inline fun<T, reified S: T> JpaRepository<S, Any>.exists(example: S, exact: Boolean = true): Boolean  {
    return (this as? JpaSpecificationExecutor<S>)?.let {
        it.count(getSpec(example, exact)) > 0
    } ?: (this as? QueryByExampleExecutor<T>)?.let {
        it.exists(Example.of(example, matching(example, exact)))
    } ?: TODO("not support")
}

inline fun<reified S> matching(example: S, exact: Boolean = true): ExampleMatcher {

    val cls = S::class.java
    var matcher: ExampleMatcher = ExampleMatcher.matching()

    // 计算IgnorePaths
    cls.declaredFields.filter {
        it.isAnnotationPresent(WithIgnorePath::class.java)
                || it.isAnnotationPresent(WithGenericMatcher::class.java)
                || it.isAnnotationPresent(GenericMatchers::class.java)
    }.map { it.name }.toTypedArray().takeIf { it.isNotEmpty() }?.let {
        matcher = matcher.withIgnorePaths(*it)
    }

    if (!exact) {
        // 非精确匹配模式下，启用Entity类和字段上定义的StringMatcher来实现模糊查询
        // 默认所有字段使用左匹配
        matcher = cls.getAnnotation(WithStringMatcher::class.java)?. let {
            matcher.withStringMatcher(it.value)
        } ?: matcher.withStringMatcher(ExampleMatcher.StringMatcher.STARTING)
        cls.declaredFields.filter { it.isAnnotationPresent(WithStringMatcher::class.java) }.forEach {
            matcher = matcher.withMatcher(it.name,
                    ExampleMatcher.GenericPropertyMatcher.of(it.getAnnotation(WithStringMatcher::class.java).value))
        }
    }
    return matcher
}

inline fun<reified T> getSpec(example: T, exact: Boolean = true): Specification<T> {
    return Specification { root, _, builder ->
        val predicate = QueryByExamplePredicateBuilder.getPredicate<T>(root, builder, Example.of(example, matching(example, exact)))
        // build generic matcher predicates
        val predicates = getPredicates<T>(example, root, builder).toTypedArray()
        if (predicates.isEmpty()) {
            predicate
        } else {
            builder.and( predicate, *predicates)
        }
    }
}

inline fun<reified T> getPredicates(entity: T, root: Root<T>, builder: CriteriaBuilder): List<Predicate> {
    val cls = T::class.java
    cls.declaredFields.filter {
        it.isAnnotationPresent(WithGenericMatcher::class.java)
    }.map {
        getPredicate(entity, it, root, builder)
    }
    return emptyList()
}

inline fun<reified T> getPredicate(entity: T, field: Field, root: Root<T>, builder: CriteriaBuilder): Predicate {
    var value: Any? = field.get(entity)
    val path = root.get<Any>(field.name)
    val matcher = field.getAnnotation(WithGenericMatcher::class.java)
    if(field.isAnnotationPresent(ValueConvert::class.java)) {
        // 转换值
        val convert = field.getAnnotation(ValueConvert::class.java)
        (convert.converter as? AttributeConverter<Any, Any>)?.let {
            value = it.convert(value, convert.parameter)
        }
    }
    return builder.equal(path, value)
}
*/