package com.zioinfo.hrm.uiws.system.service;

import com.zioinfo.hrm.uiws.common.UiwsApiException;
import com.zioinfo.hrm.uiws.common.UiwsErrorCode;
import com.zioinfo.hrm.uiws.common.mail.MailSender;
import com.zioinfo.hrm.uiws.system.dto.CheckIdResponse;
import com.zioinfo.hrm.uiws.system.dto.PageResponse;
import com.zioinfo.hrm.uiws.system.dto.UserDto;
import com.zioinfo.hrm.uiws.system.dto.UserSaveDto;
import com.zioinfo.hrm.uiws.system.mapper.CompanyMapper;
import com.zioinfo.hrm.uiws.system.mapper.DeptMapper;
import com.zioinfo.hrm.uiws.system.mapper.SysUserMapper;
import com.zioinfo.hrm.uiws.system.model.SysCompany;
import com.zioinfo.hrm.uiws.system.model.SysDept;
import com.zioinfo.hrm.uiws.system.model.SysUser;
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;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.stream.Collectors;

/**
 * 2.6 사용자(users). 관리자 사용자 CRUD/검색/중복확인/다중삭제/비번초기화/잠금해제/승인.
 * 관리자 등록 사용자는 approval_yn='Y'. 비밀번호는 BCrypt — 응답·로그(코드 외)에 절대 노출 금지.
 */
@Slf4j
@Service
@RequiredArgsConstructor
public class UserService {

    private static final SecureRandom RANDOM = new SecureRandom();

    private final SysUserMapper userMapper;
    private final DeptMapper deptMapper;
    private final CompanyMapper companyMapper;
    private final PasswordEncoder passwordEncoder;
    private final MailSender mailSender;

    @Transactional(readOnly = true)
    public PageResponse<UserDto> list(String keyword, String deptId, int page, int size) {
        Map<String, String> deptNames = deptNameMap();
        Map<String, String> companyNames = companyNameMap();
        List<UserDto> content = userMapper.search(keyword, deptId, page * size, size).stream()
                .map(u -> toDto(u, deptNames, companyNames)).toList();
        return PageResponse.of(content, userMapper.countSearch(keyword, deptId), page, size);
    }

    @Transactional(readOnly = true)
    public List<UserDto> searchPopup(String keyword, String deptId) {
        Map<String, String> deptNames = deptNameMap();
        Map<String, String> companyNames = companyNameMap();
        return userMapper.searchActive(keyword, deptId).stream()
                .map(u -> toDto(u, deptNames, companyNames)).toList();
    }

    @Transactional(readOnly = true)
    public CheckIdResponse checkId(String userId) {
        boolean available = userId != null && !userId.isBlank() && !userMapper.existsById(userId);
        return new CheckIdResponse(available);
    }

    @Transactional(readOnly = true)
    public UserDto get(String userId) {
        return toDto(find(userId), deptNameMap(), companyNameMap());
    }

    @Transactional
    public UserDto create(UserSaveDto dto) {
        if (userMapper.existsById(dto.userId())) {
            throw new UiwsApiException(UiwsErrorCode.USER_ID_DUPLICATED);
        }
        String rawPw = (dto.password() == null || dto.password().isBlank())
                ? generateTempPassword() : dto.password();

        SysUser u = new SysUser();
        u.setUserId(dto.userId());
        u.setUserNm(dto.userNm());
        u.setPassword(passwordEncoder.encode(rawPw));
        u.setEmail(dto.email());
        u.setGradeCd(dto.gradeCd());
        u.setDeptId(dto.deptId());
        u.setCompanyId(dto.companyId());
        u.setRoleCd(normalizeRole(dto.roleCd()));
        u.setNaverworksId(blankToNull(dto.naverworksId()));
        u.setLoginFailCnt(0);
        u.setLockYn("N");
        u.setUseYn(dto.useYn() == null ? "Y" : dto.useYn());
        u.setApprovalYn("Y");      // 관리자 생성 → 즉시 승인
        u.setVerifyMethod("EMAIL");
        u.setPwChangeYn("Y");      // 최초 로그인 시 비번 변경 유도
        u.setCreatedBy(SysActor.id());
        userMapper.insert(u);
        return toDto(find(dto.userId()), deptNameMap(), companyNameMap());
    }

    @Transactional
    public UserDto update(String userId, UserSaveDto dto) {
        SysUser u = find(userId);
        u.setUserNm(dto.userNm());
        u.setEmail(dto.email());
        u.setGradeCd(dto.gradeCd());
        u.setDeptId(dto.deptId());
        u.setCompanyId(dto.companyId());
        if (dto.roleCd() != null && !dto.roleCd().isBlank()) {
            u.setRoleCd(normalizeRole(dto.roleCd()));
        }
        u.setNaverworksId(blankToNull(dto.naverworksId()));
        if (dto.useYn() != null && !dto.useYn().isBlank()) {
            u.setUseYn(dto.useYn());
        }
        u.setPassword((dto.password() != null && !dto.password().isBlank())
                ? passwordEncoder.encode(dto.password()) : null);  // null → XML 에서 비밀번호 미변경
        u.setUpdatedBy(SysActor.id());
        userMapper.update(u);
        return toDto(find(userId), deptNameMap(), companyNameMap());
    }

