package com.snack.server.module.notice.service.impl;

import cn.hutool.core.bean.BeanUtil;
import cn.hutool.core.collection.CollUtil;
import cn.hutool.core.util.StrUtil;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.conditions.update.LambdaUpdateWrapper;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.snack.server.common.ResultCode;
import com.snack.server.exception.BusinessException;
import com.snack.server.module.notice.dto.req.NoticePageReq;
import com.snack.server.module.notice.dto.req.NoticeSaveReq;
import com.snack.server.module.notice.entity.Notice;
import com.snack.server.module.notice.mapper.NoticeMapper;
import com.snack.server.module.notice.service.NoticeService;
import com.snack.server.module.notice.vo.NoticeVO;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import java.time.LocalDateTime;
import java.util.List;

/**
 * 公告业务实现
 */
@Slf4j
@Service
@RequiredArgsConstructor
public class NoticeServiceImpl implements NoticeService {

    private final NoticeMapper noticeMapper;

    @Override
    public Page<NoticeVO> pageNotices(NoticePageReq req) {
        Page<Notice> page = new Page<>(req.getCurrent(), req.getSize());
        LambdaQueryWrapper<Notice> wrapper = new LambdaQueryWrapper<Notice>()
                .and(StrUtil.isNotBlank(req.getKeyword()), w -> w.like(Notice::getTitle, req.getKeyword()))
                .eq(req.getType() != null, Notice::getType, req.getType())
                .eq(req.getStatus() != null, Notice::getStatus, req.getStatus())
                // 置顶在前，其余按时间倒序
                .orderByDesc(Notice::getIsTop)
                .orderByDesc(Notice::getId);

        Page<Notice> result = noticeMapper.selectPage(page, wrapper);
        Page<NoticeVO> voPage = new Page<>(result.getCurrent(), result.getSize(), result.getTotal());
        voPage.setRecords(result.getRecords().stream().map(this::toVO).toList());
        return voPage;
    }

    @Override
    public NoticeVO getNoticeById(Long id) {
        Notice n = noticeMapper.selectById(id);
        if (n == null) {
            throw new BusinessException(1000, "公告不存在");
        }
        return toVO(n);
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public Long createNotice(NoticeSaveReq req, Long publisherId) {
        validateTimeRange(req.getStartTime(), req.getEndTime());
        Notice n = new Notice();
        BeanUtil.copyProperties(req, n);
        if (n.getIsTop() == null) n.setIsTop(0);
        if (n.getStatus() == null) n.setStatus(1);
        if (n.getViewCount() == null) n.setViewCount(0);
        n.setPublisherId(publisherId);
        noticeMapper.insert(n);
        log.info("创建公告 id={} title={}", n.getId(), n.getTitle());
        return n.getId();
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public void updateNotice(NoticeSaveReq req) {
        if (req.getId() == null) {
            throw new BusinessException(1000, "ID 不能为空");
        }
        Notice existing = noticeMapper.selectById(req.getId());
        if (existing == null) {
            throw new BusinessException(1000, "公告不存在");
        }
        validateTimeRange(req.getStartTime(), req.getEndTime());
        Notice n = new Notice();
        BeanUtil.copyProperties(req, n);
        // 不修改 publisherId、viewCount、createTime
        n.setPublisherId(existing.getPublisherId());
        n.setViewCount(existing.getViewCount());
        n.setCreateTime(existing.getCreateTime());
        noticeMapper.updateById(n);
        log.info("更新公告 id={}", req.getId());
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public void deleteNotice(Long id) {
        Notice existing = noticeMapper.selectById(id);
        if (existing == null) {
            throw new BusinessException(1000, "公告不存在");
        }
        noticeMapper.deleteById(id);
        log.info("删除公告 id={}", id);
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public void updateStatus(Long id, Integer status) {
        if (id == null || status == null) {
            throw new BusinessException(1000, "参数错误");
        }
        Notice existing = noticeMapper.selectById(id);
        if (existing == null) {
            throw new BusinessException(1000, "公告不存在");
        }
        Notice update = new Notice();
        update.setId(id);
        update.setStatus(status);
        noticeMapper.updateById(update);
        log.info("公告 id={} 状态更新为 {}", id, status);
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public void updateTop(Long id, Integer isTop) {
        if (id == null || isTop == null) {
            throw new BusinessException(1000, "参数错误");
        }
        Notice existing = noticeMapper.selectById(id);
        if (existing == null) {
            throw new BusinessException(1000, "公告不存在");
        }
        Notice update = new Notice();
        update.setId(id);
        update.setIsTop(isTop);
        noticeMapper.updateById(update);
        log.info("公告 id={} 置顶状态更新为 {}", id, isTop);
    }

    @Override
    public List<NoticeVO> listActiveNotices() {
        LocalDateTime now = LocalDateTime.now();
        LambdaQueryWrapper<Notice> wrapper = new LambdaQueryWrapper<Notice>()
                .eq(Notice::getStatus, 1)
                .and(w -> w
                        .and(w2 -> w2.isNull(Notice::getStartTime).or().le(Notice::getStartTime, now))
                        .and(w3 -> w3.isNull(Notice::getEndTime).or().ge(Notice::getEndTime, now))
                )
                .orderByDesc(Notice::getIsTop)
                .orderByDesc(Notice::getId);
        List<Notice> list = noticeMapper.selectList(wrapper);
        return list.stream().map(this::toVO).toList();
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public void incrViewCount(Long id) {
        if (id == null) return;
        // 用 SQL 直接 +1，避免读改写并发覆盖
        noticeMapper.update(null, new LambdaUpdateWrapper<Notice>()
                .eq(Notice::getId, id)
                .setSql("view_count = view_count + 1"));
    }

    private NoticeVO toVO(Notice n) {
        NoticeVO vo = new NoticeVO();
        BeanUtil.copyProperties(n, vo);
        return vo;
    }

    /** 生效时间必须早于失效时间（两个都填的情况下） */
    private void validateTimeRange(LocalDateTime start, LocalDateTime end) {
        if (start != null && end != null && start.isAfter(end)) {
            throw new BusinessException(1000, "生效时间不能晚于失效时间");
        }
    }
}
