package cn.novalon.manage.sys.core.service.impl;

import cn.novalon.manage.sys.core.domain.SysLoginLog;
import cn.novalon.manage.sys.core.repository.ISysLoginLogRepository;
import cn.novalon.manage.sys.core.service.ISysLoginLogService;
import cn.novalon.manage.common.dto.PageRequest;
import cn.novalon.manage.common.dto.PageResponse;
import org.springframework.stereotype.Service;
import reactor.core.publisher.Flux;
import reactor.core.publisher.Mono;

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

/**
 * 登录日志服务实现类
 * 
 * @author 张翔
 * @date 2026-03-14
 */
@Service
public class SysLoginLogService implements ISysLoginLogService {

    private final ISysLoginLogRepository repository;

    public SysLoginLogService(ISysLoginLogRepository repository) {
        this.repository = repository;
    }

    @Override
    public Flux<SysLoginLog> findAll() {
        return repository.findAllByOrderByLoginTimeDesc();
    }

    @Override
    public Flux<SysLoginLog> findByUsername(String username) {
        return repository.findByUsernameOrderByLoginTimeDesc(username);
    }

    @Override
    public Flux<SysLoginLog> findByLoginTimeBetween(LocalDateTime startTime, LocalDateTime endTime) {
        return repository.findByLoginTimeBetweenOrderByLoginTimeDesc(startTime, endTime);
    }

    @Override
    public Mono<SysLoginLog> save(SysLoginLog loginLog) {
        return repository.save(loginLog);
    }

    @Override
    public Mono<SysLoginLog> findById(Long id) {
        return repository.findById(id);
    }

    @Override
    public Mono<PageResponse<SysLoginLog>> findLoginLogsByPage(PageRequest pageRequest) {
        Flux<SysLoginLog> allLogs = repository.findAllByOrderByLoginTimeDesc();

        if (pageRequest.getKeyword() != null && !pageRequest.getKeyword().isEmpty()) {
            String keyword = pageRequest.getKeyword().toLowerCase();
            allLogs = allLogs.filter(log -> 
                (log.getUsername() != null && log.getUsername().toLowerCase().contains(keyword)) ||
                (log.getIp() != null && log.getIp().toLowerCase().contains(keyword)) ||
                (log.getMessage() != null && log.getMessage().toLowerCase().contains(keyword))
            );
        }

        return allLogs
                .collectList()
                .flatMap(list -> {
                    List<SysLoginLog> sortedList = new ArrayList<>(list);
                    
                    if (pageRequest.getSort() != null && !pageRequest.getSort().isEmpty()) {
                        sortedList.sort((a, b) -> {
                            int comparison = 0;
                            if ("username".equals(pageRequest.getSort())) {
                                comparison = compareStrings(a.getUsername(), b.getUsername());
                            } else if ("ip".equals(pageRequest.getSort())) {
                                comparison = compareStrings(a.getIp(), b.getIp());
                            } else if ("loginTime".equals(pageRequest.getSort())) {
                                comparison = compareLocalDateTimes(a.getLoginTime(), b.getLoginTime());
                            }
                            return "desc".equalsIgnoreCase(pageRequest.getOrder()) ? -comparison : comparison;
                        });
                    }
                    
                    return Mono.just(sortedList);
                })
                .zipWith(repository.count())
                .map(tuple -> {
                    List<SysLoginLog> all = tuple.getT1();
                    long totalCount = tuple.getT2();
                    int totalPages = (int) Math.ceil((double) totalCount / pageRequest.getSize());
                    
                    int fromIndex = pageRequest.getPage() * pageRequest.getSize();
                    int toIndex = Math.min(fromIndex + pageRequest.getSize(), all.size());
                    
                    List<SysLoginLog> pageData = fromIndex < all.size() 
                            ? all.subList(fromIndex, toIndex) 
                            : List.of();
                    
                    return new PageResponse<SysLoginLog>(
                            pageData,
                            totalPages,
                            totalCount,
                            pageRequest.getPage(),
                            pageRequest.getSize());
                });
    }

    private int compareStrings(String a, String b) {
        if (a == null && b == null) return 0;
        if (a == null) return -1;
        if (b == null) return 1;
        return a.compareTo(b);
    }

    private int compareLocalDateTimes(LocalDateTime a, LocalDateTime b) {
        if (a == null && b == null) return 0;
        if (a == null) return -1;
        if (b == null) return 1;
        return a.compareTo(b);
    }

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

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