package gaf3.core.services.log.service

import gaf3.core.data.PagedData
import gaf3.core.exception.BusinessError
import gaf3.core.services.log.dao.GafLogDAO
import gaf3.core.services.log.domain.LogForm
import gaf3.core.services.log.domain.UpdateForm
import gaf3.core.services.log.entity.GafLog
import gaf3.core.util.DataBeanHelper
import org.springframework.beans.factory.annotation.Autowired
import org.springframework.beans.factory.annotation.Qualifier
import org.springframework.data.domain.Example
import org.springframework.data.domain.ExampleMatcher
import org.springframework.data.domain.PageRequest
import org.springframework.data.domain.Sort
import org.springframework.data.jpa.convert.QueryByExamplePredicateBuilder
import org.springframework.data.jpa.domain.Specification
import org.springframework.data.repository.findByIdOrNull
import org.springframework.stereotype.Service
import org.springframework.util.Assert.hasText
import org.springframework.validation.annotation.Validated
import java.time.*
import java.time.format.DateTimeFormatter
import java.util.*
import javax.validation.Valid


@Service("gafLogService")
@Validated
class LogService (@Autowired private val logDao: GafLogDAO) {
    companion object {
        // val log: Logger = LoggerFactory.getLogger(UserServiceController::class.java)
        var fmt = DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss")
    }
    @JvmOverloads
    fun find(filter: GafLog, timestamps: Array<Long>? = emptyArray(), skip: Int = 0, limit: Int = 100): PagedData<GafLog> {
        val matcher = ExampleMatcher.matching()
                .withIgnorePaths("id", "timestamp", "createdAt", "updatedAt")
        val example = Example.of(filter, matcher)
        val page:Int = skip/limit
        val paged = if (!timestamps.isNullOrEmpty()) {
            val date1 = Instant.ofEpochMilli(timestamps[0]).atZone(ZoneId.systemDefault()).toLocalDate()
            val date2 = timestamps.getOrNull(1)?.let { Instant.ofEpochMilli(it).atZone(ZoneId.systemDefault()).toLocalDate() } ?: date1
            val start = date1.atStartOfDay()
            val end = date2.atTime(OffsetTime.MAX).toLocalDateTime()
            logDao.findAll(
                    betweenTime(example, start, end), PageRequest.of(page, limit, Sort.Direction.DESC, "createdAt"))
        } else {
            logDao.findAll(example, PageRequest.of(page, limit, Sort.Direction.DESC, "createdAt"))
        }
        return PagedData(paged.content, paged.totalElements.toInt())
    }

    fun create(@Valid form: LogForm): GafLog {

        // TODO: 准备数据
        var data = GafLog()
        DataBeanHelper.Bean2Obj(form, data)
        data.id = UUID.randomUUID().toString()
        if(data.timestamp == null) {
            val now = LocalDateTime.now()
            data.timestamp = now.toInstant(ZoneOffset.ofHours(8)).toEpochMilli()
            data.eventTime = fmt.format(now)
        }

        // TODO: 保存数据
        data = logDao.save(data)
        return data
    }

    fun update(id: String, data: UpdateForm): GafLog {
        // TODO: 验证信息
        hasText(id, "ID不能为空")
        var entity = logDao.findByIdOrNull(id) ?: throw BusinessError(BusinessError.ERR_DATA_NOTEXIST)
        DataBeanHelper.Bean2Obj(data, entity)

        // TODO: 保存数据
        entity = logDao.save(entity)
        return entity
    }

    fun fetch(id: String): GafLog? {
        // TODO: 验证信息
        hasText(id, "ID不能为空")

        return logDao.findByIdOrNull(id)
    }

    fun betweenTime(example: Example<GafLog>, startTime: Long, endTime: Long): Specification<GafLog> {
        return Specification { root, _, builder ->
            val predicate = QueryByExamplePredicateBuilder.getPredicate<GafLog>(root, builder, example)
            builder.and( predicate,
                    builder.between(root.get("timestamp"), startTime, endTime))
        }
    }

    fun betweenTime(example: Example<GafLog>, startTime: LocalDateTime, endTime: LocalDateTime): Specification<GafLog> {
        return Specification { root, _, builder ->
            val predicate = QueryByExamplePredicateBuilder.getPredicate<GafLog>(root, builder, example)
            builder.and( predicate,
                    builder.greaterThanOrEqualTo(root.get("createdAt"), startTime),
                    builder.lessThanOrEqualTo(root.get("createdAt"), endTime))
        }
    }

    fun fetchValues(field: String): List<String> {
        return when(field) {
            "module" -> logDao.fetchModules()
            "event" -> logDao.fetchEvents()
            "action" -> logDao.fetchActions()
            "result" -> logDao.fetchResults()
            else -> throw BusinessError(BusinessError.ERR_BADPARAM, "无效字段名")
        }
    }
}