package com.stdproject.controller;

import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.baomidou.mybatisplus.core.metadata.IPage;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.stdproject.common.OperationLog;
import com.stdproject.common.PageRequest;
import com.stdproject.common.Result;
import com.stdproject.entity.AppMenu;
import com.stdproject.service.IAppMenuService;
import io.swagger.v3.oas.annotations.Operation;
import io.swagger.v3.oas.annotations.Parameter;
import io.swagger.v3.oas.annotations.tags.Tag;
import jakarta.validation.Valid;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.util.StringUtils;
import org.springframework.web.bind.annotation.*;

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

/**
 * <p>
 * 应用-菜单表 前端控制器
 * </p>
 *
 * @author StdProject
 * @since 2023-12-07
 */
@Tag(name = "菜单管理", description = "菜单添加、菜单修改、菜单删除、菜单查询等功能")
@RestController
@RequestMapping("/app-menu")
public class AppMenuController {

    @Autowired
    private IAppMenuService appMenuService;

    @Operation(summary = "分页查询菜单列表")
    @PostMapping("/page")
    @OperationLog(type = "06", module = "菜单管理", description = "分页查询菜单列表")
    public Result<IPage<AppMenu>> page(@RequestBody @Valid PageRequest pageRequest) {
        Page<AppMenu> page = new Page<>(pageRequest.getCurrent(), pageRequest.getSize());
        QueryWrapper<AppMenu> queryWrapper = new QueryWrapper<>();
        
        // 关键字搜索
        if (StringUtils.hasText(pageRequest.getKeyword())) {
            queryWrapper.and(wrapper -> wrapper
                .like("name", pageRequest.getKeyword())
                .or().like("code", pageRequest.getKeyword())
                .or().like("url", pageRequest.getKeyword())
            );
        }
        
        // 排序
        if (StringUtils.hasText(pageRequest.getOrderBy())) {
            if ("asc".equalsIgnoreCase(pageRequest.getOrderDirection())) {
                queryWrapper.orderByAsc(pageRequest.getOrderBy());
            } else {
                queryWrapper.orderByDesc(pageRequest.getOrderBy());
            }
        } else {
            queryWrapper.orderByAsc("parentid", "orderno");
        }
        
        IPage<AppMenu> result = appMenuService.page(page, queryWrapper);
        return Result.success(result);
    }

    @Operation(summary = "查询所有菜单列表")
    @GetMapping("/list")
    @OperationLog(type = "06", module = "菜单管理", description = "查询所有菜单列表")
    public Result<List<AppMenu>> list() {
        QueryWrapper<AppMenu> queryWrapper = new QueryWrapper<>();
        queryWrapper.orderByAsc("parentid", "orderno");
        
        List<AppMenu> menus = appMenuService.list(queryWrapper);
        return Result.success(menus);
    }

    @Operation(summary = "获取菜单树形结构")
    @GetMapping("/tree")
    @OperationLog(type = "06", module = "菜单管理", description = "获取菜单树形结构")
    public Result<List<MenuTreeNode>> getMenuTree() {
        QueryWrapper<AppMenu> queryWrapper = new QueryWrapper<>();
        queryWrapper.orderByAsc("parentid", "orderno");
        
        List<AppMenu> allMenus = appMenuService.list(queryWrapper);
        List<MenuTreeNode> tree = buildMenuTree(allMenus, "0");
        return Result.success(tree);
    }

    @Operation(summary = "根据ID查询菜单详情")
    @GetMapping("/{id}")
    @OperationLog(type = "06", module = "菜单管理", description = "查询菜单详情")
    public Result<AppMenu> getById(@Parameter(description = "菜单ID") @PathVariable String id) {
        AppMenu menu = appMenuService.getById(id);
        return Result.success(menu);
    }

    @Operation(summary = "根据父级ID查询子菜单")
    @GetMapping("/children/{parentId}")
    @OperationLog(type = "06", module = "菜单管理", description = "根据父级ID查询子菜单")
    public Result<List<AppMenu>> getChildren(@Parameter(description = "父级菜单ID") @PathVariable String parentId) {
        QueryWrapper<AppMenu> queryWrapper = new QueryWrapper<>();
        queryWrapper.eq("parentid", parentId);
        queryWrapper.orderByAsc("orderno");
        
        List<AppMenu> children = appMenuService.list(queryWrapper);
        return Result.success(children);
    }

