package com.zioinfo.esn.auth;

import com.zioinfo.esn.auth.mapper.UserAuthMapper;
import com.zioinfo.esn.uiws.common.mail.MailSender;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.security.crypto.password.PasswordEncoder;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import java.security.SecureRandom;

/**
 * 로그인 보조 3종(회원가입 승인대기 / 아이디찾기 / 비밀번호 초기화).
 * 기존 AuthService(2FA 로그인)는 손대지 않고 별도 서비스로 분리.
 *
 * 보안(불변규칙):
 *  - 임시비밀번호/비밀번호/자격증명은 API 응답·예외 메시지에 절대 미노출. 메일/로그 채널로만 전달.
 *  - 아이디찾기/비번초기화는 미존재 계정도 success=true(found=false) — 계정 열거(enumeration) 방지.
 *  - 회원가입은 승인대기(is_active=FALSE, approval_status='PENDING') — 즉시 활성화 없음.
 *  - 외부 API 호출 없음(MailSender 폴백은 로그). Ollama 외 외부 통신 금지 준수.
 */
@Slf4j
@Service
@RequiredArgsConstructor
public class AuthHelperService {

    private static final SecureRandom RANDOM = new SecureRandom();
    private static final String TEMP_PW_ALPHABET =
            "ABCDEFGHJKLMNPQRSTUVWXYZabcdefghijkmnpqrstuvwxyz23456789";
    private static final int TEMP_PW_LEN = 12;

    private final UserAuthMapper userMapper;
    private final PasswordEncoder passwordEncoder;
    private final MailSender mailSender;

    /** 회원가입 — 승인대기 등록. 아이디 중복 시 거부. */
    @Transactional
    public SignupResponse signup(SignupRequest req) {
        if (req == null || req.username() == null || req.username().isBlank()
                || req.password() == null || req.password().isBlank()) {
            throw new RuntimeException("ERR-AUTH-100: 아이디/비밀번호는 필수입니다.");
        }
        if (userMapper.countByUsername(req.username()) > 0) {
            throw new RuntimeException("ERR-AUTH-101: 이미 사용 중인 아이디");
        }
        String hash = passwordEncoder.encode(req.password());
        userMapper.insertPendingUser(
                req.tenantCode(), req.username(), hash,
                req.name(), req.email(), req.phone());
        log.info("[signup] pending user created username={} (approval required)", req.username());
        return new SignupResponse(req.username(), "PENDING_APPROVAL");
    }

    /** 아이디 찾기 — 이름+이메일 매칭. 미존재도 200(found=false), 마스킹 반환. */
    public FindIdResponse findId(FindIdRequest req) {
        if (req == null || req.name() == null || req.email() == null) {
            return new FindIdResponse(false, "");
        }
        String username = userMapper.findUsernameByNameEmail(req.name(), req.email());
        if (username == null || username.isBlank()) {
            return new FindIdResponse(false, "");
        }
        return new FindIdResponse(true, maskUsername(username));
    }

    /**
     * 비밀번호 초기화 — 아이디+이름+이메일 일치 시 임시비번 발급(BCrypt 갱신) + 메일 발송.
     * 미존재해도 예외 없이 반환(열거방지). 임시비번은 메일/로그로만 전달.
     */
    @Transactional
    public void resetPassword(ResetPwRequest req) {
        if (req == null || req.username() == null || req.name() == null || req.email() == null) {
            return; // 열거방지 — 잘못된 요청도 조용히 성공 처리
        }
        EsnUser user = userMapper.findByUsernameNameEmail(req.username(), req.name(), req.email());
        if (user == null) {
            log.info("[reset-pw] no match for username={} (silently success — enumeration guard)",
                    req.username());
            return;
        }
        String tempPw = generateTempPassword();
        userMapper.updatePasswordHash(user.getUsername(), passwordEncoder.encode(tempPw));

        String subject = "[GUARDiA ESN] 임시 비밀번호 발급";
        String body = String.format(
                "안녕하세요 %s 님,\n임시 비밀번호는 [%s] 입니다.\n로그인 후 즉시 변경해 주세요.",
                user.getUsername(), tempPw);
        if (user.getEmail() != null && !user.getEmail().isBlank()) {
            // 임시비번은 메일 본문에만 — API 응답/예외 메시지에는 절대 미포함.
            mailSender.send(user.getEmail(), subject, body);
        } else {
            log.warn("[reset-pw] no email for username={} — temp password issued (log only)",
                    user.getUsername());
        }
    }

    /** 임시 비밀번호 생성(혼동 문자 제외 영숫자). */
    private static String generateTempPassword() {
        StringBuilder sb = new StringBuilder(TEMP_PW_LEN);
        for (int i = 0; i < TEMP_PW_LEN; i++) {
            sb.append(TEMP_PW_ALPHABET.charAt(RANDOM.nextInt(TEMP_PW_ALPHABET.length())));
        }
        return sb.toString();
    }

    /** 아이디 마스킹: 앞 2자만 노출 + 나머지 '*'. (2자 이하는 첫 글자만 노출.) */
    private static String maskUsername(String username) {
        if (username == null || username.isBlank()) {
            return "";
        }
        if (username.length() <= 2) {
            return username.charAt(0) + "*";
        }
        return username.substring(0, 2) + "*".repeat(username.length() - 2);
    }
}
