package com.zioinfo.kintex.common.audit;

import com.zioinfo.kintex.common.PageResponse;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.stereotype.Service;

import java.util.HashMap;
import java.util.List;
import java.util.Map;

/**
 * 감사 로그 기록/조회 서비스. 기록은 실패해도 업무 트랜잭션을 막지 않는다(best-effort, 예외 삼킴).
 * 민감정보·스택트레이스는 절대 기록하지 않는다(계약 §0-3/§7-3).
 */
@Service
public class AuditLogService {

    private static final Logger log = LoggerFactory.getLogger(AuditLogService.class);
    private final AuditLogMapper mapper;

    public AuditLogService(AuditLogMapper mapper) {
        this.mapper = mapper;
    }

    /** 감사 기록(best-effort). summary 는 호출 측에서 민감정보 제거 후 전달. */
    public void record(String actorId, String actorName, String action, String targetType,
                       String targetId, String eventId, String summary, String result) {
        try {
            Map<String, Object> p = new HashMap<>();
            p.put("actorId", actorId);
            p.put("actorName", actorName);
            p.put("action", action);
            p.put("targetType", nullIfBlank(targetType));
            p.put("targetId", targetId);
            p.put("eventId", eventId);
            p.put("summary", truncate(summary, 1000));
            p.put("rulesetVersion", null);
            p.put("result", result == null ? "SUCCESS" : result);
            p.put("ipHint", null);
            mapper.insert(p);
        } catch (Exception e) {
            log.warn("감사 로그 기록 실패(action={}) — 업무 흐름 계속", action);
        }
    }

    public PageResponse<AuditLogDto> search(String action, String actorId, String eventId, int page, int size) {
        int p = Math.max(page, 0);
        int s = size <= 0 ? 20 : Math.min(size, 200);
        List<Map<String, Object>> rows = mapper.search(action, actorId, eventId, s, p * s);
        long total = mapper.count(action, actorId, eventId);
        return PageResponse.of(rows.stream().map(AuditLogService::toDto).toList(), p, s, total);
    }

    private static AuditLogDto toDto(Map<String, Object> r) {
        return new AuditLogDto(
                ((Number) r.getOrDefault("id", 0L)).longValue(),
                str(r.get("actorId")), str(r.get("actorName")), str(r.get("action")),
                str(r.get("targetType")), str(r.get("targetId")), str(r.get("eventId")),
                str(r.get("summary")), str(r.get("result")), str(r.get("createdAt")));
    }
    private static String str(Object o) { return o == null ? null : String.valueOf(o); }
    private static String nullIfBlank(String s) { return (s == null || s.isBlank()) ? null : s; }
    private static String truncate(String s, int max) {
        if (s == null) return null;
        return s.length() <= max ? s : s.substring(0, max);
    }
}
