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

import cn.novalon.gym.manage.sys.core.domain.SysRole;
import cn.novalon.gym.manage.sys.core.repository.ISysRoleRepository;
import cn.novalon.gym.manage.sys.core.query.SysRoleQuery;
import cn.novalon.gym.manage.common.dto.PageRequest;
import cn.novalon.gym.manage.common.dto.PageResponse;
import cn.novalon.gym.manage.db.converter.SysRoleConverter;
import cn.novalon.gym.manage.db.dao.SysRoleDao;
import cn.novalon.gym.manage.db.dao.QueryUtil;
import cn.novalon.gym.manage.db.entity.SysRoleEntity;
import cn.novalon.gym.manage.db.entity.query.SysRoleQueryCriteria;
import org.springframework.data.domain.Sort;
import org.springframework.data.r2dbc.core.R2dbcEntityTemplate;
import org.springframework.data.relational.core.query.Query;
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 SysRoleRepository implements ISysRoleRepository {

    private final SysRoleDao sysRoleDao;
    private final SysRoleConverter sysRoleConverter;
    private final R2dbcEntityTemplate r2dbcEntityTemplate;

    public SysRoleRepository(SysRoleDao sysRoleDao, SysRoleConverter sysRoleConverter, R2dbcEntityTemplate r2dbcEntityTemplate) {
        this.sysRoleDao = sysRoleDao;
        this.sysRoleConverter = sysRoleConverter;
        this.r2dbcEntityTemplate = r2dbcEntityTemplate;
    }

    @Override
    public Mono<SysRole> findById(Long id) {
        return sysRoleDao.findByIdAndDeletedAtIsNull(id)
                .map(sysRoleConverter::toDomain);
    }

    @Override
    public Mono<SysRole> findByIdIncludingDeleted(Long id) {
        return sysRoleDao.findById(id)
                .map(sysRoleConverter::toDomain);
    }

    @Override
    public Mono<SysRole> save(SysRole sysRole) {
        SysRoleEntity entity = sysRoleConverter.toEntity(sysRole);
        if (entity.isNew()) {
            return r2dbcEntityTemplate.insert(SysRoleEntity.class)
                    .using(entity)
                    .doOnNext(e -> e.markNotNew())
                    .map(sysRoleConverter::toDomain);
        }
        return sysRoleDao.save(entity)
                .map(sysRoleConverter::toDomain);
    }

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

    @Override
    public Flux<SysRole> findAll() {
        return sysRoleDao.findByDeletedAtIsNull()
                .map(sysRoleConverter::toDomain);
    }

    @Override
    public Flux<SysRole> findAll(Sort sort) {
        return sysRoleDao.findByDeletedAtIsNull(sort)
                .map(sysRoleConverter::toDomain);
    }

    @Override
    public Flux<SysRole> findByRoleNameLikeOrRoleKeyLike(String roleName, String roleKey, Sort sort) {
        SysRoleQueryCriteria criteria = new SysRoleQueryCriteria();
        criteria.setRoleName(roleName);
        criteria.setRoleKey(roleKey);
        
        Query dbQuery = QueryUtil.getQuery(criteria);
        
        if (sort != null && sort.isSorted()) {
            dbQuery = dbQuery.sort(sort);
        }
        
        return r2dbcEntityTemplate.select(SysRoleEntity.class)
                .matching(dbQuery)
                .all()
                .map(sysRoleConverter::toDomain);
    }

    @Override
    public Mono<Long> count() {
        return sysRoleDao.countByDeletedAtIsNull();
    }

    @Override
    public Mono<Long> countByRoleNameLikeOrRoleKeyLike(String roleName, String roleKey) {
        SysRoleQueryCriteria criteria = new SysRoleQueryCriteria();
        criteria.setRoleName(roleName);
        criteria.setRoleKey(roleKey);
        
        Query dbQuery = QueryUtil.getQuery(criteria);
        
        return r2dbcEntityTemplate.count(dbQuery, SysRoleEntity.class);
    }

    @Override
    public Mono<PageResponse<SysRole>> findByQueryWithPagination(SysRoleQuery query, 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);

        SysRoleQueryCriteria criteria = new SysRoleQueryCriteria();
        criteria.convert(query);
        Query dbQuery = QueryUtil.getQuery(criteria);

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

    @Override
    public Mono<SysRole> findByRoleName(String roleName) {
        return sysRoleDao.findByRoleNameAndDeletedAtIsNull(roleName)
                .map(sysRoleConverter::toDomain);
    }

    @Override
    public Mono<Boolean> existsByRoleName(String roleName) {
        return sysRoleDao.existsByRoleNameAndDeletedAtIsNull(roleName);
    }

    @Override
    public Mono<SysRole> updateRole(SysRole role) {
        SysRoleEntity entity = sysRoleConverter.toEntity(role);
        entity.markNotNew();
        return sysRoleDao.save(entity)
                .map(sysRoleConverter::toDomain);
    }
}