package cn.nianxx.thhotel.platform.identity.service.impl;

import cn.nianxx.thhotel.platform.access.common.enums.PlatformRoleStatus;
import cn.nianxx.thhotel.platform.access.domain.PlatformRoleEntity;
import cn.nianxx.thhotel.platform.access.domain.PlatformUserRoleEntity;
import cn.nianxx.thhotel.platform.access.repository.PlatformAccessRepository;
import cn.nianxx.thhotel.platform.audit.common.dto.PlatformAdminAuditLogDraft;
import cn.nianxx.thhotel.platform.audit.service.PlatformAdminAuditLogService;
import cn.nianxx.thhotel.platform.common.dto.PlatformPageSnapshot;
import cn.nianxx.thhotel.platform.common.exception.AdminOperationException;
import cn.nianxx.thhotel.platform.common.result.PlatformPageResult;
import cn.nianxx.thhotel.platform.common.result.PlatformPaginationResult;
import cn.nianxx.thhotel.platform.common.time.UtcTimeFormatter;
import cn.nianxx.thhotel.platform.hotel.common.enums.PlatformHotelStatus;
import cn.nianxx.thhotel.platform.hotel.domain.PlatformHotelEntity;
import cn.nianxx.thhotel.platform.hotel.domain.PlatformUserHotelEntity;
import cn.nianxx.thhotel.platform.hotel.repository.PlatformHotelRepository;
import cn.nianxx.thhotel.platform.identity.common.enums.PlatformUserStatus;
import cn.nianxx.thhotel.platform.identity.common.request.AdminUserCreateRequest;
import cn.nianxx.thhotel.platform.identity.common.request.AdminUserHotelAssignmentRequest;
import cn.nianxx.thhotel.platform.identity.common.request.AdminUserPasswordResetRequest;
import cn.nianxx.thhotel.platform.identity.common.request.AdminUserRoleAssignmentRequest;
import cn.nianxx.thhotel.platform.identity.common.request.AdminUserUpdateRequest;
import cn.nianxx.thhotel.platform.identity.common.result.AdminUserDetailResult;
import cn.nianxx.thhotel.platform.identity.common.result.AdminUserHotelResult;
import cn.nianxx.thhotel.platform.identity.common.result.AdminUserListItemResult;
import cn.nianxx.thhotel.platform.identity.common.result.AdminUserPasswordResetResult;
import cn.nianxx.thhotel.platform.identity.common.result.AdminUserRoleResult;
import cn.nianxx.thhotel.platform.identity.domain.PlatformUserEntity;
import cn.nianxx.thhotel.platform.identity.repository.PlatformIdentityRepository;
import cn.nianxx.thhotel.platform.identity.service.AdminUserManagementService;
import cn.nianxx.thhotel.platform.security.common.dto.AuthenticatedUserContext;
import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.databind.ObjectMapper;
import java.security.SecureRandom;
import java.time.LocalDateTime;
import java.time.ZoneOffset;
import java.util.ArrayList;
import java.util.Collections;
import java.util.LinkedHashMap;
import java.util.LinkedHashSet;
import java.util.List;
import java.util.Map;
import java.util.Objects;
import java.util.Set;
import java.util.stream.Collectors;
import org.springframework.http.HttpStatus;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

/**
 * 管理后台用户管理服务实现。聚合用户、角色和酒店授权信息供前端展示。
 */
@Service
public class AdminUserManagementServiceImpl implements AdminUserManagementService {

    private static final int DEFAULT_PAGE_NUM = 1;
    private static final int DEFAULT_PAGE_SIZE = 20;
    private static final int MAX_PAGE_SIZE = 100;
    private static final String TEMP_PASSWORD_CHARS = "ABCDEFGHJKLMNPQRSTUVWXYZabcdefghijkmnopqrstuvwxyz23456789@#$%";
    private static final SecureRandom SECURE_RANDOM = new SecureRandom();

    private final PlatformIdentityRepository identityRepository;
    private final PlatformAccessRepository accessRepository;
    private final PlatformHotelRepository hotelRepository;
    private final AuthPasswordService passwordService;
    private final PlatformAdminAuditLogService auditLogService;
    private final ObjectMapper objectMapper;

