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

import cn.novalon.gym.manage.db.converter.SysUserConverter;
import cn.novalon.gym.manage.db.dao.SysUserDao;
import cn.novalon.gym.manage.db.entity.SysUserEntity;
import cn.novalon.gym.manage.db.entity.query.SysUserQueryCriteria;
import cn.novalon.gym.manage.common.dao.QueryUtil;
import cn.novalon.gym.manage.sys.core.domain.SysUser;
import cn.novalon.gym.manage.sys.core.query.SysUserQuery;
import cn.novalon.gym.manage.sys.core.repository.ISysUserRepository;
import cn.novalon.gym.manage.common.dto.PageRequest;
import cn.novalon.gym.manage.common.dto.PageResponse;
import cn.novalon.gym.manage.sys.dto.response.UserResponse;
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;

/**
 * 用户仓储实现类
 * 
 * 文件定义：用户数据访问层的仓储实现，负责用户数据的持久化操作
 * 涉及业务：用户增删改查、分页查询、条件查询、逻辑删除等数据访问业务
 * 算法：使用R2DBC进行响应式数据库操作，支持分页算法、条件查询算法
 * 
 * @author 张翔
 * @date 2026-03-13
 */
@Repository
public class SysUserRepository implements ISysUserRepository {

    private final SysUserDao sysUserDao;
    private final SysUserConverter sysUserConverter;
    private final R2dbcEntityTemplate r2dbcEntityTemplate;

    public SysUserRepository(SysUserDao sysUserDao, SysUserConverter sysUserConverter,
            R2dbcEntityTemplate r2dbcEntityTemplate) {
        this.sysUserDao = sysUserDao;
        this.sysUserConverter = sysUserConverter;
        this.r2dbcEntityTemplate = r2dbcEntityTemplate;
    }

    @Override
    public Mono<SysUser> findByUsername(String username) {
        return sysUserDao.findByUsernameAndDeletedAtIsNull(username)
                .map(sysUserConverter::toDomain);
    }

    @Override
    public Mono<SysUser> findByEmail(String email) {
        return sysUserDao.findByEmailAndDeletedAtIsNull(email)
                .map(sysUserConverter::toDomain);
    }

    @Override
    public Mono<SysUser> findById(Long id) {
        return sysUserDao.findByIdAndDeletedAtIsNull(id)
                .map(sysUserConverter::toDomain);
    }

    @Override
    public Mono<SysUser> findByIdIncludingDeleted(Long id) {
        return sysUserDao.findById(id)
                .map(sysUserConverter::toDomain);
    }

    @Override
    public Mono<SysUser> save(SysUser sysUser) {
        SysUserEntity entity = sysUserConverter.toEntity(sysUser);
        if (entity.isNew()) {
            return r2dbcEntityTemplate.insert(SysUserEntity.class)
                    .using(entity)
                    .doOnNext(e -> e.markNotNew())
                    .map(sysUserConverter::toDomain);
        }
        return sysUserDao.save(entity)
                .map(sysUserConverter::toDomain);
    }

    @Override
    public Mono<SysUser> update(SysUser sysUser) {
        SysUserEntity entity = sysUserConverter.toEntity(sysUser);
        entity.markNotNew();
        return sysUserDao.save(entity)
                .map(sysUserConverter::toDomain);
    }

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

    @Override
    public Flux<SysUser> findAll() {
        return sysUserDao.findAll()
                .map(sysUserConverter::toDomain);
    }

    @Override
    public Flux<SysUser> findAll(Sort sort) {
        return sysUserDao.findAll(sort)
                .map(sysUserConverter::toDomain);
    }

    @Override
    public Flux<SysUser> findByDeletedAtIsNull() {
        return sysUserDao.findByDeletedAtIsNull()
                .map(sysUserConverter::toDomain);
    }

    @Override
    public Flux<SysUser> findByDeletedAtIsNull(Sort sort) {
        return sysUserDao.findByDeletedAtIsNull(sort)
                .map(sysUserConverter::toDomain);
    }

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

    @Override
    public Mono<PageResponse<SysUser>> findByQueryWithPagination(SysUserQuery query, PageRequest pageRequest) {
        int page = pageRequest.getPage();
        int size = pageRequest.getSize();
        String sort = pageRequest.getSort();
        String order = pageRequest.getOrder();
        String keyword = pageRequest.getKeyword();

        System.out.println("=== SysUserRepository.findByQueryWithPagination ===");
        System.out.println("Keyword from pageRequest: " + keyword);

        SysUserQuery sysUserQuery = new SysUserQuery();
        sysUserQuery.setKeyword(keyword);

        SysUserQueryCriteria criteria = new SysUserQueryCriteria();
        criteria.convertWithoutKeyword(sysUserQuery);
        
        if (keyword != null && !keyword.isEmpty()) {
            criteria.setKeyword(keyword);
            System.out.println("Set keyword to criteria: " + keyword);
        }

        System.out.println("Criteria keyword: " + criteria.getKeyword());

        Query queryObj = QueryUtil.getQuery(criteria);
        System.out.println("Generated query: " + queryObj);

        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);

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

    @Override
    public Mono<Boolean> existsByUsername(String username) {
        return sysUserDao.findByUsernameAndDeletedAtIsNull(username)
                .map(user -> true)
                .defaultIfEmpty(false);
    }

    @Override
    public Mono<Boolean> existsByEmail(String email) {
        return sysUserDao.findByEmailAndDeletedAtIsNull(email)
                .map(user -> true)
                .defaultIfEmpty(false);
    }

    @Override
    public Mono<Void> logicalDeleteById(Long id) {
        return sysUserDao.findById(id)
                .flatMap(entity -> {
                    entity.markNotNew();
                    entity.setDeletedAt(java.time.LocalDateTime.now());
                    return sysUserDao.save(entity).then();
                });
    }

    @Override
    public Mono<Void> logicalDeleteByIds(List<Long> ids) {
        return Flux.fromIterable(ids)
                .flatMap(id -> logicalDeleteById(id))
                .then();
    }

    @Override
    public Mono<Void> restoreById(Long id) {
        return sysUserDao.findById(id)
                .flatMap(entity -> {
                    entity.markNotNew();
                    entity.setDeletedAt(null);
                    return sysUserDao.save(entity).then();
                });
    }

    @Override
    public Mono<Void> restoreByIds(List<Long> ids) {
        return Flux.fromIterable(ids)
                .flatMap(id -> restoreById(id))
                .then();
    }

    @Override
    public Mono<Void> updateRoleIdToNullByRoleId(Long roleId) {
        return sysUserDao.findByRoleId(roleId)
                .flatMap(entity -> {
                    entity.setRoleId(null);
                    return sysUserDao.save(entity);
                })
                .then();
    }
}