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

import cn.novalon.manage.sys.core.domain.SysExceptionLog;
import cn.novalon.manage.sys.core.repository.ISysExceptionLogRepository;
import cn.novalon.manage.sys.core.service.ISysExceptionLogService;
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.List;

/**
 * 异常日志服务实现类
 * 
 * @author 张翔
 * @date 2026-03-14
 */
@Service
public class SysExceptionLogService implements ISysExceptionLogService {

    private final ISysExceptionLogRepository repository;

    public SysExceptionLogService(ISysExceptionLogRepository repository) {
        this.repository = repository;
    }

    @Override
    public Flux<SysExceptionLog> findAll() {
        return repository.findAllByOrderByCreateTimeDesc();
    }

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

    @Override
    public Flux<SysExceptionLog> findByCreateTimeBetween(LocalDateTime startTime, LocalDateTime endTime) {
        return repository.findByCreateTimeBetweenOrderByCreateTimeDesc(startTime, endTime);
    }

    @Override
    public Mono<SysExceptionLog> save(SysExceptionLog exceptionLog) {
        return repository.save(exceptionLog);
    }

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

    @Override
    public Mono<PageResponse<SysExceptionLog>> findExceptionLogsByPage(PageRequest pageRequest) {
        Flux<SysExceptionLog> allLogs = repository.findAllByOrderByCreateTimeDesc();

        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.getTitle() != null && log.getTitle().toLowerCase().contains(keyword)) ||
                            (log.getExceptionName() != null && log.getExceptionName().toLowerCase().contains(keyword)));
        }

        return allLogs
                .collectList()
                .map(list -> {
                    if (pageRequest.getSort() != null && !pageRequest.getSort().isEmpty()) {
                        list.sort((a, b) -> {
                            int comparison = 0;
                            if ("username".equals(pageRequest.getSort())) {
                                comparison = compareStrings(a.getUsername(), b.getUsername());
                            } else if ("title".equals(pageRequest.getSort())) {
                                comparison = compareStrings(a.getTitle(), b.getTitle());
                            } else if ("createTime".equals(pageRequest.getSort())) {
                                comparison = compareLocalDateTimes(a.getCreateTime(), b.getCreateTime());
                            }
                            return "desc".equalsIgnoreCase(pageRequest.getOrder()) ? -comparison : comparison;
                        });
                    }
                    return list;
                })
                .zipWith(repository.count())
                .map(tuple -> {
                    List<SysExceptionLog> 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<SysExceptionLog> pageData = fromIndex < all.size()
                            ? all.subList(fromIndex, toIndex)
                            : List.of();

                    return new PageResponse<SysExceptionLog>(
                            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();
    }
}