    /** 사용자 삭제 = 소프트삭제(use_yn='N' + 승인 회수). 이력 보존 위해 하드삭제 금지. */
    @Transactional
    public void delete(List<String> ids) {
        for (String id : ids) {
            SysUser u = find(id);
            u.setUseYn("N");
            u.setApprovalYn("N");
            u.setUpdatedBy(SysActor.id());
            userMapper.update(u);
        }
    }

    /** 비번 초기화: 임시비번 생성 → BCrypt 저장 → 메일/로그(코드 외)로만 전달, 잠금 해제. */
    @Transactional
    public void resetPassword(String userId) {
        SysUser u = find(userId);
        String temp = generateTempPassword();
        u.setPassword(passwordEncoder.encode(temp));
        u.setLockYn("N");
        u.setLoginFailCnt(0);
        u.setPwChangeYn("Y");
        u.setUpdatedBy(SysActor.id());
        userMapper.update(u);
        // 임시비번은 메일/로그 채널로만 — API 응답에는 절대 미포함(보안 불변규칙).
        mailSender.send(u.getEmail(), "[GUARDiA HRM] 비밀번호 초기화",
                String.format("임시 비밀번호: %s\n로그인 후 즉시 변경하세요.", temp));
    }

    /** 잠금 해제. */
    @Transactional
    public void unlock(String userId) {
        SysUser u = find(userId);
        u.setLockYn("N");
        u.setLoginFailCnt(0);
        u.setUpdatedBy(SysActor.id());
        userMapper.update(u);
    }

    /** 가입 승인 — approval_yn='Y'. */
    @Transactional
    public void approve(String userId) {
        SysUser u = find(userId);
        u.setApprovalYn("Y");
        u.setUpdatedBy(SysActor.id());
        userMapper.update(u);
    }

    /** 승인 취소 — approval_yn='N'. */
    @Transactional
    public void revokeApproval(String userId) {
        SysUser u = find(userId);
        u.setApprovalYn("N");
        u.setUpdatedBy(SysActor.id());
        userMapper.update(u);
    }

    // ------------------------------------------------------------------
    private UserDto toDto(SysUser u, Map<String, String> deptNames, Map<String, String> companyNames) {
        String deptNm = u.getDeptId() == null ? null : deptNames.get(u.getDeptId());
        String companyNm = u.getCompanyId() == null ? null : companyNames.get(u.getCompanyId());
        return new UserDto(u.getUserId(), u.getUserNm(), u.getEmail(), u.getGradeCd(),
                u.getDeptId(), deptNm, u.getCompanyId(), companyNm,
                u.getRoleCd(), u.getNaverworksId(), u.getLockYn(), u.getUseYn(), u.getApprovalYn());
    }

    private static String blankToNull(String s) {
        return (s == null || s.isBlank()) ? null : s;
    }

    private String normalizeRole(String roleCd) {
        if ("MANAGER".equals(roleCd) || "ADMIN".equals(roleCd)) {
            return roleCd;
        }
        return "USER";
    }

    private Map<String, String> deptNameMap() {
        return deptMapper.findAll().stream()
                .collect(Collectors.toMap(SysDept::getDeptId, SysDept::getDeptNm, (a, b) -> a, HashMap::new));
    }

    private Map<String, String> companyNameMap() {
        return companyMapper.findAll().stream()
                .collect(Collectors.toMap(SysCompany::getCompanyId, SysCompany::getCompanyNm, (a, b) -> a, HashMap::new));
    }

    private SysUser find(String userId) {
        SysUser u = userMapper.findById(userId);
        if (u == null) {
            throw new UiwsApiException(UiwsErrorCode.USER_NOT_FOUND);
        }
        return u;
    }

    private String generateTempPassword() {
        String upper = "ABCDEFGHJKLMNPQRSTUVWXYZ";
        String lower = "abcdefghijkmnpqrstuvwxyz";
        String digit = "23456789";
        String special = "!@#$%";
        String all = upper + lower + digit + special;
        StringBuilder sb = new StringBuilder();
        sb.append(upper.charAt(RANDOM.nextInt(upper.length())));
        sb.append(lower.charAt(RANDOM.nextInt(lower.length())));
        sb.append(digit.charAt(RANDOM.nextInt(digit.length())));
        sb.append(special.charAt(RANDOM.nextInt(special.length())));
        for (int i = 0; i < 6; i++) {
            sb.append(all.charAt(RANDOM.nextInt(all.length())));
        }
        return sb.toString();
    }
}
