package cn.novalon.gym.manage.db.repository;

import cn.novalon.gym.manage.common.dto.PageRequest;
import cn.novalon.gym.manage.common.dto.PageResponse;
import cn.novalon.gym.manage.notify.core.domain.SysUserMessage;
import cn.novalon.gym.manage.notify.core.repository.ISysUserMessageRepository;
import cn.novalon.gym.manage.db.converter.SysUserMessageConverter;
import cn.novalon.gym.manage.db.entity.SysUserMessageEntity;
import cn.novalon.gym.manage.db.dao.SysUserMessageDao;
import cn.novalon.gym.manage.db.dao.QueryUtil;
import cn.novalon.gym.manage.db.entity.query.SysUserMessageQueryCriteria;
import org.springframework.data.domain.Sort;
import org.springframework.data.r2dbc.core.R2dbcEntityTemplate;
import org.springframework.stereotype.Repository;
import reactor.core.publisher.Flux;
import reactor.core.publisher.Mono;

import java.util.List;

/**
 * 用户消息仓储实现类
 * 
 * @author 张翔
 * @date 2026-03-13
 */
@Repository
public class SysUserMessageRepository implements ISysUserMessageRepository {

    private final SysUserMessageDao sysUserMessageDao;
    private final SysUserMessageConverter sysUserMessageConverter;
    private final R2dbcEntityTemplate r2dbcEntityTemplate;

    public SysUserMessageRepository(SysUserMessageDao sysUserMessageDao,
            SysUserMessageConverter sysUserMessageConverter, R2dbcEntityTemplate r2dbcEntityTemplate) {
        this.sysUserMessageDao = sysUserMessageDao;
        this.sysUserMessageConverter = sysUserMessageConverter;
        this.r2dbcEntityTemplate = r2dbcEntityTemplate;
    }

    @Override
    public Flux<SysUserMessage> findByUserIdOrderByCreateTimeDesc(Long userId) {
        SysUserMessageQueryCriteria criteria = new SysUserMessageQueryCriteria();
        criteria.setUserId(userId);

        org.springframework.data.relational.core.query.Query dbQuery = QueryUtil.getQuery(criteria);
        Sort sort = Sort.by(Sort.Direction.DESC, "createTime");
        dbQuery = dbQuery.sort(sort);

        return r2dbcEntityTemplate.select(SysUserMessageEntity.class)
                .matching(dbQuery)
                .all()
                .map(sysUserMessageConverter::toDomain);
    }

    @Override
    public Flux<SysUserMessage> findByUserIdAndIsReadOrderByCreateTimeDesc(Long userId, String isRead) {
        SysUserMessageQueryCriteria criteria = new SysUserMessageQueryCriteria();
        criteria.setUserId(userId);
        criteria.setIsRead(isRead);

        org.springframework.data.relational.core.query.Query dbQuery = QueryUtil.getQuery(criteria);
        Sort sort = Sort.by(Sort.Direction.DESC, "createTime");
        dbQuery = dbQuery.sort(sort);

        return r2dbcEntityTemplate.select(SysUserMessageEntity.class)
                .matching(dbQuery)
                .all()
                .map(sysUserMessageConverter::toDomain);
    }

    @Override
    public Mono<Long> countByUserIdAndIsRead(Long userId, String isRead) {
        SysUserMessageQueryCriteria criteria = new SysUserMessageQueryCriteria();
        criteria.setUserId(userId);
        criteria.setIsRead(isRead);

        org.springframework.data.relational.core.query.Query dbQuery = QueryUtil.getQuery(criteria);

        return r2dbcEntityTemplate.count(dbQuery, SysUserMessageEntity.class);
    }