    /**
     * 注入用户、角色权限和酒店仓储。
     */
    public AdminUserManagementServiceImpl(
            PlatformIdentityRepository identityRepository,
            PlatformAccessRepository accessRepository,
            PlatformHotelRepository hotelRepository,
            AuthPasswordService passwordService,
            PlatformAdminAuditLogService auditLogService,
            ObjectMapper objectMapper) {
        this.identityRepository = identityRepository;
        this.accessRepository = accessRepository;
        this.hotelRepository = hotelRepository;
        this.passwordService = passwordService;
        this.auditLogService = auditLogService;
        this.objectMapper = objectMapper;
    }

    /**
     * 分页查询用户列表，并补齐角色摘要和默认酒店。
     */
    @Override
    public PlatformPageResult<AdminUserListItemResult> queryUsers(
            String keyword,
            String userStatus,
            Integer pageNum,
            Integer pageSize) {
        int normalizedPageNum = normalizePageNum(pageNum);
        int normalizedPageSize = normalizePageSize(pageSize);
        PlatformPageSnapshot<PlatformUserEntity> page = identityRepository.queryUsers(
                trimToNull(keyword),
                trimToNull(userStatus),
                normalizedPageNum,
                normalizedPageSize);
        List<Long> userIds = page.items().stream().map(PlatformUserEntity::getId).toList();
        Map<Long, List<AdminUserRoleResult>> rolesByUserId = rolesByUserId(userIds);
        Map<Long, List<PlatformUserHotelEntity>> hotelRelationsByUserId = hotelRelationsByUserId(userIds);
        List<AdminUserListItemResult> items = page.items().stream()
                .map(user -> toListItem(user, rolesByUserId, hotelRelationsByUserId))
                .toList();
        return new PlatformPageResult<>(
                items,
                new PlatformPaginationResult(page.pageNum(), page.pageSize(), page.total()));
    }

    /**
     * 查询用户详情，包含角色和授权酒店。
     */
    @Override
    public AdminUserDetailResult getUser(String userId) {
        Long id = parseId(userId, "用户 ID 不合法。");
        PlatformUserEntity user = identityRepository.findUserById(id)
                .orElseThrow(() -> notFound("用户不存在。"));
        Map<Long, List<AdminUserRoleResult>> rolesByUserId = rolesByUserId(List.of(id));
        List<AdminUserHotelResult> hotels = hotelsForUser(id);
        return new AdminUserDetailResult(
                stringId(user.getId()),
                user.getUsername(),
                user.getDisplayName(),
                user.getEmail(),
                user.getPhone(),
                user.getUserStatus(),
                user.getSuperAdmin(),
                UtcTimeFormatter.toUtcOffsetDateTime(user.getPasswordChangedAt()),
                UtcTimeFormatter.toUtcOffsetDateTime(user.getLastLoginAt()),
                UtcTimeFormatter.toUtcOffsetDateTime(user.getCreatedAt()),
                UtcTimeFormatter.toUtcOffsetDateTime(user.getUpdatedAt()),
                rolesByUserId.getOrDefault(id, Collections.emptyList()),
                hotels);
    }