    @Operation(summary = "新增菜单")
    @PostMapping
    @OperationLog(type = "01", module = "菜单管理", description = "新增菜单")
    public Result<String> save(@RequestBody @Valid AppMenu appMenu) {
        // 检查菜单编号是否已存在
        QueryWrapper<AppMenu> queryWrapper = new QueryWrapper<>();
        queryWrapper.eq("code", appMenu.getCode());
        AppMenu existMenu = appMenuService.getOne(queryWrapper);
        if (existMenu != null) {
            return Result.error("菜单编号已存在");
        }
        
        // 检查父级菜单是否存在（如果不是顶级菜单）
        if (!"0".equals(appMenu.getParentid())) {
            AppMenu parentMenu = appMenuService.getById(appMenu.getParentid());
            if (parentMenu == null) {
                return Result.error("父级菜单不存在");
            }
        }
        
        // 设置默认值
        if (appMenu.getOrderno() == null) {
            // 获取同级菜单的最大序号
            QueryWrapper<AppMenu> orderQuery = new QueryWrapper<>();
            orderQuery.eq("parentid", appMenu.getParentid());
            orderQuery.orderByDesc("orderno");
            orderQuery.last("LIMIT 1");
            AppMenu lastMenu = appMenuService.getOne(orderQuery);
            appMenu.setOrderno(lastMenu != null ? lastMenu.getOrderno() + 1 : 1);
        }
        
        if (!StringUtils.hasText(appMenu.getIsdisplay())) {
            appMenu.setIsdisplay("1");
        }
        
        if (!StringUtils.hasText(appMenu.getIslink())) {
            appMenu.setIslink("0");
        }
        
        if (!StringUtils.hasText(appMenu.getType())) {
            appMenu.setType("01");
        }
        
        appMenu.setLastmodifydate(LocalDateTime.now());
        boolean success = appMenuService.save(appMenu);
        return success ? Result.success("新增成功") : Result.error("新增失败");
    }

    @Operation(summary = "修改菜单")
    @PutMapping
    @OperationLog(type = "02", module = "菜单管理", description = "修改菜单")
    public Result<String> update(@RequestBody @Valid AppMenu appMenu) {
        // 检查菜单是否存在
        AppMenu existMenu = appMenuService.getById(appMenu.getId());
        if (existMenu == null) {
            return Result.error("菜单不存在");
        }
        
        // 如果修改了菜单编号，检查新编号是否已被其他菜单使用
        if (!existMenu.getCode().equals(appMenu.getCode())) {
            QueryWrapper<AppMenu> queryWrapper = new QueryWrapper<>();
            queryWrapper.eq("code", appMenu.getCode());
            queryWrapper.ne("id", appMenu.getId());
            AppMenu menuWithSameCode = appMenuService.getOne(queryWrapper);
            if (menuWithSameCode != null) {
                return Result.error("菜单编号已被其他菜单使用");
            }
        }
        
        // 如果修改了父级菜单，检查是否会形成循环引用
        if (!existMenu.getParentid().equals(appMenu.getParentid())) {
            if (isCircularReference(appMenu.getId(), appMenu.getParentid())) {
                return Result.error("不能将菜单移动到自己的子菜单下");
            }
            
            // 检查父级菜单是否存在（如果不是顶级菜单）
            if (!"0".equals(appMenu.getParentid())) {
                AppMenu parentMenu = appMenuService.getById(appMenu.getParentid());
                if (parentMenu == null) {
                    return Result.error("父级菜单不存在");
                }
            }
        }
        
        appMenu.setLastmodifydate(LocalDateTime.now());
        boolean success = appMenuService.updateById(appMenu);
        return success ? Result.success("修改成功") : Result.error("修改失败");
    }

    @Operation(summary = "删除菜单")
    @DeleteMapping("/{id}")
    @OperationLog(type = "03", module = "菜单管理", description = "删除菜单")
    public Result<String> delete(@Parameter(description = "菜单ID") @PathVariable String id) {
        // 检查是否有子菜单
        QueryWrapper<AppMenu> childQuery = new QueryWrapper<>();
        childQuery.eq("parentid", id);
        long childCount = appMenuService.count(childQuery);
        if (childCount > 0) {
            return Result.error("存在子菜单，无法删除");
        }
        
        boolean success = appMenuService.removeById(id);
        return success ? Result.success("删除成功") : Result.error("删除失败");
    }

    @Operation(summary = "批量删除菜单")
    @DeleteMapping("/batch")
    @OperationLog(type = "03", module = "菜单管理", description = "批量删除菜单")
    public Result<String> deleteBatch(@RequestBody List<String> ids) {
        for (String id : ids) {
            // 检查是否有子菜单
            QueryWrapper<AppMenu> childQuery = new QueryWrapper<>();
            childQuery.eq("parentid", id);
            long childCount = appMenuService.count(childQuery);
            if (childCount > 0) {
                return Result.error("存在子菜单的菜单无法删除");
            }
        }
        
        boolean success = appMenuService.removeByIds(ids);
        return success ? Result.success("批量删除成功") : Result.error("批量删除失败");
    }

