package com.zioinfo.esn.admin;

import com.zioinfo.esn.admin.dto.AuditLog;
import com.zioinfo.esn.admin.mapper.AuditLogMapper;
import jakarta.servlet.http.HttpServletRequest;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.security.core.Authentication;
import org.springframework.security.core.context.SecurityContextHolder;
import org.springframework.stereotype.Service;
import org.springframework.web.context.request.RequestContextHolder;
import org.springframework.web.context.request.ServletRequestAttributes;

import java.time.LocalDateTime;
import java.util.List;

/**
 * 감사 로그 서비스.
 *
 * <p>ESL 제어 운영(펌웨어 배포·POS 가격 변경·태그 큐 전파)과 관리 행위(사용자/테넌트 CRUD·설정 변경)를
 * {@code esn_audit_log} 에 기록하고 조회한다.
 *
 * <p>감사 기록 실패가 본업(펌웨어 배포 등)을 막아서는 안 되므로 insert 는 best-effort(예외 흡수)다.
 *
 * <p>보안 불변: 비밀번호/자격증명/PII 는 detail 에 절대 기록하지 않는다. 호출 측이 키/식별자/요약만 넘긴다.
 */
@Slf4j
@Service
@RequiredArgsConstructor
public class AuditService {

    private final AuditLogMapper mapper;

    /** 가장 단순한 형태: 액션/리소스/식별자/요약. 현재 인증 주체로 기록. */
    public void log(String action, String resource, String resourceId, String detail) {
        log(null, action, resource, resourceId, detail, true);
    }

    /** 테넌트 스코프 포함. */
    public void log(String tenantCode, String action, String resource, String resourceId,
                    String detail, boolean success) {
        try {
            AuditLog entry = new AuditLog();
            entry.setTenantCode(tenantCode);
            entry.setUsername(currentActor());
            entry.setAction(action);
            entry.setResource(resource);
            entry.setResourceId(resourceId);
            entry.setDetail(detail);
            entry.setSuccess(success);
            HttpServletRequest req = currentRequest();
            if (req != null) {
                entry.setIpAddress(clientIp(req));
                String ua = req.getHeader("User-Agent");
                entry.setUserAgent(ua != null && ua.length() > 500 ? ua.substring(0, 500) : ua);
            }
            mapper.insert(entry);
        } catch (Exception e) {
            // best-effort: 감사 기록 실패가 본업을 막지 않는다.
            log.warn("audit log insert 실패 action={} resource={}: {}", action, resource, e.getMessage());
        }
    }

    public List<AuditLog> find(String tenantCode, String username, String action, String resource,
                               LocalDateTime from, LocalDateTime to, int limit) {
        int safeLimit = (limit <= 0 || limit > 1000) ? 100 : limit;
        return mapper.find(tenantCode, username, action, resource, from, to, safeLimit);
    }

    public long count(String tenantCode, String username, String action, String resource,
                      LocalDateTime from, LocalDateTime to) {
        return mapper.count(tenantCode, username, action, resource, from, to);
    }

    /** SecurityContext 의 JWT subject(username). 없으면 system. */
    public static String currentActor() {
        Authentication auth = SecurityContextHolder.getContext().getAuthentication();
        if (auth != null && auth.getName() != null && !auth.getName().isBlank()) {
            return auth.getName();
        }
        return "system";
    }

    private static HttpServletRequest currentRequest() {
        var attrs = RequestContextHolder.getRequestAttributes();
        if (attrs instanceof ServletRequestAttributes sra) {
            return sra.getRequest();
        }
        return null;
    }

    private static String clientIp(HttpServletRequest req) {
        String xf = req.getHeader("X-Forwarded-For");
        if (xf != null && !xf.isBlank()) {
            return xf.split(",")[0].trim();
        }
        return req.getRemoteAddr();
    }
}