    /**
     * 新增普通用户账号，并可同步写入初始角色和酒店授权。
     */
    @Override
    @Transactional
    public AdminUserDetailResult createUser(AdminUserCreateRequest request, AuthenticatedUserContext actor) {
        if (request == null) {
            throw invalidRequest("用户创建请求不能为空。");
        }
        String username = requireCodeLike(request.username(), "用户名不能为空。");
        if (identityRepository.findUserByUsername(username).isPresent()) {
            throw conflict("用户名已存在。");
        }
        String rawPassword = requireText(request.initialPassword(), "初始密码不能为空。");
        validatePassword(rawPassword);
        String status = normalizeStatus(request.userStatus(), PlatformUserStatus.ACTIVE.name());
        validateUserStatus(status);

        LocalDateTime now = nowUtc();
        PlatformUserEntity user = new PlatformUserEntity();
        user.setUsername(username);
        user.setPasswordHash(passwordService.hash(rawPassword));
        user.setDisplayName(defaultIfBlank(request.displayName(), username));
        user.setEmail(trimToNull(request.email()));
        user.setPhone(trimToNull(request.phone()));
        user.setUserStatus(status);
        user.setSuperAdmin(false);
        user.setPasswordChangedAt(now);
        user.setCreatedAt(now);
        user.setUpdatedAt(now);
        identityRepository.insertUser(user);

        List<Long> roleIds = parseIdList(request.roleIds(), "角色 ID 不合法。");
        validateRolesExist(roleIds);
        accessRepository.replaceUserRoles(user.getId(), roleIds);

        List<String> hotelIds = normalizeHotelIds(request.hotelIds());
        validateUserHotels(hotelIds, request.defaultHotelId());
        hotelRepository.replaceUserHotels(user.getId(), hotelIds, trimToNull(request.defaultHotelId()));

        AdminUserDetailResult after = getUser(stringId(user.getId()));
        audit(actor, "PLATFORM_USER", stringId(user.getId()), "CREATE_USER", null, after);
        return after;
    }

    /**
     * 编辑用户基础信息。禁用用户时会撤销该用户全部 ACTIVE session。
     */
    @Override
    @Transactional
    public AdminUserDetailResult updateUser(
            String userId,
            AdminUserUpdateRequest request,
            AuthenticatedUserContext actor) {
        if (request == null) {
            throw invalidRequest("用户更新请求不能为空。");
        }
        Long id = parseId(userId, "用户 ID 不合法。");
        AdminUserDetailResult before = getUser(userId);
        PlatformUserEntity user = identityRepository.findUserById(id)
                .orElseThrow(() -> notFound("用户不存在。"));
        String status = normalizeStatus(request.userStatus(), user.getUserStatus());
        validateUserStatus(status);
        if (Boolean.TRUE.equals(user.getSuperAdmin())
                && PlatformUserStatus.DISABLED.name().equals(status)
                && identityRepository.countSuperAdminUsers() <= 1) {
            throw conflict("不能禁用最后一个启用的超级管理员。");
        }
        user.setDisplayName(defaultIfBlank(request.displayName(), user.getUsername()));
        user.setEmail(trimToNull(request.email()));
        user.setPhone(trimToNull(request.phone()));
        boolean disableUser = PlatformUserStatus.DISABLED.name().equals(status)
                && !PlatformUserStatus.DISABLED.name().equals(user.getUserStatus());
        user.setUserStatus(status);
        user.setUpdatedAt(nowUtc());
        identityRepository.updateUser(user);
        if (disableUser) {
            identityRepository.revokeActiveSessionsByUserId(user.getId());
        }
        AdminUserDetailResult after = getUser(userId);
        audit(actor, "PLATFORM_USER", userId, "UPDATE_USER", before, after);
        return after;
    }

    /**
     * 覆盖用户角色关系。超级管理员标记不通过角色分配接口变更。
     */
    @Override
    @Transactional
    public AdminUserDetailResult assignRoles(
            String userId,
            AdminUserRoleAssignmentRequest request,
            AuthenticatedUserContext actor) {
        if (request == null) {
            throw invalidRequest("用户角色分配请求不能为空。");
        }
        Long id = parseId(userId, "用户 ID 不合法。");
        identityRepository.findUserById(id).orElseThrow(() -> notFound("用户不存在。"));
        AdminUserDetailResult before = getUser(userId);
        List<Long> roleIds = parseIdList(request.roleIds(), "角色 ID 不合法。");
        validateRolesExist(roleIds);
        accessRepository.replaceUserRoles(id, roleIds);
        AdminUserDetailResult after = getUser(userId);
        audit(actor, "PLATFORM_USER", userId, "ASSIGN_USER_ROLES", before, after);
        return after;
    }

