package com.writeoff.module.system.service;

import com.writeoff.module.system.model.GlobalSearchGroup;
import com.writeoff.module.system.model.GlobalSearchItem;
import com.writeoff.module.system.model.GlobalSearchResult;
import com.writeoff.module.system.model.MenuInfo;
import com.writeoff.security.AuthContext;
import com.writeoff.security.AuthScope;
import com.writeoff.security.PermissionService;
import org.springframework.jdbc.core.JdbcTemplate;
import org.springframework.stereotype.Service;

import java.net.URLEncoder;
import java.nio.charset.StandardCharsets;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import java.util.Locale;
import java.util.Map;

@Service
public class GlobalSearchService {
    private final JdbcTemplate jdbcTemplate;
    private final MenuService menuService;
    private final PlatformMenuService platformMenuService;
    private final PermissionService permissionService;
    private final DataPermissionService dataPermissionService;

    public GlobalSearchService(JdbcTemplate jdbcTemplate,
                               MenuService menuService,
                               PlatformMenuService platformMenuService,
                               PermissionService permissionService,
                               DataPermissionService dataPermissionService) {
        this.jdbcTemplate = jdbcTemplate;
        this.menuService = menuService;
        this.platformMenuService = platformMenuService;
        this.permissionService = permissionService;
        this.dataPermissionService = dataPermissionService;
    }

    public GlobalSearchResult search(String keyword, Integer limitPerType) {
        String normalizedKeyword = normalizeKeyword(keyword);
        int safeLimit = normalizeLimit(limitPerType);
        Long userId = AuthContext.userId();
        if (userId == null || normalizedKeyword.isEmpty()) {
            return new GlobalSearchResult(normalizedKeyword, 0, Collections.<GlobalSearchGroup>emptyList());
        }

        List<GlobalSearchGroup> groups = AuthContext.scope() == AuthScope.PLATFORM
                ? searchPlatform(userId, normalizedKeyword, safeLimit)
                : searchTenant(userId, normalizedKeyword, safeLimit);
        int total = 0;
        for (GlobalSearchGroup group : groups) {
            total += group.getItems() == null ? 0 : group.getItems().size();
        }
        return new GlobalSearchResult(normalizedKeyword, total, groups);
    }

    private List<GlobalSearchGroup> searchTenant(Long userId, String keyword, int limit) {
        List<GlobalSearchGroup> groups = new ArrayList<GlobalSearchGroup>();

        List<GlobalSearchItem> menuItems = searchTenantMenus(userId, keyword, limit);
        if (!menuItems.isEmpty()) {
            groups.add(new GlobalSearchGroup("MENU", "菜单", menuItems));
        }

        if (permissionService.hasPermission(userId, "meeting.read")) {
            List<GlobalSearchItem> meetingItems = searchTenantMeetings(keyword, limit);
            if (!meetingItems.isEmpty()) {
                groups.add(new GlobalSearchGroup("MEETING", "会议", meetingItems));
            }
        }

        if (permissionService.hasPermission(userId, "user.read")) {
            List<GlobalSearchItem> userItems = searchTenantUsers(keyword, limit);
            if (!userItems.isEmpty()) {
                groups.add(new GlobalSearchGroup("USER", "用户", userItems));
            }
        }

        return groups;
    }

    private List<GlobalSearchGroup> searchPlatform(Long userId, String keyword, int limit) {
        List<GlobalSearchGroup> groups = new ArrayList<GlobalSearchGroup>();

        List<GlobalSearchItem> menuItems = searchPlatformMenus(userId, keyword, limit);
        if (!menuItems.isEmpty()) {
            groups.add(new GlobalSearchGroup("MENU", "平台菜单", menuItems));
        }

        if (permissionService.hasPlatformPermission(userId, "platform.user.manage")) {
            List<GlobalSearchItem> userItems = searchPlatformUsers(keyword, limit);
            if (!userItems.isEmpty()) {
                groups.add(new GlobalSearchGroup("USER", "平台用户", userItems));
            }
        }

        return groups;
    }

    private List<GlobalSearchItem> searchTenantMenus(Long userId, String keyword, int limit) {
        List<MenuInfo> menus = menuService.currentUserMenus(userId);
        return buildMenuItems(menus, keyword, limit);
    }

    private List<GlobalSearchItem> searchPlatformMenus(Long userId, String keyword, int limit) {
        List<MenuInfo> menus = platformMenuService.currentUserMenus(userId);
        return buildMenuItems(menus, keyword, limit);
    }

