package cn.novalon.manage.db.repository;

import cn.novalon.manage.sys.core.domain.SysLoginLog;
import cn.novalon.manage.sys.core.repository.ISysLoginLogRepository;
import cn.novalon.manage.common.dto.PageRequest;
import cn.novalon.manage.common.dto.PageResponse;
import cn.novalon.manage.db.converter.SysLoginLogConverter;
import cn.novalon.manage.db.dao.SysLoginLogDao;
import cn.novalon.manage.db.dao.QueryUtil;
import cn.novalon.manage.db.entity.SysLoginLogEntity;
import cn.novalon.manage.db.entity.query.SysLoginLogQueryCriteria;
import org.springframework.data.domain.Sort;
import org.springframework.data.r2dbc.core.R2dbcEntityTemplate;
import org.springframework.data.relational.core.query.Criteria;
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.time.LocalDateTime;
import java.util.List;

/**
 * 登录日志仓储实现类
 * 
 * @author 张翔
 * @date 2026-03-13
 */
@Repository
public class SysLoginLogRepository implements ISysLoginLogRepository {

    private final SysLoginLogDao sysLoginLogDao;
    private final SysLoginLogConverter sysLoginLogConverter;
    private final R2dbcEntityTemplate r2dbcEntityTemplate;

    public SysLoginLogRepository(SysLoginLogDao sysLoginLogDao, SysLoginLogConverter sysLoginLogConverter, R2dbcEntityTemplate r2dbcEntityTemplate) {
        this.sysLoginLogDao = sysLoginLogDao;
        this.sysLoginLogConverter = sysLoginLogConverter;
        this.r2dbcEntityTemplate = r2dbcEntityTemplate;
    }

    @Override
    public Flux<SysLoginLog> findAllByOrderByLoginTimeDesc() {
        return sysLoginLogDao.findAllByOrderByLoginTimeDesc()
                .map(sysLoginLogConverter::toDomain);
    }

    @Override
    public Flux<SysLoginLog> findByUsernameOrderByLoginTimeDesc(String username) {
        SysLoginLogQueryCriteria criteria = new SysLoginLogQueryCriteria();
        criteria.setUsername(username);
        
        Query dbQuery = QueryUtil.getQuery(criteria);
        Sort sort = Sort.by(Sort.Direction.DESC, "loginTime");
        dbQuery = dbQuery.sort(sort);
        
        return r2dbcEntityTemplate.select(SysLoginLogEntity.class)
                .matching(dbQuery)
                .all()
                .map(sysLoginLogConverter::toDomain);
    }

    @Override
    public Flux<SysLoginLog> findByLoginTimeBetweenOrderByLoginTimeDesc(LocalDateTime startTime, LocalDateTime endTime) {
        Criteria criteria = Criteria.where("loginTime").between(startTime, endTime);
        Query dbQuery = Query.query(criteria);
        Sort sort = Sort.by(Sort.Direction.DESC, "loginTime");
        dbQuery = dbQuery.sort(sort);
        
        return r2dbcEntityTemplate.select(SysLoginLogEntity.class)
                .matching(dbQuery)
                .all()
                .map(sysLoginLogConverter::toDomain);
    }

    @Override
    public Mono<SysLoginLog> save(SysLoginLog loginLog) {
        SysLoginLogEntity entity = sysLoginLogConverter.toEntity(loginLog);
        return sysLoginLogDao.save(entity)
                .map(sysLoginLogConverter::toDomain);
    }

    @Override
    public Mono<SysLoginLog> findById(Long id) {
        return sysLoginLogDao.findById(id)
                .map(sysLoginLogConverter::toDomain);
    }

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

    @Override
    public Mono<Long> countToday() {
        LocalDateTime todayStart = LocalDateTime.now().withHour(0).withMinute(0).withSecond(0).withNano(0);
        LocalDateTime todayEnd = todayStart.plusDays(1);
        return findByLoginTimeBetweenOrderByLoginTimeDesc(todayStart, todayEnd).count();
    }

    @Override
    public Mono<PageResponse<SysLoginLog>> findLoginLogsByPage(PageRequest pageRequest) {
        int page = pageRequest.getPage();
        int size = pageRequest.getSize();
        String sort = pageRequest.getSort();
        String order = pageRequest.getOrder();
        String keyword = pageRequest.getKeyword();

        SysLoginLogQueryCriteria criteria = new SysLoginLogQueryCriteria();
        
        if (keyword != null && !keyword.isEmpty()) {
            criteria.setKeyword(keyword);
        }

        Query queryObj = QueryUtil.getQuery(criteria);

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

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

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