package com.gxwebsoft.cms.service.impl;

import cn.hutool.core.util.ObjectUtil;
import cn.hutool.core.util.StrUtil;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.toolkit.Wrappers;
import com.gxwebsoft.cms.entity.CmsNavigation;
import com.gxwebsoft.cms.entity.CmsPage;
import com.gxwebsoft.cms.mapper.CmsNavigationMapper;
import com.gxwebsoft.cms.mapper.CmsPageMapper;
import com.gxwebsoft.cms.param.CmsPageParam;
import com.gxwebsoft.cms.service.CmsPageService;
import com.gxwebsoft.common.core.web.PageParam;
import com.gxwebsoft.common.core.web.PageResult;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import javax.annotation.Resource;
import java.io.Serializable;
import java.util.List;

/**
 * 单页Service实现
 *
 * @author WorkBuddy
 * @since 2026-07-29
 */
@Service
public class CmsPageServiceImpl extends com.baomidou.mybatisplus.extension.service.impl.ServiceImpl<CmsPageMapper, CmsPage> implements CmsPageService {

    @Resource
    private CmsNavigationMapper cmsNavigationMapper;

    @Override
    public PageResult<CmsPage> pageRel(CmsPageParam param) {
        PageParam<CmsPage, CmsPageParam> page = new PageParam<>(param);
        List<CmsPage> list = baseMapper.selectPageRel(page, param);
        return new PageResult<>(list, page.getTotal());
    }

    @Override
    public List<CmsPage> listRel(CmsPageParam param) {
        List<CmsPage> list = baseMapper.selectListRel(param);
        PageParam<CmsPage, CmsPageParam> page = new PageParam<>();
        page.setDefaultOrder("sort_number asc,create_time desc");
        return page.sortRecords(list);
    }

    @Override
    public CmsPage getByIdRel(Integer pageId) {
        CmsPageParam param = new CmsPageParam();
        param.setPageId(pageId);
        return param.getOne(baseMapper.selectListRel(param));
    }

    @Override
    public CmsPage getPublishedByPath(String path) {
        if (StrUtil.isBlank(path)) {
            return null;
        }
        LambdaQueryWrapper<CmsPage> wrapper = Wrappers.lambdaQuery(CmsPage.class)
                .eq(CmsPage::getPath, path)
                .eq(CmsPage::getStatus, 1)
                .last("limit 1");
        return getOne(wrapper, false);
    }

    @Override
    public boolean pathExists(String path, Integer excludePageId) {
        if (StrUtil.isBlank(path)) {
            return false;
        }
        LambdaQueryWrapper<CmsPage> wrapper = Wrappers.lambdaQuery(CmsPage.class)
                .eq(CmsPage::getPath, path);
        if (excludePageId != null) {
            wrapper.ne(CmsPage::getPageId, excludePageId);
        }
        return count(wrapper) > 0;
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public boolean saveRel(CmsPage page) {
        if (StrUtil.isBlank(page.getPath())) {
            return false;
        }
        if (pathExists(page.getPath(), null)) {
            return false;
        }
        if (page.getSortNumber() == null) {
            page.setSortNumber(0);
        }
        if (page.getStatus() == null) {
            page.setStatus(0);
        }
        if (page.getViews() == null) {
            page.setViews(0);
        }
        boolean saved = save(page);
        if (saved && page.getNavigationId() != null) {
            bindNavigation(page.getPageId(), page.getNavigationId(), page.getTenantId());
        }
        return saved;
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public boolean updateByIdRel(CmsPage page) {
        if (page.getPageId() == null) {
            return false;
        }
        if (StrUtil.isNotBlank(page.getPath()) && pathExists(page.getPath(), page.getPageId())) {
            return false;
        }
        if (page.getSortNumber() == null) {
            page.setSortNumber(0);
        }
        boolean updated = updateById(page);
        if (updated) {
            // 编辑场景控制器未注入 tenantId，需从库中取回原记录的 tenantId，
            // 否则 bindNavigation 的 WHERE tenant_id = null 不会命中任何行，关联导航无法写回。
            Integer tenantId = page.getTenantId();
            if (tenantId == null) {
                CmsPage existing = getById(page.getPageId());
                tenantId = existing != null ? existing.getTenantId() : null;
            }
            bindNavigation(page.getPageId(), page.getNavigationId(), tenantId);
        }
        return updated;
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public boolean removeById(Serializable id) {
        // 先清空所有指向该单页的导航引用（navigation.page_id = NULL），再逻辑删除单页
        cmsNavigationMapper.update(null, Wrappers.lambdaUpdate(CmsNavigation.class)
                .eq(CmsNavigation::getPageId, (Integer) id)
                .set(CmsNavigation::getPageId, null));
        return super.removeById(id);
    }

    /**
     * 维护「单页 ↔ 导航」的一一对应关系：
     * 1) navigationId 为 null → 解绑（清掉所有绑了该 page 的导航引用）
     * 2) 否则：先清目标导航此前绑定的其他单页、再清本单页此前绑定的其他导航，最后建立新绑定
     * 依赖 UNIQUE(tenant_id, page_id) 保证一个 page 只被一个导航绑；
     * navigationId 为单值列（每个导航最多一个 page_id），天然保证一个导航只绑一篇单页。
     */
    private void bindNavigation(Integer pageId, Integer navigationId, Integer tenantId) {
        if (navigationId == null) {
            cmsNavigationMapper.update(null, Wrappers.lambdaUpdate(CmsNavigation.class)
                    .eq(CmsNavigation::getPageId, pageId)
                    .set(CmsNavigation::getPageId, null));
            return;
        }
        // 清掉目标导航此前绑定的其他单页（保证一个导航只绑一篇单页）
        cmsNavigationMapper.update(null, Wrappers.lambdaUpdate(CmsNavigation.class)
                .eq(CmsNavigation::getNavigationId, navigationId)
                .set(CmsNavigation::getPageId, null));
        // 清掉本单页此前绑定的其他导航（换绑场景）
        cmsNavigationMapper.update(null, Wrappers.lambdaUpdate(CmsNavigation.class)
                .eq(CmsNavigation::getPageId, pageId)
                .set(CmsNavigation::getPageId, null));
        // 建立新绑定
        cmsNavigationMapper.update(null, Wrappers.lambdaUpdate(CmsNavigation.class)
                .eq(CmsNavigation::getNavigationId, navigationId)
                .eq(CmsNavigation::getTenantId, tenantId)
                .set(CmsNavigation::getPageId, pageId));
    }
}