    private List<GlobalSearchItem> buildMenuItems(List<MenuInfo> menus, String keyword, int limit) {
        List<GlobalSearchItem> items = new ArrayList<GlobalSearchItem>();
        if (menus == null || menus.isEmpty()) {
            return items;
        }
        for (MenuInfo menu : menus) {
            if (!matchesKeyword(keyword, menu.getMenuName(), menu.getRoutePath(), menu.getMenuCode())) {
                continue;
            }
            items.add(new GlobalSearchItem(
                    "MENU",
                    safeText(menu.getMenuName()),
                    safeText(normalizeMenuRoutePath(menu.getRoutePath())),
                    normalizeMenuRoutePath(menu.getRoutePath()),
                    null
            ));
            if (items.size() >= limit) {
                break;
            }
        }
        return items;
    }

    private List<GlobalSearchItem> searchTenantMeetings(String keyword, int limit) {
        String like = "%" + keyword + "%";
        List<Map<String, Object>> rows = jdbcTemplate.queryForList(
                "SELECT m.id, m.topic, m.start_time, m.status, m.audit_status, m.project_id, " +
                        "m.created_by AS meeting_created_by, p.created_by AS project_created_by, p.project_name " +
                        "FROM meeting m " +
                        "LEFT JOIN project p ON m.tenant_id=p.tenant_id AND m.project_id=p.id AND p.is_deleted=0 " +
                        "WHERE m.tenant_id=? AND m.is_deleted=0 " +
                        "AND (m.topic LIKE ? OR p.project_name LIKE ? OR CAST(m.id AS CHAR) LIKE ?) " +
                        "ORDER BY m.id DESC LIMIT ?",
                tenantId(),
                like,
                like,
                like,
                limit * 4
        );
        DataPermissionService.DataScope scope = dataPermissionService == null
                ? DataPermissionService.DataScope.allowAll()
                : dataPermissionService.resolveCurrentUserScope();
        List<GlobalSearchItem> items = new ArrayList<GlobalSearchItem>();
        for (Map<String, Object> row : rows) {
            Long meetingId = toLong(row.get("id"));
            Long projectId = toLong(row.get("project_id"));
            Long meetingCreatedBy = toLong(row.get("meeting_created_by"));
            Long projectCreatedBy = toLong(row.get("project_created_by"));
            if (meetingId == null || meetingId <= 0) {
                continue;
            }
            if (dataPermissionService != null && !dataPermissionService.canAccessMeeting(meetingId, projectId, meetingCreatedBy, projectCreatedBy, scope)) {
                continue;
            }
            String topic = safeText(row.get("topic"));
            String projectName = safeText(row.get("project_name"));
            String startTime = safeText(row.get("start_time"));
            String subtitle = joinParts(
                    projectName.isEmpty() ? null : "项目：" + projectName,
                    startTime.isEmpty() ? null : "时间：" + startTime
            );
            items.add(new GlobalSearchItem(
                    "MEETING",
                    topic.isEmpty() ? "会议#" + meetingId : topic,
                    subtitle,
                    "/meetings?meetingId=" + meetingId + "&topic=" + topic,
                    safeText(row.get("status"))
            ));
            if (items.size() >= limit) {
                break;
            }
        }
        return items;
    }

    private List<GlobalSearchItem> searchTenantUsers(String keyword, int limit) {
        String like = "%" + keyword + "%";
        List<Map<String, Object>> rows = jdbcTemplate.queryForList(
                "SELECT u.id, u.user_name, u.phone, u.email, u.status, u.created_by, " +
                        "COALESCE(GROUP_CONCAT(DISTINCT r.role_name ORDER BY r.id SEPARATOR '、'), '') AS role_names " +
                        "FROM sys_user u " +
                        "LEFT JOIN user_role ur ON u.tenant_id=ur.tenant_id AND u.id=ur.user_id " +
                        "LEFT JOIN role r ON ur.tenant_id=r.tenant_id AND ur.role_id=r.id AND r.is_deleted=0 " +
                        "WHERE u.tenant_id=? AND u.is_deleted=0 AND (u.user_name LIKE ? OR u.phone LIKE ? OR u.email LIKE ?) " +
                        "GROUP BY u.id, u.user_name, u.phone, u.email, u.status, u.created_by " +
                        "ORDER BY u.id DESC LIMIT ?",
                tenantId(),
                like,
                like,
                like,
                limit * 4
        );
        DataPermissionService.DataScope scope = dataPermissionService == null
                ? DataPermissionService.DataScope.allowAll()
                : dataPermissionService.resolveCurrentUserScope();
        List<GlobalSearchItem> items = new ArrayList<GlobalSearchItem>();
        for (Map<String, Object> row : rows) {
            Long userId = toLong(row.get("id"));
            Long createdBy = toLong(row.get("created_by"));
            if (userId == null || userId <= 0) {
                continue;
            }
            if (dataPermissionService != null && !dataPermissionService.canAccessUser(userId, createdBy, scope)) {
                continue;
            }
            String userName = safeText(row.get("user_name"));
            String phone = safeText(row.get("phone"));
            String email = safeText(row.get("email"));
            String roleNames = safeText(row.get("role_names"));
            String subtitle = joinParts(
                    phone.isEmpty() ? null : "手机号：" + phone,
                    email.isEmpty() ? null : email,
                    roleNames.isEmpty() ? null : "角色：" + roleNames
            );
            items.add(new GlobalSearchItem(
                    "USER",
                    userName.isEmpty() ? ("用户#" + userId) : userName,
                    subtitle,
                    "/users?keyword=" + resolveUserKeyword(phone, email, userName, userId),
                    safeText(row.get("status"))
            ));
            if (items.size() >= limit) {
                break;
            }
        }
        return items;
    }

