package com.eactive.eai.rms.ext.djb.smsauth;

import java.security.SecureRandom;
import java.time.LocalDateTime;
import java.time.format.DateTimeFormatter;
import java.util.HashMap;

import javax.servlet.http.HttpSession;

import org.apache.commons.lang3.StringUtils;
import org.springframework.stereotype.Service;

import com.eactive.apim.portal.template.entity.MessageCode;
import com.eactive.apim.portal.user.entity.UserInfo;
import com.eactive.eai.rms.common.login.LoginVo;
import com.eactive.eai.rms.ext.djb.common.DjbProperty;
import com.eactive.eai.rms.ext.djb.common.DjbPropertyHolder;
import com.eactive.eai.rms.ext.djb.ums.UmsManager;

import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;

@Slf4j
@Service
@RequiredArgsConstructor
public class SmsAuthService {

    // 세션 키
    public static final String SESSION_KEY_AUTH_CODE = "SMS_AUTH_CODE";
    public static final String SESSION_KEY_ISSUED_AT = "SMS_AUTH_ISSUED_AT";
    public static final String SESSION_KEY_USER_INFO = "SMS_AUTH_USER_INFO";
    public static final String SESSION_KEY_LOGIN_VO = "SMS_AUTH_LOGIN_VO";

    // 시간 설정 (밀리초)
    public static final long EXPIRY_MS = 60_000;           // 1분
    public static final long RESEND_INTERVAL_MS = 20_000;  // 20초

    // 인증번호 생성 모드
    public static final String MODE_REAL = "real";
    public static final String MODE_HHMM00 = "hhmm00";
    public static final String MODE_FIXED = "fixed";

    private final SecureRandom random = new SecureRandom();

    private final UmsManager ums;

    private static DjbProperty getProperty() {
        return DjbPropertyHolder.get();
    }

    

    /**
     * SMS 인증이 필요한지 확인
     */
    public boolean isRequired(UserInfo userInfo) {
        if (!getProperty().isSmsAuthEnabled()) {
            log.debug("SMS 인증 비활성화 상태");
            return false;
        }

        String phone = userInfo.getCphnno();
        if (StringUtils.isEmpty(phone)) {
            log.warn("SMS 인증 필요하나 휴대폰번호 없음 - userId: {}", userInfo.getUserid());
            // 전화번호 없으면 인증 불가이므로 isRequired는 true 반환하고
            // 컨트롤러에서 에러 처리
            return true;
        }

        return true;
    }

    /**
     * 사용자의 휴대폰번호가 유효한지 확인
     */
    public boolean hasValidPhoneNumber(UserInfo userInfo) {
        String phone = userInfo.getCphnno();
        return StringUtils.isNotEmpty(phone) && phone.replaceAll("[^0-9]", "").length() >= 10;
    }

    /**
     * 인증번호 생성
     */
    public String generateAuthCode() {
        DjbProperty prop = getProperty();
        String mode = prop.getSmsAuthMode();

        if (MODE_FIXED.equalsIgnoreCase(mode)) {
            String fixedValue = prop.getSmsAuthFixedValue();
            log.debug("SMS 인증번호 생성 (fixed 모드): {}", fixedValue);
            return fixedValue;
        } else if (MODE_HHMM00.equalsIgnoreCase(mode)) {
            String timeCode = LocalDateTime.now().format(DateTimeFormatter.ofPattern("HHmm")) + "00";
            log.debug("SMS 인증번호 생성 (hhmm00 모드): {}", timeCode);
            return timeCode;
        } else {
            // real 모드 - 랜덤 6자리
            String randomCode = String.format("%06d", random.nextInt(1000000));
            log.debug("SMS 인증번호 생성 (real 모드): {}", randomCode);
            return randomCode;
        }
    }

    /**
     * SMS 인증번호 발송
     */
    public boolean sendAuthCode(UserInfo userInfo, String authCode) {
        try {
            HashMap<String,Object> params = new HashMap<String, Object>();
            params.put("authNumber", authCode);
            params.put("userName", userInfo.getUsername());

			ums.send(userInfo, MessageCode.ADMIN_VERIFICATION_MOBILEPHONE, params);
            return true;
        } catch (Exception e) {
            log.error("SMS 인증번호 발송 실패", e);
            return false;
        }
    }

    /**
     * 세션에 SMS 인증 정보 저장
     */
    public void setupAuthSession(HttpSession session, UserInfo userInfo, LoginVo loginVo, String authCode) {
        long now = System.currentTimeMillis();
        session.setAttribute(SESSION_KEY_AUTH_CODE, authCode);
        session.setAttribute(SESSION_KEY_ISSUED_AT, now);
        session.setAttribute(SESSION_KEY_USER_INFO, userInfo);
        session.setAttribute(SESSION_KEY_LOGIN_VO, loginVo);
        log.debug("SMS 인증 세션 설정 완료 - userId: {}", userInfo.getUserid());
    }