    /**
     * 覆盖普通用户酒店授权。默认酒店必须属于授权列表且必须是启用酒店。
     */
    @Override
    @Transactional
    public AdminUserDetailResult assignHotels(
            String userId,
            AdminUserHotelAssignmentRequest request,
            AuthenticatedUserContext actor) {
        if (request == null) {
            throw invalidRequest("用户酒店授权请求不能为空。");
        }
        Long id = parseId(userId, "用户 ID 不合法。");
        identityRepository.findUserById(id).orElseThrow(() -> notFound("用户不存在。"));
        AdminUserDetailResult before = getUser(userId);
        List<String> hotelIds = normalizeHotelIds(request.hotelIds());
        validateUserHotels(hotelIds, request.defaultHotelId());
        hotelRepository.replaceUserHotels(id, hotelIds, trimToNull(request.defaultHotelId()));
        AdminUserDetailResult after = getUser(userId);
        audit(actor, "PLATFORM_USER", userId, "ASSIGN_USER_HOTELS", before, after);
        return after;
    }

    /**
     * 重置用户密码。明文临时密码只在本次响应返回，审计里只记录发生过重置。
     */
    @Override
    @Transactional
    public AdminUserPasswordResetResult resetPassword(
            String userId,
            AdminUserPasswordResetRequest request,
            AuthenticatedUserContext actor) {
        Long id = parseId(userId, "用户 ID 不合法。");
        PlatformUserEntity user = identityRepository.findUserById(id)
                .orElseThrow(() -> notFound("用户不存在。"));
        String temporaryPassword = trimToNull(request == null ? null : request.newPassword());
        if (temporaryPassword == null) {
            temporaryPassword = generateTemporaryPassword();
        }
        validatePassword(temporaryPassword);
        AdminUserDetailResult before = getUser(userId);
        user.setPasswordHash(passwordService.hash(temporaryPassword));
        user.setPasswordChangedAt(nowUtc());
        user.setUpdatedAt(nowUtc());
        identityRepository.updateUser(user);
        identityRepository.revokeActiveSessionsByUserId(id);
        audit(actor, "PLATFORM_USER", userId, "RESET_USER_PASSWORD", before, Map.of(
                "user_id", userId,
                "username", user.getUsername(),
                "password_reset", true));
        return new AdminUserPasswordResetResult(userId, user.getUsername(), temporaryPassword);
    }

    /**
     * 将用户实体转换为列表项。
     */
    private AdminUserListItemResult toListItem(
            PlatformUserEntity user,
            Map<Long, List<AdminUserRoleResult>> rolesByUserId,
            Map<Long, List<PlatformUserHotelEntity>> hotelRelationsByUserId) {
        String defaultHotelId = hotelRelationsByUserId.getOrDefault(user.getId(), Collections.emptyList())
                .stream()
                .filter(relation -> Boolean.TRUE.equals(relation.getDefaultHotel()))
                .map(PlatformUserHotelEntity::getHotelId)
                .findFirst()
                .orElse(null);
        return new AdminUserListItemResult(
                stringId(user.getId()),
                user.getUsername(),
                user.getDisplayName(),
                user.getEmail(),
                user.getPhone(),
                user.getUserStatus(),
                user.getSuperAdmin(),
                rolesByUserId.getOrDefault(user.getId(), Collections.emptyList()),
                defaultHotelId,
                UtcTimeFormatter.toUtcOffsetDateTime(user.getLastLoginAt()),
                UtcTimeFormatter.toUtcOffsetDateTime(user.getUpdatedAt()));
    }

    /**
     * 查询指定用户集合的角色摘要。
     */
    private Map<Long, List<AdminUserRoleResult>> rolesByUserId(List<Long> userIds) {
        List<PlatformUserRoleEntity> relations = accessRepository.listUserRolesByUserIds(userIds);
        Set<Long> roleIds = relations.stream()
                .map(PlatformUserRoleEntity::getRoleId)
                .filter(Objects::nonNull)
                .collect(Collectors.toSet());
        Map<Long, PlatformRoleEntity> roleById = accessRepository.listRolesByIds(roleIds.stream().toList()).stream()
                .collect(Collectors.toMap(PlatformRoleEntity::getId, role -> role));
        Map<Long, List<AdminUserRoleResult>> grouped = new LinkedHashMap<>();
        for (PlatformUserRoleEntity relation : relations) {
            PlatformRoleEntity role = roleById.get(relation.getRoleId());
            if (role == null) {
                continue;
            }
            grouped.computeIfAbsent(relation.getUserId(), ignored -> new java.util.ArrayList<>())
                    .add(new AdminUserRoleResult(
                            stringId(role.getId()),
                            role.getRoleCode(),
                            role.getRoleName(),
                            role.getSystemBuiltin()));
        }
        return grouped;
    }