    @Operation(summary = "根据菜单类型查询菜单列表")
    @GetMapping("/type/{type}")
    @OperationLog(type = "06", module = "菜单管理", description = "根据菜单类型查询菜单列表")
    public Result<List<AppMenu>> getByType(@Parameter(description = "菜单类型") @PathVariable String type) {
        QueryWrapper<AppMenu> queryWrapper = new QueryWrapper<>();
        queryWrapper.eq("type", type);
        queryWrapper.orderByAsc("parentid", "orderno");
        
        List<AppMenu> menus = appMenuService.list(queryWrapper);
        return Result.success(menus);
    }

    @Operation(summary = "获取显示的菜单树")
    @GetMapping("/display-tree")
    @OperationLog(type = "06", module = "菜单管理", description = "获取显示的菜单树")
    public Result<List<MenuTreeNode>> getDisplayMenuTree() {
        QueryWrapper<AppMenu> queryWrapper = new QueryWrapper<>();
        queryWrapper.eq("isdisplay", "1");
        queryWrapper.orderByAsc("parentid", "orderno");
        
        List<AppMenu> displayMenus = appMenuService.list(queryWrapper);
        List<MenuTreeNode> tree = buildMenuTree(displayMenus, "0");
        return Result.success(tree);
    }

    @Operation(summary = "调整菜单顺序")
    @PutMapping("/reorder")
    @OperationLog(type = "02", module = "菜单管理", description = "调整菜单顺序")
    public Result<String> reorder(@RequestBody List<MenuOrderRequest> orderRequests) {
        for (MenuOrderRequest request : orderRequests) {
            AppMenu menu = appMenuService.getById(request.getId());
            if (menu != null) {
                menu.setOrderno(request.getOrderno());
                menu.setLastmodifydate(LocalDateTime.now());
                appMenuService.updateById(menu);
            }
        }
        return Result.success("顺序调整成功");
    }

    /**
     * 构建菜单树
     */
    private List<MenuTreeNode> buildMenuTree(List<AppMenu> allMenus, String parentId) {
        return allMenus.stream()
            .filter(menu -> parentId.equals(menu.getParentid()))
            .map(menu -> {
                MenuTreeNode node = new MenuTreeNode();
                node.setId(menu.getId());
                node.setCode(menu.getCode());
                node.setName(menu.getName());
                node.setIcon(menu.getIcon());
                node.setUrl(menu.getUrl());
                node.setType(menu.getType());
                node.setIslink(menu.getIslink());
                node.setIsdisplay(menu.getIsdisplay());
                node.setOrderno(menu.getOrderno());
                node.setParentid(menu.getParentid());
                node.setModuleId(menu.getModuleId());
                node.setChildren(buildMenuTree(allMenus, menu.getId()));
                return node;
            })
            .collect(Collectors.toList());
    }

    /**
     * 检查是否会形成循环引用
     */
    private boolean isCircularReference(String menuId, String newParentId) {
        if ("0".equals(newParentId)) {
            return false;
        }
        
        if (menuId.equals(newParentId)) {
            return true;
        }
        
        AppMenu parentMenu = appMenuService.getById(newParentId);
        if (parentMenu == null) {
            return false;
        }
        
        return isCircularReference(menuId, parentMenu.getParentid());
    }

    /**
     * 菜单树节点
     */
    public static class MenuTreeNode {
        private String id;
        private String code;
        private String name;
        private String icon;
        private String url;
        private String type;
        private String islink;
        private String isdisplay;
        private Integer orderno;
        private String parentid;
        private String moduleId;
        private List<MenuTreeNode> children = new ArrayList<>();

        // Getters and Setters
        public String getId() { return id; }
        public void setId(String id) { this.id = id; }
        public String getCode() { return code; }
        public void setCode(String code) { this.code = code; }
        public String getName() { return name; }
        public void setName(String name) { this.name = name; }
        public String getIcon() { return icon; }
        public void setIcon(String icon) { this.icon = icon; }
        public String getUrl() { return url; }
        public void setUrl(String url) { this.url = url; }
        public String getType() { return type; }
        public void setType(String type) { this.type = type; }
        public String getIslink() { return islink; }
        public void setIslink(String islink) { this.islink = islink; }
        public String getIsdisplay() { return isdisplay; }
        public void setIsdisplay(String isdisplay) { this.isdisplay = isdisplay; }
        public Integer getOrderno() { return orderno; }
        public void setOrderno(Integer orderno) { this.orderno = orderno; }
        public String getParentid() { return parentid; }
        public void setParentid(String parentid) { this.parentid = parentid; }
        public String getModuleId() { return moduleId; }
        public void setModuleId(String moduleId) { this.moduleId = moduleId; }
        public List<MenuTreeNode> getChildren() { return children; }
        public void setChildren(List<MenuTreeNode> children) { this.children = children; }
    }

    /**
     * 菜单排序请求
     */
    public static class MenuOrderRequest {
        private String id;
        private Integer orderno;

        public String getId() { return id; }
        public void setId(String id) { this.id = id; }
        public Integer getOrderno() { return orderno; }
        public void setOrderno(Integer orderno) { this.orderno = orderno; }
    }
}