package gaf3.core.services.menu.service.impl

import gaf3.core.exception.BusinessError
import gaf3.core.services.menu.dao.GafMenuItemDAO
import gaf3.core.services.menu.domain.MenuItem
import gaf3.core.services.menu.entity.GafMenuItem
import gaf3.core.services.menu.service.GafMenuStore
import gaf3.core.util.DataBeanHelper
import gaf3.core.util.StringUtil
import org.springframework.beans.factory.annotation.Autowired
import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty
import org.springframework.data.domain.Example
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
import org.springframework.validation.annotation.Validated
import java.util.*

@Service
@Validated
@ConditionalOnProperty(prefix = "gaf.menu", name = ["store-type"], havingValue = "db")
class DBMenuStore(@Autowired val itemDao: GafMenuItemDAO) : GafMenuStore {

    override fun load(): List<MenuItem> {
        return tree() ?: emptyList()
    }

    fun tree(pid: String? = null, depth: Int = 0): List<MenuItem>? {
        if (depth > 6) return null // 最大支持六级子菜单
        val filter = with(GafMenuItem()) {
            this.pid = pid ?: "00000000-0000-0000-0000-000000000000"
            this
        }
        val example = Example.of(filter)
        val spec = Specification<GafMenuItem> { root, _, builder ->
            val predicate = QueryByExamplePredicateBuilder.getPredicate(root, builder, example)
            builder.and(predicate,
                    builder.or(builder.equal(root.get<String>("mode"), "0"),
                            builder.equal(root.get<String>("mode"), "2")))
        }
        val rs = itemDao.findAll(spec, Sort.by(Sort.Direction.ASC, "ranking")).map {
            val item = convert(it)
            item.children = tree(it.id, depth + 1)
            item
        }

        return if (rs.isNotEmpty()) rs else null
    }

    override fun save(data: MenuItem): MenuItem {
        var entity = if (data.id == null) {
            // 创建
            Assert.hasText(data.title, "title不能为空")
            with(GafMenuItem()) {
                DataBeanHelper.Bean2Obj(data, this)
                id = UUID.randomUUID().toString()
                pid = data.pid ?: "00000000-0000-0000-0000-000000000000"
                preset = 0
                this
            }
        } else {
            // 修改
            with(itemDao.findByIdOrNull(data.id) ?: throw BusinessError(BusinessError.ERR_DATA_NOTEXIST)) {
                // 检查数据存在性
                if (!StringUtil.isNullOrEmpty(data.title) && data.title != title && itemDao.existsByPidAndTitle(pid!!, data.title!!)) {
                    throw BusinessError(BusinessError.ERR_DATA_EXISTED, "菜单项标题重复")
                }
                // 准备数据，禁止修改preset
                DataBeanHelper.Bean2Obj(data, this, "preset")
                this
            }
        }

        // 保存数据
        entity = itemDao.save(entity)
        return convert(entity)
    }

    override fun delete(id: String) {
        // 验证信息
        Assert.hasText(id, "ID不能为空")
        // 检查数据存在性
        val entity = itemDao.findByIdOrNull(id) ?: throw BusinessError(BusinessError.ERR_DATA_NOTEXIST)
        if (entity.preset == 1) {
            throw BusinessError(BusinessError.ERR_SERVICE_FAULT, "系统预置数据不能删除")
        }
        // 删除数据
        itemDao.deleteById(id)
    }

    fun convert(data: GafMenuItem): MenuItem {
        val res = MenuItem()
        DataBeanHelper.Bean2Obj(data, res)
        return res
    }
}