    /**
     * 查询用户酒店授权关系并按用户分组。
     */
    private Map<Long, List<PlatformUserHotelEntity>> hotelRelationsByUserId(List<Long> userIds) {
        return hotelRepository.listUserHotelRelationsByUserIds(userIds).stream()
                .collect(Collectors.groupingBy(
                        PlatformUserHotelEntity::getUserId,
                        LinkedHashMap::new,
                        Collectors.toList()));
    }

    /**
     * 查询指定用户的授权酒店展示信息。
     */
    private List<AdminUserHotelResult> hotelsForUser(Long userId) {
        List<PlatformUserHotelEntity> relations = hotelRepository.listUserHotelRelationsByUserIds(List.of(userId));
        Map<String, PlatformHotelEntity> hotelByHotelId = hotelRepository.listHotelsByHotelIds(relations.stream()
                        .map(PlatformUserHotelEntity::getHotelId)
                        .toList())
                .stream()
                .collect(Collectors.toMap(PlatformHotelEntity::getHotelId, hotel -> hotel));
        return relations.stream()
                .map(relation -> {
                    PlatformHotelEntity hotel = hotelByHotelId.get(relation.getHotelId());
                    return new AdminUserHotelResult(
                            relation.getHotelId(),
                            hotel == null ? relation.getHotelId() : hotel.getHotelName(),
                            hotel == null ? null : hotel.getHotelStatus(),
                            relation.getDefaultHotel());
                })
                .toList();
    }

    private int normalizePageNum(Integer pageNum) {
        return pageNum == null || pageNum < 1 ? DEFAULT_PAGE_NUM : pageNum;
    }

    private int normalizePageSize(Integer pageSize) {
        if (pageSize == null || pageSize < 1) {
            return DEFAULT_PAGE_SIZE;
        }
        return Math.min(pageSize, MAX_PAGE_SIZE);
    }

    private String trimToNull(String value) {
        if (value == null || value.isBlank()) {
            return null;
        }
        return value.trim();
    }

    private Long parseId(String id, String message) {
        try {
            return Long.valueOf(id);
        } catch (NumberFormatException exception) {
            throw new AdminOperationException(HttpStatus.BAD_REQUEST, "ADMIN_INVALID_REQUEST", message);
        }
    }

    private AdminOperationException notFound(String message) {
        return new AdminOperationException(HttpStatus.NOT_FOUND, "ADMIN_TARGET_NOT_FOUND", message);
    }

    private AdminOperationException invalidRequest(String message) {
        return new AdminOperationException(HttpStatus.BAD_REQUEST, "ADMIN_INVALID_REQUEST", message);
    }

    private AdminOperationException conflict(String message) {
        return new AdminOperationException(HttpStatus.CONFLICT, "ADMIN_CONFLICT", message);
    }

    private String stringId(Long id) {
        return id == null ? null : id.toString();
    }

    private String requireText(String value, String message) {
        String normalized = trimToNull(value);
        if (normalized == null) {
            throw invalidRequest(message);
        }
        return normalized;
    }

    private String requireCodeLike(String value, String message) {
        String normalized = requireText(value, message);
        if (!normalized.matches("[A-Za-z0-9_.@-]{3,128}")) {
            throw invalidRequest(message);
        }
        return normalized;
    }

    private String defaultIfBlank(String value, String fallback) {
        String normalized = trimToNull(value);
        return normalized == null ? fallback : normalized;
    }

    private String normalizeStatus(String value, String defaultStatus) {
        String normalized = trimToNull(value);
        return normalized == null ? defaultStatus : normalized.toUpperCase(java.util.Locale.ROOT);
    }

    private void validateUserStatus(String status) {
        if (!PlatformUserStatus.ACTIVE.name().equals(status) && !PlatformUserStatus.DISABLED.name().equals(status)) {
            throw invalidRequest("用户状态不合法。");
        }
    }

