package com.zioinfo.kintex.system.authpolicy;

import com.zioinfo.kintex.tenant.TenantContext;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

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

/**
 * 인증 정책 서비스 — 비밀번호 정책·로그인 실패 잠금 임계·세션 타임아웃(관리 조회/변경).
 * 실제 잠금 강제는 LoginAttemptService/sys_setting(login.*)가 담당하며, 본 정책은 관리 화면 계약이다
 * (수치 정합: login_max_fail↔login.maxFailedAttempts, lock_minutes↔login.lockMinutes).
 */
@Service
public class AuthPolicyService {

    private final AuthPolicyMapper mapper;

    public AuthPolicyService(AuthPolicyMapper mapper) {
        this.mapper = mapper;
    }

    public AuthPolicyDto get() {
        Map<String, Object> r = mapper.find(TenantContext.currentTenantId());
        if (r == null) {
            // 표준 기본값(마이그레이션 시드가 없을 경우의 방어).
            return new AuthPolicyDto(8, "N", "Y", "N", 0, 5, 15, 120, null, null);
        }
        return new AuthPolicyDto(
                intv(r.get("pwMinLength")), str(r.get("pwRequireUpper")), str(r.get("pwRequireNumber")),
                str(r.get("pwRequireSpecial")), intv(r.get("pwExpireDays")), intv(r.get("loginMaxFail")),
                intv(r.get("lockMinutes")), intv(r.get("sessionTimeoutMin")),
                str(r.get("updatedAt")), str(r.get("updatedBy")));
    }

    @Transactional
    public void save(AuthPolicySaveRequest req, String actorId) {
        Map<String, Object> p = new HashMap<>();
        p.put("tenantId", TenantContext.currentTenantId());
        p.put("pwMinLength", clamp(req.pwMinLength(), 4, 64, 8));
        p.put("pwRequireUpper", yn(req.pwRequireUpper()));
        p.put("pwRequireNumber", yn(req.pwRequireNumber()));
        p.put("pwRequireSpecial", yn(req.pwRequireSpecial()));
        p.put("pwExpireDays", clamp(req.pwExpireDays(), 0, 3650, 0));
        p.put("loginMaxFail", clamp(req.loginMaxFail(), 1, 20, 5));
        p.put("lockMinutes", clamp(req.lockMinutes(), 1, 1440, 15));
        p.put("sessionTimeoutMin", clamp(req.sessionTimeoutMin(), 5, 1440, 120));
        p.put("updatedBy", actorId);
        mapper.upsert(p);
    }

    private static String yn(String v) { return "Y".equalsIgnoreCase(v) ? "Y" : "N"; }
    private static int clamp(Integer v, int min, int max, int def) {
        int x = v == null ? def : v;
        return Math.max(min, Math.min(max, x));
    }
    private static String str(Object o) { return o == null ? null : String.valueOf(o); }
    private static Integer intv(Object o) {
        if (o instanceof Number n) return n.intValue();
        return o == null ? null : Integer.parseInt(String.valueOf(o));
    }
}
