package gaf3.core.services.code.service

import gaf3.core.data.PagedData
import gaf3.core.exception.BusinessError
import gaf3.core.services.code.dao.GafCodeItemDAO
import gaf3.core.services.code.domain.CodeForm
import gaf3.core.services.code.domain.CodeItem
import gaf3.core.services.code.entity.GafCodeItem
import gaf3.core.util.DataBeanHelper
import org.springframework.beans.factory.annotation.Autowired
import org.springframework.data.domain.Example
import org.springframework.data.domain.PageRequest
import org.springframework.data.domain.Sort
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.util.*
import javax.transaction.Transactional
import javax.validation.Valid

@Service
@Validated
class CodeService (@Autowired private val itemDao: GafCodeItemDAO) {
    @JvmOverloads
    fun find(filter: CodeItem? = null, skip: Int = 0, limit: Int = 100): PagedData<CodeItem> {
        val temp = GafCodeItem()
        DataBeanHelper.Bean2Obj(filter, temp)
        val example = Example.of(temp)
        val page:Int = skip/limit
        val paged = itemDao.findAll(example, PageRequest.of(page, limit, Sort.Direction.ASC, "code"))
        val rs = paged.content.map {
            p->convertDomain(p)
        }

        return PagedData(rs, paged.totalElements.toInt())
    }

    fun create(type: String?, @Valid form: CodeForm): CodeItem {
        // 检查约束条件
        hasText(form.code, "字典代码不能为空")
        hasText(type, "字典类型不能为空")
        if(itemDao.existsByTypeAndCode(type!!, form.code!!)) throw BusinessError(BusinessError.ERR_DATA_EXISTED, "字典代码重复")

        // 检查字典类型
        if("group" != type) {
           val item = itemDao.findOneByTypeAndCode("group", type) ?: throw BusinessError(BusinessError.ERR_DATA_EXISTED, "字典类型不存在")
            if (item.preset == 1) throw BusinessError(BusinessError.ERR_SERVICE_FAULT, "系统预置字典不能添加")
        }

        // 准备数据
        var data = GafCodeItem()
        DataBeanHelper.Bean2Obj(form, data)
        data.id = UUID.randomUUID().toString()
        data.type = type
        data.status = form.status ?: "0"
        data.preset = 0

        // 保存数据
        data = itemDao.save(data)
        return convertDomain(data)
    }

    @Transactional
    fun delete(id: String) {
        // TODO: 验证信息
        hasText(id, "ID不能为空")
        // 检查数据存在性
        val entity = itemDao.findByIdOrNull(id) ?: throw BusinessError(BusinessError.ERR_DATA_NOTEXIST)
        if(entity.preset == 1) {
            throw BusinessError(BusinessError.ERR_SERVICE_FAULT, "系统预置数据不能删除")
        }
        if(entity.type == null || entity.type == "" || entity.type == "group") {
            // 删除分类下的字典项
            itemDao.deleteAllByType(entity.code!!)
        }
        // 删除数据
        itemDao.delete(entity)
    }

    fun update(id: String, form: CodeForm): CodeItem {
        // TODO: 验证信息
        hasText(id, "ID不能为空")

        // TODO: 设置修改的属性
        var entity = itemDao.findByIdOrNull(id) ?: throw BusinessError(BusinessError.ERR_DATA_NOTEXIST, "字典数据不存在")
        // 准备数据，禁止修改type、code、preset
        DataBeanHelper.Bean2Obj(form, entity, "type", "code", "preset")

        entity = itemDao.save(entity)
        return convertDomain(entity)
    }

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

        val entity = itemDao.findByIdOrNull(id) ?: return null
        return convertDomain(entity)
    }

    fun fetchByCode(type: String, code: String): CodeItem? {
        // TODO: 验证信息
        hasText(type, "type不能为空")
        hasText(code, "code不能为空")
        val entity = itemDao.findOneByTypeAndCode(type, code) ?: return null
        return convertDomain(entity)
    }

    fun listTypes(): List<CodeItem> {
        return itemDao.listTypes().map {
            p->
            convertDomain(p)
        }
    }

    @JvmOverloads
    fun listCodes(type: String, limit: Int = 2000): PagedData<CodeItem> {
        val size = if(limit > 5000) 5000 else limit
        val item = itemDao.findOneByTypeAndCode("", type) ?: itemDao.findOneByTypeAndCode("group", type)
                ?: itemDao.findOneByTypeAndAlias("", type) ?: itemDao.findOneByTypeAndAlias("group", type)
                ?: throw BusinessError(BusinessError.ERR_BUSINESS, "字典类型不存在")
        val paged = itemDao.findByType(item.code ?: item.name ?: item.id!!, PageRequest.of(0, size, Sort.Direction.ASC, "code"))
        val rs = paged.content.map {
            p->convertDomain(p)
        }

        return PagedData(rs, paged.totalElements.toInt())
    }

//    fun exists(type: String?, code: String): Boolean {
//        val filter = GafCodeItem()
//        filter.type = type
//        filter.code = code
//        val fields = GafCodeItem::class.memberProperties.map { p-> p.name }
//        val ignorePaths = fields.subtract(listOf("type", "code")).toTypedArray()
//        val matcher = ExampleMatcher.matching()
//                .withIgnorePaths(*ignorePaths)
//                .withIncludeNullValues()
//        val example = Example.of(filter, matcher)
//        return itemDao.exists(example)
//    }

    companion object {
        fun convertDomain(entity: GafCodeItem): CodeItem {
            val data = CodeItem()
            DataBeanHelper.Bean2Obj(entity, data)
            return data
        }
    }
}