    private List<GlobalSearchItem> searchPlatformUsers(String keyword, int limit) {
        String like = "%" + keyword + "%";
        List<Map<String, Object>> rows = jdbcTemplate.queryForList(
                "SELECT id, user_name, phone, email, status " +
                        "FROM platform_user " +
                        "WHERE is_deleted=0 AND (user_name LIKE ? OR phone LIKE ? OR email LIKE ?) " +
                        "ORDER BY id DESC LIMIT ?",
                like,
                like,
                like,
                limit
        );
        List<GlobalSearchItem> items = new ArrayList<GlobalSearchItem>();
        for (Map<String, Object> row : rows) {
            Long userId = toLong(row.get("id"));
            String userName = safeText(row.get("user_name"));
            String phone = safeText(row.get("phone"));
            String email = safeText(row.get("email"));
            String subtitle = joinParts(
                    phone.isEmpty() ? null : "手机号：" + phone,
                    email.isEmpty() ? null : email
            );
            items.add(new GlobalSearchItem(
                    "USER",
                    userName.isEmpty() ? ("平台用户#" + (userId == null ? 0L : userId)) : userName,
                    subtitle,
                    "/platform/users?keyword=" + resolveUserKeyword(phone, email, userName, userId),
                    safeText(row.get("status"))
            ));
        }
        return items;
    }

    private boolean matchesKeyword(String keyword, String... values) {
        if (keyword == null || keyword.isEmpty()) {
            return false;
        }
        String normalizedKeyword = keyword.toLowerCase(Locale.ROOT);
        if (values == null) {
            return false;
        }
        for (String value : values) {
            if (value == null) {
                continue;
            }
            if (value.toLowerCase(Locale.ROOT).contains(normalizedKeyword)) {
                return true;
            }
        }
        return false;
    }

    private String normalizeKeyword(String keyword) {
        return keyword == null ? "" : keyword.trim();
    }

    private int normalizeLimit(Integer limitPerType) {
        if (limitPerType == null) {
            return 5;
        }
        return Math.max(1, Math.min(limitPerType, 10));
    }

    private String normalizeMenuRoutePath(String routePath) {
        String raw = safeText(routePath);
        if ("/permissions".equals(raw)) {
            return "/menus?tab=permissions";
        }
        if ("/platform/permissions".equals(raw)) {
            return "/platform/menus?tab=permissions";
        }
        return raw;
    }

    private String resolveUserKeyword(String phone, String email, String userName, Long userId) {
        if (phone != null && !phone.trim().isEmpty()) {
            return phone.trim();
        }
        if (email != null && !email.trim().isEmpty()) {
            return email.trim();
        }
        if (userName != null && !userName.trim().isEmpty()) {
            return userName.trim();
        }
        return String.valueOf(userId == null ? 0L : userId);
    }

    private String joinParts(String... parts) {
        StringBuilder builder = new StringBuilder();
        if (parts == null) {
            return "";
        }
        for (String part : parts) {
            if (part == null || part.trim().isEmpty()) {
                continue;
            }
            if (builder.length() > 0) {
                builder.append(" | ");
            }
            builder.append(part.trim());
        }
        return builder.toString();
    }

    private String safeText(Object value) {
        return value == null ? "" : String.valueOf(value).trim();
    }

    private Long toLong(Object value) {
        if (value == null) {
            return null;
        }
        if (value instanceof Number) {
            return ((Number) value).longValue();
        }
        try {
            return Long.valueOf(String.valueOf(value).trim());
        } catch (Exception ex) {
            return null;
        }
    }

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