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

import cn.nianxx.thhotel.platform.audit.common.dto.PlatformAdminAuditLogDraft;
import cn.nianxx.thhotel.platform.audit.common.result.AdminAuditLogResult;
import cn.nianxx.thhotel.platform.audit.domain.PlatformAdminAuditLogEntity;
import cn.nianxx.thhotel.platform.audit.repository.PlatformAdminAuditLogRepository;
import cn.nianxx.thhotel.platform.audit.service.PlatformAdminAuditLogService;
import cn.nianxx.thhotel.platform.common.dto.PlatformPageSnapshot;
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 java.time.LocalDateTime;
import java.time.ZoneOffset;
import org.springframework.stereotype.Service;

/**
 * 平台管理审计服务实现。负责补齐 actor 和 UTC 时间。
 */
@Service
public class PlatformAdminAuditLogServiceImpl implements PlatformAdminAuditLogService {

    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 PlatformAdminAuditLogRepository auditLogRepository;

    /**
     * 注入管理审计仓储。
     */
    public PlatformAdminAuditLogServiceImpl(PlatformAdminAuditLogRepository auditLogRepository) {
        this.auditLogRepository = auditLogRepository;
    }

    /**
     * 记录平台管理写操作审计；空草稿直接忽略，避免管理主流程被空审计破坏。
     */
    @Override
    public void record(PlatformAdminAuditLogDraft draft) {
        if (draft == null || draft.actor() == null) {
            return;
        }
        PlatformAdminAuditLogEntity entity = new PlatformAdminAuditLogEntity();
        entity.setActorUserId(draft.actor().userId());
        entity.setActorUsername(draft.actor().username());
        entity.setActorDisplayName(draft.actor().displayName());
        entity.setTargetType(draft.targetType());
        entity.setTargetId(draft.targetId());
        entity.setAction(draft.action());
        entity.setBeforeSnapshotJson(draft.beforeSnapshotJson());
        entity.setAfterSnapshotJson(draft.afterSnapshotJson());
        entity.setOccurredAt(LocalDateTime.now(ZoneOffset.UTC));
        auditLogRepository.insert(entity);
    }

    /**
     * 分页查询平台管理审计，供系统管理后台展示写操作历史。
     */
    @Override
    public PlatformPageResult<AdminAuditLogResult> query(
            String targetType,
            String targetId,
            String action,
            Integer pageNum,
            Integer pageSize) {
        int normalizedPageNum = normalizePageNum(pageNum);
        int normalizedPageSize = normalizePageSize(pageSize);
        PlatformPageSnapshot<PlatformAdminAuditLogEntity> page = auditLogRepository.query(
                trimToNull(targetType),
                trimToNull(targetId),
                trimToNull(action),
                normalizedPageNum,
                normalizedPageSize);
        return new PlatformPageResult<>(
                page.items().stream().map(this::toResult).toList(),
                new PlatformPaginationResult(page.pageNum(), page.pageSize(), page.total()));
    }

    private AdminAuditLogResult toResult(PlatformAdminAuditLogEntity entity) {
        return new AdminAuditLogResult(
                stringId(entity.getId()),
                stringId(entity.getActorUserId()),
                entity.getActorUsername(),
                entity.getActorDisplayName(),
                entity.getTargetType(),
                entity.getTargetId(),
                entity.getAction(),
                entity.getBeforeSnapshotJson(),
                entity.getAfterSnapshotJson(),
                UtcTimeFormatter.toUtcOffsetDateTime(entity.getOccurredAt()));
    }

    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 String stringId(Long id) {
        return id == null ? null : id.toString();
    }
}
