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

import cn.nianxx.thhotel.platform.access.common.enums.PlatformPermissionStatus;
import cn.nianxx.thhotel.platform.access.common.enums.PlatformRoleStatus;
import cn.nianxx.thhotel.platform.access.common.request.AdminRoleCreateRequest;
import cn.nianxx.thhotel.platform.access.common.request.AdminRolePermissionAssignmentRequest;
import cn.nianxx.thhotel.platform.access.common.request.AdminRoleUpdateRequest;
import cn.nianxx.thhotel.platform.access.common.result.AdminPermissionResult;
import cn.nianxx.thhotel.platform.access.common.result.AdminRoleDetailResult;
import cn.nianxx.thhotel.platform.access.common.result.AdminRoleListItemResult;
import cn.nianxx.thhotel.platform.access.domain.PlatformPermissionEntity;
import cn.nianxx.thhotel.platform.access.domain.PlatformRoleEntity;
import cn.nianxx.thhotel.platform.access.domain.PlatformRolePermissionEntity;
import cn.nianxx.thhotel.platform.access.repository.PlatformAccessRepository;
import cn.nianxx.thhotel.platform.access.service.AdminAccessManagementService;
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.security.common.dto.AuthenticatedUserContext;
import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.databind.ObjectMapper;
import java.time.LocalDateTime;
import java.time.ZoneOffset;
import java.util.ArrayList;
import java.util.Collections;
import java.util.LinkedHashSet;
import java.util.List;
import java.util.Locale;
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 AdminAccessManagementServiceImpl implements AdminAccessManagementService {

    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 final PlatformAccessRepository accessRepository;
    private final PlatformAdminAuditLogService auditLogService;
    private final ObjectMapper objectMapper;

    /**
     * 注入角色权限仓储。
     */
    public AdminAccessManagementServiceImpl(
            PlatformAccessRepository accessRepository,
            PlatformAdminAuditLogService auditLogService,
            ObjectMapper objectMapper) {
        this.accessRepository = accessRepository;
        this.auditLogService = auditLogService;
        this.objectMapper = objectMapper;
    }

    /**
     * 分页查询角色列表，并补齐权限数量和用户数量。
     */
    @Override
    public PlatformPageResult<AdminRoleListItemResult> queryRoles(
            String keyword,
            String roleStatus,
            Integer pageNum,
            Integer pageSize) {
        int normalizedPageNum = normalizePageNum(pageNum);
        int normalizedPageSize = normalizePageSize(pageSize);
        PlatformPageSnapshot<PlatformRoleEntity> page = accessRepository.queryRoles(
                trimToNull(keyword),
                trimToNull(roleStatus),
                normalizedPageNum,
                normalizedPageSize);
        List<AdminRoleListItemResult> items = page.items().stream()
                .map(role -> new AdminRoleListItemResult(
                        stringId(role.getId()),
                        role.getRoleCode(),
                        role.getRoleName(),
                        role.getRoleStatus(),
                        role.getSystemBuiltin(),
                        accessRepository.countPermissionsByRoleId(role.getId()),
                        accessRepository.countUsersByRoleId(role.getId())))
                .toList();
        return new PlatformPageResult<>(
                items,
                new PlatformPaginationResult(page.pageNum(), page.pageSize(), page.total()));
    }

    /**
     * 查询角色详情和角色权限列表。
     */
    @Override
    public AdminRoleDetailResult getRole(String roleId) {
        Long id = parseId(roleId, "角色 ID 不合法。");
        PlatformRoleEntity role = accessRepository.findRoleById(id)
                .orElseThrow(() -> notFound("角色不存在。"));
        Map<Long, PlatformPermissionEntity> permissionById = accessRepository.listAllPermissions().stream()
                .collect(Collectors.toMap(PlatformPermissionEntity::getId, permission -> permission));
        List<AdminPermissionResult> permissions = accessRepository.listRolePermissionsByRoleId(id).stream()
                .map(PlatformRolePermissionEntity::getPermissionId)
                .map(permissionById::get)
                .filter(permission -> permission != null)
                .map(this::toPermissionResult)
                .toList();
        return new AdminRoleDetailResult(
                stringId(role.getId()),
                role.getRoleCode(),
                role.getRoleName(),
                role.getRoleStatus(),
                role.getSystemBuiltin(),
                UtcTimeFormatter.toUtcOffsetDateTime(role.getCreatedAt()),
                UtcTimeFormatter.toUtcOffsetDateTime(role.getUpdatedAt()),
                permissions,
                accessRepository.countUsersByRoleId(role.getId()));
    }

    /**
     * 查询全部权限码，供角色分配和菜单权限下拉使用。
     */
    @Override
    public List<AdminPermissionResult> listPermissions() {
        return accessRepository.listAllPermissions().stream()
                .map(this::toPermissionResult)
                .toList();
    }

    /**
     * 新增自定义角色，角色代码一旦创建不提供修改入口。
     */
    @Override
    @Transactional
    public AdminRoleDetailResult createRole(AdminRoleCreateRequest request, AuthenticatedUserContext actor) {
        if (request == null) {
            throw invalidRequest("角色创建请求不能为空。");
        }
        String roleCode = requireRoleCode(request.roleCode());
        if (accessRepository.findRoleByCode(roleCode).isPresent()) {
            throw conflict("角色代码已存在。");
        }
        String roleName = requireText(request.roleName(), "角色名称不能为空。");
        String roleStatus = normalizeStatus(request.roleStatus(), PlatformRoleStatus.ACTIVE.name());
        validateRoleStatus(roleStatus);
        PlatformRoleEntity role = new PlatformRoleEntity();
        role.setRoleCode(roleCode);
        role.setRoleName(roleName);
        role.setRoleStatus(roleStatus);
        role.setSystemBuiltin(false);
        role.setCreatedAt(nowUtc());
        role.setUpdatedAt(nowUtc());
        accessRepository.insertRole(role);
        AdminRoleDetailResult after = getRole(stringId(role.getId()));
        audit(actor, "PLATFORM_ROLE", stringId(role.getId()), "CREATE_ROLE", null, after);
        return after;
    }

    /**
     * 编辑自定义角色基础信息；内置角色由启动同步矩阵管理，不允许页面修改。
     */
    @Override
    @Transactional
    public AdminRoleDetailResult updateRole(
            String roleId,
            AdminRoleUpdateRequest request,
            AuthenticatedUserContext actor) {
        if (request == null) {
            throw invalidRequest("角色更新请求不能为空。");
        }
        Long id = parseId(roleId, "角色 ID 不合法。");
        PlatformRoleEntity role = accessRepository.findRoleById(id)
                .orElseThrow(() -> notFound("角色不存在。"));
        ensureCustomRole(role);
        AdminRoleDetailResult before = getRole(roleId);
        String roleName = requireText(request.roleName(), "角色名称不能为空。");
        String roleStatus = normalizeStatus(request.roleStatus(), role.getRoleStatus());
        validateRoleStatus(roleStatus);
        role.setRoleName(roleName);
        role.setRoleStatus(roleStatus);
        role.setUpdatedAt(nowUtc());
        accessRepository.updateRole(role);
        AdminRoleDetailResult after = getRole(roleId);
        audit(actor, "PLATFORM_ROLE", roleId, "UPDATE_ROLE", before, after);
        return after;
    }

    /**
     * 覆盖自定义角色权限。内置角色权限继续由代码矩阵启动同步维护。
     */
    @Override
    @Transactional
    public AdminRoleDetailResult assignPermissions(
            String roleId,
            AdminRolePermissionAssignmentRequest request,
            AuthenticatedUserContext actor) {
        if (request == null) {
            throw invalidRequest("角色权限分配请求不能为空。");
        }
        Long id = parseId(roleId, "角色 ID 不合法。");
        PlatformRoleEntity role = accessRepository.findRoleById(id)
                .orElseThrow(() -> notFound("角色不存在。"));
        ensureCustomRole(role);
        AdminRoleDetailResult before = getRole(roleId);
        List<Long> permissionIds = parseIdList(request.permissionIds(), "权限 ID 不合法。");
        validatePermissionsExist(permissionIds);
        accessRepository.replaceRolePermissions(id, permissionIds);
        AdminRoleDetailResult after = getRole(roleId);
        audit(actor, "PLATFORM_ROLE", roleId, "ASSIGN_ROLE_PERMISSIONS", before, after);
        return after;
    }

    private AdminPermissionResult toPermissionResult(PlatformPermissionEntity permission) {
        return new AdminPermissionResult(
                stringId(permission.getId()),
                permission.getPermissionCode(),
                permission.getPermissionName(),
                permission.getPermissionGroup(),
                permission.getPermissionStatus(),
                permission.getSystemBuiltin());
    }

    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 requireRoleCode(String value) {
        String normalized = requireText(value, "角色代码不能为空。").toUpperCase(Locale.ROOT);
        if (!normalized.matches("[A-Z0-9_]{3,128}")) {
            throw invalidRequest("角色代码只能包含大写字母、数字和下划线。");
        }
        return normalized;
    }

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

    private void validateRoleStatus(String status) {
        if (!PlatformRoleStatus.ACTIVE.name().equals(status) && !PlatformRoleStatus.DISABLED.name().equals(status)) {
            throw invalidRequest("角色状态不合法。");
        }
    }

    private void ensureCustomRole(PlatformRoleEntity role) {
        if (Boolean.TRUE.equals(role.getSystemBuiltin())) {
            throw conflict("内置角色不允许通过管理后台修改。");
        }
    }

    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 validatePermissionsExist(List<Long> permissionIds) {
        if (permissionIds == null || permissionIds.isEmpty()) {
            return;
        }
        Set<Long> activePermissionIds = accessRepository.listAllPermissions().stream()
                .filter(permission -> PlatformPermissionStatus.ACTIVE.name().equals(permission.getPermissionStatus()))
                .map(PlatformPermissionEntity::getId)
                .filter(Objects::nonNull)
                .collect(Collectors.toSet());
        if (!activePermissionIds.containsAll(permissionIds)) {
            throw invalidRequest("存在不存在或未启用的权限 ID。");
        }
    }

    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",
                    "系统管理审计序列化失败。");
        }
    }
}
