package com.writeoff.module.system.service;

import com.writeoff.common.api.PageResult;
import com.writeoff.common.exception.BusinessException;
import com.writeoff.module.system.dto.BindRoleMenusRequest;
import com.writeoff.module.system.dto.CreateMenuRequest;
import com.writeoff.module.system.dto.ReorderMenusRequest;
import com.writeoff.module.system.dto.UpdateMenuRequest;
import com.writeoff.module.system.model.MenuInfo;
import com.writeoff.security.AuthContext;
import org.springframework.jdbc.core.JdbcTemplate;
import org.springframework.jdbc.core.RowMapper;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import java.util.LinkedHashSet;
import java.util.List;

@Service
public class MenuService {
    private final JdbcTemplate jdbcTemplate;

    private static final RowMapper<MenuInfo> MENU_ROW_MAPPER = (rs, n) -> new MenuInfo(
            rs.getLong("id"),
            rs.getString("menu_code"),
            rs.getString("menu_name"),
            rs.getString("route_path"),
            rs.getString("permission_code"),
            rs.getInt("sort_no"),
            rs.getString("status")
    );

    public MenuService(JdbcTemplate jdbcTemplate) {
        this.jdbcTemplate = jdbcTemplate;
    }

    public PageResult<MenuInfo> list() {
        List<MenuInfo> list = jdbcTemplate.query(
                "SELECT id, menu_code, menu_name, route_path, permission_code, sort_no, status " +
                        "FROM menu WHERE tenant_id=? AND is_deleted=0 ORDER BY sort_no ASC, id ASC",
                MENU_ROW_MAPPER,
                tenantId()
        );
        return new PageResult<>(list, list.size(), 1, 200);
    }

    public List<MenuInfo> currentUserMenus(Long userId) {
        return jdbcTemplate.query(
                "SELECT DISTINCT m.id, m.menu_code, m.menu_name, m.route_path, m.permission_code, m.sort_no, m.status " +
                        "FROM user_role ur " +
                        "JOIN role_menu rm ON ur.tenant_id=rm.tenant_id AND ur.role_id=rm.role_id " +
                        "JOIN menu m ON rm.tenant_id=m.tenant_id AND rm.menu_id=m.id " +
                        "WHERE ur.tenant_id=? AND ur.user_id=? AND m.is_deleted=0 AND m.status='ENABLED' " +
                        "AND (m.permission_code IS NULL OR EXISTS (" +
                        "   SELECT 1 FROM user_role ur2 " +
                        "   JOIN role_permission rp2 ON ur2.tenant_id=rp2.tenant_id AND ur2.role_id=rp2.role_id " +
                        "   JOIN permission p2 ON rp2.permission_id=p2.id " +
                        "   WHERE ur2.tenant_id=ur.tenant_id AND ur2.user_id=ur.user_id AND p2.permission_code=m.permission_code" +
                        ")) " +
                        "ORDER BY m.sort_no ASC, m.id ASC",
                MENU_ROW_MAPPER,
                tenantId(),
                userId
        );
    }

    public MenuInfo create(CreateMenuRequest request) {
        assertPermissionExists(request.getPermissionCode().trim());
        Integer dup = jdbcTemplate.queryForObject(
                "SELECT COUNT(1) FROM menu WHERE tenant_id=? AND menu_code=?",
                Integer.class,
                tenantId(),
                request.getMenuCode().trim()
        );
        if (dup != null && dup > 0) {
            throw new BusinessException(10001, "菜单编码已存在");
        }
        jdbcTemplate.update(
                "INSERT INTO menu (tenant_id, menu_code, menu_name, route_path, permission_code, sort_no, status, is_deleted, created_by, updated_by) " +
                        "VALUES (?, ?, ?, ?, ?, ?, 'ENABLED', 0, ?, ?)",
                tenantId(),
                request.getMenuCode().trim(),
                request.getMenuName().trim(),
                request.getRoutePath().trim(),
                request.getPermissionCode().trim(),
                request.getSortNo(),
                safeUserId(),
                safeUserId()
        );
        Long id = jdbcTemplate.queryForObject("SELECT IFNULL(MAX(id), 0) FROM menu WHERE tenant_id=?", Long.class, tenantId());
        return findById(id == null ? 0L : id);
    }

    public MenuInfo update(Long id, UpdateMenuRequest request) {
        assertMenuExists(id);
        assertPermissionExists(request.getPermissionCode().trim());
        String status = request.getStatus().trim().toUpperCase();
        if (!"ENABLED".equals(status) && !"DISABLED".equals(status)) {
            throw new BusinessException(10001, "菜单状态非法");
        }
        jdbcTemplate.update(
                "UPDATE menu SET menu_name=?, route_path=?, permission_code=?, sort_no=?, status=?, updated_by=?, updated_at=CURRENT_TIMESTAMP " +
                        "WHERE tenant_id=? AND id=?",
                request.getMenuName().trim(),
                request.getRoutePath().trim(),
                request.getPermissionCode().trim(),
                request.getSortNo(),
                status,
                safeUserId(),
                tenantId(),
                id
        );
        return findById(id);
    }