    @Override
    public Mono<PageResponse<SysUserMessage>> findByUserIdPage(Long userId, PageRequest pageRequest) {
        int page = pageRequest.getPage();
        int size = pageRequest.getSize();
        String sort = pageRequest.getSort();
        String order = pageRequest.getOrder();

        Sort sortObj = Sort.unsorted();
        if (sort != null && !sort.isEmpty()) {
            sortObj = Sort.by(Sort.Direction.fromString(order), sort);
        }

        org.springframework.data.domain.PageRequest pageable = org.springframework.data.domain.PageRequest.of(page, size, sortObj);

        SysUserMessageQueryCriteria criteria = new SysUserMessageQueryCriteria();
        criteria.setUserId(userId);
        org.springframework.data.relational.core.query.Query dbQuery = QueryUtil.getQuery(criteria);

        return r2dbcEntityTemplate.select(SysUserMessageEntity.class)
                .matching(dbQuery.with(pageable))
                .all()
                .collectList()
                .zipWith(r2dbcEntityTemplate.count(dbQuery, SysUserMessageEntity.class))
                .map(tuple -> {
                    long total = tuple.getT2();
                    int totalPages = (int) Math.ceil((double) total / size);
                    List<SysUserMessage> messageList = tuple.getT1().stream()
                            .map(sysUserMessageConverter::toDomain)
                            .toList();
                    return new PageResponse<>(messageList, totalPages, total, page, size);
                });
    }

    @Override
    public Mono<PageResponse<SysUserMessage>> findByUserIdAndIsReadPage(Long userId, String isRead, PageRequest pageRequest) {
        int page = pageRequest.getPage();
        int size = pageRequest.getSize();
        String sort = pageRequest.getSort();
        String order = pageRequest.getOrder();

        Sort sortObj = Sort.unsorted();
        if (sort != null && !sort.isEmpty()) {
            sortObj = Sort.by(Sort.Direction.fromString(order), sort);
        }

        org.springframework.data.domain.PageRequest pageable = org.springframework.data.domain.PageRequest.of(page, size, sortObj);

        SysUserMessageQueryCriteria criteria = new SysUserMessageQueryCriteria();
        criteria.setUserId(userId);
        criteria.setIsRead(isRead);
        org.springframework.data.relational.core.query.Query dbQuery = QueryUtil.getQuery(criteria);

        return r2dbcEntityTemplate.select(SysUserMessageEntity.class)
                .matching(dbQuery.with(pageable))
                .all()
                .collectList()
                .zipWith(r2dbcEntityTemplate.count(dbQuery, SysUserMessageEntity.class))
                .map(tuple -> {
                    long total = tuple.getT2();
                    int totalPages = (int) Math.ceil((double) total / size);
                    List<SysUserMessage> messageList = tuple.getT1().stream()
                            .map(sysUserMessageConverter::toDomain)
                            .toList();
                    return new PageResponse<>(messageList, totalPages, total, page, size);
                });
    }

    @Override
    public Mono<Long> markAllAsReadByUserId(Long userId) {
        org.springframework.data.relational.core.query.Update update = org.springframework.data.relational.core.query.Update.update("is_read", "1");
        
        org.springframework.data.relational.core.query.Query query = org.springframework.data.relational.core.query.Query.query(
                org.springframework.data.relational.core.query.Criteria.where("user_id").is(userId)
                        .and("deleted_at").isNull()
        );

        return r2dbcEntityTemplate.update(SysUserMessageEntity.class)
                .matching(query)
                .apply(update);
    }

    @Override
    public Mono<SysUserMessage> save(SysUserMessage message) {
        SysUserMessageEntity entity = sysUserMessageConverter.toEntity(message);
        return sysUserMessageDao.save(entity)
                .map(sysUserMessageConverter::toDomain);
    }

    @Override
    public Mono<SysUserMessage> findById(Long id) {
        return sysUserMessageDao.findById(id)
                .map(sysUserMessageConverter::toDomain);
    }

    @Override
    public Mono<Void> deleteById(Long id) {
        return sysUserMessageDao.deleteById(id);
    }
}