    private void validatePassword(String rawPassword) {
        if (rawPassword == null || rawPassword.length() < 8 || rawPassword.length() > 128) {
            throw invalidRequest("密码长度必须在 8 到 128 位之间。");
        }
    }

    private List<Long> parseIdList(List<String> ids, String message) {
        if (ids == null || ids.isEmpty()) {
            return Collections.emptyList();
        }
        List<Long> result = new ArrayList<>();
        for (String id : ids) {
            result.add(parseId(id, message));
        }
        return new ArrayList<>(new LinkedHashSet<>(result));
    }

    private void validateRolesExist(List<Long> roleIds) {
        if (roleIds == null || roleIds.isEmpty()) {
            return;
        }
        Set<Long> activeRoleIds = accessRepository.listRolesByIds(roleIds).stream()
                .filter(role -> PlatformRoleStatus.ACTIVE.name().equals(role.getRoleStatus()))
                .map(PlatformRoleEntity::getId)
                .collect(Collectors.toSet());
        if (!activeRoleIds.containsAll(roleIds)) {
            throw invalidRequest("存在不存在或未启用的角色 ID。");
        }
    }

    private List<String> normalizeHotelIds(List<String> hotelIds) {
        if (hotelIds == null) {
            return Collections.emptyList();
        }
        return hotelIds.stream()
                .map(this::trimToNull)
                .filter(Objects::nonNull)
                .distinct()
                .toList();
    }

    private void validateUserHotels(List<String> hotelIds, String defaultHotelId) {
        String normalizedDefaultHotelId = trimToNull(defaultHotelId);
        if (hotelIds == null || hotelIds.isEmpty()) {
            throw invalidRequest("用户至少需要授权一个酒店。");
        }
        if (normalizedDefaultHotelId == null || !hotelIds.contains(normalizedDefaultHotelId)) {
            throw invalidRequest("默认酒店必须在授权酒店列表内。");
        }
        Map<String, PlatformHotelEntity> hotelById = hotelRepository.listHotelsByHotelIds(hotelIds).stream()
                .collect(Collectors.toMap(PlatformHotelEntity::getHotelId, hotel -> hotel));
        if (!hotelById.keySet().containsAll(hotelIds)) {
            throw invalidRequest("存在不存在的酒店 ID。");
        }
        boolean hasDisabledHotel = hotelById.values().stream()
                .anyMatch(hotel -> !PlatformHotelStatus.ACTIVE.name().equals(hotel.getHotelStatus()));
        if (hasDisabledHotel) {
            throw invalidRequest("用户授权酒店必须全部是启用酒店。");
        }
        PlatformHotelEntity defaultHotel = hotelById.get(normalizedDefaultHotelId);
        if (defaultHotel == null || !PlatformHotelStatus.ACTIVE.name().equals(defaultHotel.getHotelStatus())) {
            throw invalidRequest("默认酒店必须是启用酒店。");
        }
    }

    private String generateTemporaryPassword() {
        StringBuilder builder = new StringBuilder(14);
        for (int i = 0; i < 14; i++) {
            builder.append(TEMP_PASSWORD_CHARS.charAt(SECURE_RANDOM.nextInt(TEMP_PASSWORD_CHARS.length())));
        }
        return builder.toString();
    }

    private LocalDateTime nowUtc() {
        return LocalDateTime.now(ZoneOffset.UTC);
    }

    private void audit(
            AuthenticatedUserContext actor,
            String targetType,
            String targetId,
            String action,
            Object before,
            Object after) {
        auditLogService.record(new PlatformAdminAuditLogDraft(
                actor,
                targetType,
                targetId,
                action,
                toJson(before),
                toJson(after)));
    }

    private String toJson(Object value) {
        if (value == null) {
            return null;
        }
        try {
            return objectMapper.writeValueAsString(value);
        } catch (JsonProcessingException exception) {
            throw new AdminOperationException(
                    HttpStatus.INTERNAL_SERVER_ERROR,
                    "ADMIN_AUDIT_SERIALIZE_FAILED",
                    "系统管理审计序列化失败。");
        }
    }
}
