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

import cn.novalon.gym.manage.sys.core.domain.SysMenu;
import cn.novalon.gym.manage.sys.core.repository.ISysMenuRepository;
import cn.novalon.gym.manage.sys.core.service.ISysMenuService;
import cn.novalon.gym.manage.sys.core.command.CreateMenuCommand;
import cn.novalon.gym.manage.sys.core.command.UpdateMenuCommand;
import cn.novalon.gym.manage.common.util.StatusConstants;
import cn.novalon.gym.manage.sys.audit.AuditLogHelper;
import cn.novalon.gym.manage.sys.audit.service.IAuditLogService;
import org.springframework.stereotype.Service;
import reactor.core.publisher.Flux;
import reactor.core.publisher.Mono;

import java.time.LocalDateTime;
import java.util.List;
import java.util.stream.Collectors;

/**
 * 系统菜单服务实现类
 * 
 * @author 张翔
 * @date 2026-03-14
 */
@Service
public class SysMenuService implements ISysMenuService {

    private final ISysMenuRepository menuRepository;
    private final IAuditLogService auditLogService;

    public SysMenuService(ISysMenuRepository menuRepository, IAuditLogService auditLogService) {
        this.menuRepository = menuRepository;
        this.auditLogService = auditLogService;
    }

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

    @Override
    public Flux<SysMenu> findAll() {
        return menuRepository.findAll();
    }

    @Override
    public Flux<SysMenu> findByParentId(Long parentId) {
        return menuRepository.findByParentId(parentId);
    }

    @Override
    public Mono<SysMenu> createMenu(SysMenu menu) {
        return menuRepository.save(menu)
                .flatMap(saved -> AuditLogHelper.record(auditLogService, "Menu", saved.getId(), "CREATE", saved)
                        .thenReturn(saved));
    }

    @Override
    public Mono<SysMenu> createMenu(CreateMenuCommand command) {
        SysMenu menu = new SysMenu();
        menu.setParentId(command.parentId());
        menu.setMenuName(command.menuName());
        menu.setMenuType(command.menuType());
        menu.setOrderNum(command.orderNum());
        menu.setComponent(command.component());
        menu.setPerms(command.perms());
        menu.setStatus(command.status() != null ? command.status() : StatusConstants.ENABLED);
        return menuRepository.save(menu)
                .flatMap(saved -> AuditLogHelper.record(auditLogService, "Menu", saved.getId(), "CREATE", saved)
                        .thenReturn(saved));
    }

    @Override
    public Mono<SysMenu> updateMenu(SysMenu menu) {
        menu.setUpdatedAt(LocalDateTime.now());
        return menuRepository.findById(menu.getId())
                .flatMap(before -> menuRepository.update(menu)
                        .flatMap(saved -> AuditLogHelper.record(auditLogService, "Menu", saved.getId(), "UPDATE", before, saved)
                                .thenReturn(saved)));
    }

    @Override
    public Mono<SysMenu> updateMenu(UpdateMenuCommand command) {
        return menuRepository.findById(command.id())
                .switchIfEmpty(Mono.error(new RuntimeException("Menu not found")))
                .flatMap(menu -> {
                    SysMenu before = new SysMenu();
                    before.setId(menu.getId());
                    before.setParentId(menu.getParentId());
                    before.setMenuName(menu.getMenuName());
                    before.setMenuType(menu.getMenuType());
                    before.setOrderNum(menu.getOrderNum());
                    before.setComponent(menu.getComponent());
                    before.setPerms(menu.getPerms());
                    before.setStatus(menu.getStatus());
                    if (command.parentId() != null) {
                        menu.setParentId(command.parentId());
                    }
                    if (command.menuName() != null) {
                        menu.setMenuName(command.menuName());
                    }
                    if (command.menuType() != null) {
                        menu.setMenuType(command.menuType());
                    }
                    if (command.orderNum() != null) {
                        menu.setOrderNum(command.orderNum());
                    }
                    if (command.component() != null) {
                        menu.setComponent(command.component());
                    }
                    if (command.perms() != null) {
                        menu.setPerms(command.perms());
                    }
                    if (command.status() != null) {
                        menu.setStatus(command.status());
                    }
                    menu.setUpdatedAt(LocalDateTime.now());
                    return menuRepository.update(menu)
                            .flatMap(saved -> AuditLogHelper.record(auditLogService, "Menu", saved.getId(), "UPDATE", before, saved)
                                    .thenReturn(saved));
                });
    }

    @Override
    public Mono<Void> deleteMenu(Long id) {
        return menuRepository.findById(id)
                .flatMap(menu -> menuRepository.deleteById(id)
                        .then(AuditLogHelper.record(auditLogService, "Menu", id, "DELETE", menu, null)))
                .then();
    }

    @Override
    public Flux<SysMenu> buildMenuTree(Flux<SysMenu> menus) {
        return menus.collectList()
                .map(list -> buildTree(list, 0L))
                .flatMapMany(Flux::fromIterable);
    }

    private List<SysMenu> buildTree(List<SysMenu> menus, Long parentId) {
        return menus.stream()
                .filter(m -> m.getParentId().equals(parentId))
                .peek(m -> m.setChildren(buildTree(menus, m.getId())))
                .collect(Collectors.toList());
    }
}