    /**
     * 인증번호 검증
     */
    public boolean validateAuthCode(HttpSession session, String inputCode) {
        String storedCode = (String) session.getAttribute(SESSION_KEY_AUTH_CODE);
        Long issuedAt = (Long) session.getAttribute(SESSION_KEY_ISSUED_AT);

        if (storedCode == null || issuedAt == null) {
            log.warn("SMS 인증 세션 정보 없음");
            return false;
        }

        if (isExpired(session)) {
            log.warn("SMS 인증번호 만료됨");
            return false;
        }

        boolean valid = storedCode.equals(inputCode);
        if (valid) {
            log.info("SMS 인증번호 검증 성공");
        } else {
            log.warn("SMS 인증번호 불일치");
        }
        return valid;
    }

    /**
     * 인증번호 만료 여부 확인
     */
    public boolean isExpired(HttpSession session) {
        Long issuedAt = (Long) session.getAttribute(SESSION_KEY_ISSUED_AT);
        if (issuedAt == null) {
            return true;
        }
        return System.currentTimeMillis() - issuedAt > EXPIRY_MS;
    }

    /**
     * 재발송 가능 여부 확인
     */
    public boolean canResend(HttpSession session) {
        Long issuedAt = (Long) session.getAttribute(SESSION_KEY_ISSUED_AT);
        if (issuedAt == null) {
            return true;
        }
        return System.currentTimeMillis() - issuedAt >= RESEND_INTERVAL_MS;
    }

    /**
     * 남은 유효 시간 (밀리초)
     */
    public long getRemainingTime(HttpSession session) {
        Long issuedAt = (Long) session.getAttribute(SESSION_KEY_ISSUED_AT);
        if (issuedAt == null) {
            return 0;
        }
        long remaining = EXPIRY_MS - (System.currentTimeMillis() - issuedAt);
        return Math.max(0, remaining);
    }

    /**
     * 재발송 가능 시간까지 남은 시간 (밀리초)
     */
    public long getResendWaitTime(HttpSession session) {
        Long issuedAt = (Long) session.getAttribute(SESSION_KEY_ISSUED_AT);
        if (issuedAt == null) {
            return 0;
        }
        long wait = RESEND_INTERVAL_MS - (System.currentTimeMillis() - issuedAt);
        return Math.max(0, wait);
    }

    /**
     * 세션에서 UserInfo 조회
     */
    public UserInfo getUserInfoFromSession(HttpSession session) {
        return (UserInfo) session.getAttribute(SESSION_KEY_USER_INFO);
    }

    /**
     * 세션에서 LoginVo 조회
     */
    public LoginVo getLoginVoFromSession(HttpSession session) {
        return (LoginVo) session.getAttribute(SESSION_KEY_LOGIN_VO);
    }

    /**
     * SMS 인증 세션 정보 클리어
     */
    public void clearAuthSession(HttpSession session) {
        session.removeAttribute(SESSION_KEY_AUTH_CODE);
        session.removeAttribute(SESSION_KEY_ISSUED_AT);
        session.removeAttribute(SESSION_KEY_USER_INFO);
        session.removeAttribute(SESSION_KEY_LOGIN_VO);
        log.debug("SMS 인증 세션 정보 클리어 완료");
    }

    /**
     * 만료 시각 (timestamp) 반환
     */
    public long getExpiresAt(HttpSession session) {
        Long issuedAt = (Long) session.getAttribute(SESSION_KEY_ISSUED_AT);
        if (issuedAt == null) {
            return System.currentTimeMillis();
        }
        return issuedAt + EXPIRY_MS;
    }

    /**
     * 재발송 가능 시각 (timestamp) 반환
     */
    public long getResendableAt(HttpSession session) {
        Long issuedAt = (Long) session.getAttribute(SESSION_KEY_ISSUED_AT);
        if (issuedAt == null) {
            return System.currentTimeMillis();
        }
        return issuedAt + RESEND_INTERVAL_MS;
    }

    /**
     * 전화번호 마스킹 (표준 규칙: 2·3번째 세그먼트 각각 뒤 2자리)
     * 예: 010-1234-5678 → 010-12**-56**
     */
    public String maskPhoneNumber(String phone) {
        if (StringUtils.isEmpty(phone)) {
            return "****";
        }
        return com.eactive.eai.common.util.MaskingUtils.maskPhoneNumber(phone);
    }

    /**
     * SMS 인증 활성화 여부
     */
    public boolean isEnabled() {
        return getProperty().isSmsAuthEnabled();
    }
}