    public List<Long> getRoleMenuIds(Long roleId) {
        assertRoleExists(roleId);
        return jdbcTemplate.queryForList(
                "SELECT rm.menu_id FROM role_menu rm " +
                        "JOIN menu m ON rm.tenant_id=m.tenant_id AND rm.menu_id=m.id " +
                        "WHERE rm.tenant_id=? AND rm.role_id=? AND m.is_deleted=0 ORDER BY rm.menu_id ASC",
                Long.class,
                tenantId(),
                roleId
        );
    }

    @Transactional
    public void bindRoleMenus(Long roleId, BindRoleMenusRequest request) {
        assertRoleExists(roleId);
        if (request.getMenuIds() == null || request.getMenuIds().isEmpty()) {
            jdbcTemplate.update("DELETE FROM role_menu WHERE tenant_id=? AND role_id=?", tenantId(), roleId);
            return;
        }
        LinkedHashSet<Long> menuIds = new LinkedHashSet<Long>(request.getMenuIds());
        for (Long menuId : menuIds) {
            assertMenuExists(menuId);
        }
        jdbcTemplate.update("DELETE FROM role_menu WHERE tenant_id=? AND role_id=?", tenantId(), roleId);
        Long nextId = jdbcTemplate.queryForObject("SELECT IFNULL(MAX(id), 0) + 1 FROM role_menu", Long.class);
        long currentId = nextId == null ? 1L : nextId;
        for (Long menuId : menuIds) {
            jdbcTemplate.update(
                    "INSERT INTO role_menu (id, tenant_id, role_id, menu_id) VALUES (?, ?, ?, ?)",
                    currentId++,
                    tenantId(),
                    roleId,
                    menuId
            );
        }
    }

    public List<String> getMenuRoleNames(Long menuId) {
        assertMenuExists(menuId);
        return jdbcTemplate.queryForList(
                "SELECT r.role_name FROM role_menu rm " +
                        "JOIN role r ON rm.tenant_id=r.tenant_id AND rm.role_id=r.id " +
                        "WHERE rm.tenant_id=? AND rm.menu_id=? AND r.is_deleted=0 ORDER BY r.id ASC",
                String.class,
                tenantId(),
                menuId
        );
    }

    public void reorderMenus(ReorderMenusRequest request) {
        if (request.getMenus() == null || request.getMenus().isEmpty()) {
            return;
        }
        for (ReorderMenusRequest.MenuSortItem item : request.getMenus()) {
            assertMenuExists(item.getId());
            jdbcTemplate.update(
                    "UPDATE menu SET sort_no=?, updated_by=?, updated_at=CURRENT_TIMESTAMP WHERE tenant_id=? AND id=?",
                    item.getSortNo(),
                    safeUserId(),
                    tenantId(),
                    item.getId()
            );
        }
    }

    private MenuInfo findById(Long id) {
        List<MenuInfo> list = jdbcTemplate.query(
                "SELECT id, menu_code, menu_name, route_path, permission_code, sort_no, status " +
                        "FROM menu WHERE tenant_id=? AND id=? AND is_deleted=0",
                MENU_ROW_MAPPER,
                tenantId(),
                id
        );
        if (list.isEmpty()) {
            throw new BusinessException(10003, "菜单不存在");
        }
        return list.get(0);
    }

    private void assertMenuExists(Long menuId) {
        Integer count = jdbcTemplate.queryForObject(
                "SELECT COUNT(1) FROM menu WHERE tenant_id=? AND id=? AND is_deleted=0",
                Integer.class,
                tenantId(),
                menuId
        );
        if (count == null || count == 0) {
            throw new BusinessException(10003, "菜单不存在");
        }
    }

    private void assertRoleExists(Long roleId) {
        Integer count = jdbcTemplate.queryForObject(
                "SELECT COUNT(1) FROM role WHERE tenant_id=? AND id=? AND is_deleted=0",
                Integer.class,
                tenantId(),
                roleId
        );
        if (count == null || count == 0) {
            throw new BusinessException(10003, "角色不存在");
        }
    }

    private void assertPermissionExists(String permissionCode) {
        Integer count = jdbcTemplate.queryForObject(
                "SELECT COUNT(1) FROM permission WHERE permission_code=?",
                Integer.class,
                permissionCode
        );
        if (count == null || count == 0) {
            throw new BusinessException(10003, "权限码不存在");
        }
    }

    private Long tenantId() {
        return AuthContext.requireTenantId();
    }

    private Long safeUserId() {
        Long userId = AuthContext.userId();
        return userId == null ? 0L : userId;
    }
}
