package com.zioinfo.kintex.system.sysuser;

import com.zioinfo.kintex.common.PageResponse;
import com.zioinfo.kintex.common.error.ApiException;
import com.zioinfo.kintex.common.error.ErrorCode;
import com.zioinfo.kintex.security.AccountSecurityMapper;
import com.zioinfo.kintex.tenant.TenantContext;
import org.springframework.security.crypto.password.PasswordEncoder;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.UUID;

/**
 * 시스템관리 — 사용자 관리 서비스. 비밀번호는 BCrypt 해시로만 저장하며 응답/로그 노출 금지(계약 §0-3).
 * OTP 초기화는 보안 매퍼 재사용(uims 방식).
 */
@Service
public class SysUserService {

    private final SysUserMapper mapper;
    private final AccountSecurityMapper securityMapper;
    private final PasswordEncoder passwordEncoder;

    public SysUserService(SysUserMapper mapper, AccountSecurityMapper securityMapper,
                          PasswordEncoder passwordEncoder) {
        this.mapper = mapper;
        this.securityMapper = securityMapper;
        this.passwordEncoder = passwordEncoder;
    }

    public PageResponse<SysUserDto> list(String keyword, String status, String roleCode, int page, int size) {
        int p = Math.max(page, 0);
        int s = size <= 0 ? 20 : Math.min(size, 200);
        // 사용자 목록은 현재 테넌트로 격리(§8-2). 기준 테넌트('kintex') 단일 운영 시 회귀 0.
        String tenantId = TenantContext.currentTenantId();
        List<Map<String, Object>> rows = mapper.search(tenantId, keyword, status, roleCode, s, p * s);
        long total = mapper.count(tenantId, keyword, status, roleCode);
        return PageResponse.of(rows.stream().map(SysUserService::toDto).toList(), p, s, total);
    }

    public SysUserDto get(String userId) {
        Map<String, Object> row = mapper.findById(userId);
        if (row == null) {
            throw new ApiException(ErrorCode.NOT_FOUND);
        }
        return toDto(row);
    }

    @Transactional
    public SysUserDto create(UserSaveRequest req) {
        if (req.email() == null || req.email().isBlank() || req.password() == null || req.password().isBlank()) {
            throw new ApiException(ErrorCode.VALIDATION, "이메일과 초기 비밀번호는 필수입니다.");
        }
        String id = "usr-" + UUID.randomUUID().toString().substring(0, 12);
        Map<String, Object> p = new HashMap<>();
        p.put("userId", id);
        p.put("email", req.email().toLowerCase());
        p.put("displayName", req.displayName());
        p.put("passwordHash", passwordEncoder.encode(req.password()));
        p.put("hallManager", Boolean.TRUE.equals(req.hallManager()));
        p.put("roleCode", req.roleCode() == null ? "USER" : req.roleCode());
        p.put("deptId", req.deptId());
        // 소속 테넌트는 클라이언트 입력이 아닌 컨텍스트에서 세팅(위조 차단, §8-2).
        p.put("tenantId", TenantContext.currentTenantId());
        mapper.insert(p);
        return get(id);
    }

    @Transactional
    public SysUserDto update(String userId, UserSaveRequest req) {
        Map<String, Object> p = new HashMap<>();
        p.put("userId", userId);
        p.put("displayName", req.displayName());
        p.put("roleCode", req.roleCode() == null ? "USER" : req.roleCode());
        p.put("deptId", req.deptId());
        p.put("hallManager", Boolean.TRUE.equals(req.hallManager()));
        if (mapper.update(p) == 0) {
            throw new ApiException(ErrorCode.NOT_FOUND);
        }
        return get(userId);
    }

    @Transactional
    public void changeStatus(String userId, String status) {
        if (mapper.updateStatus(userId, status) == 0) {
            throw new ApiException(ErrorCode.NOT_FOUND);
        }
    }

    @Transactional
    public void resetPassword(String userId, String newPassword) {
        if (newPassword == null || newPassword.isBlank()) {
            throw new ApiException(ErrorCode.VALIDATION, "새 비밀번호가 필요합니다.");
        }
        if (mapper.updatePassword(userId, passwordEncoder.encode(newPassword)) == 0) {
            throw new ApiException(ErrorCode.NOT_FOUND);
        }
    }

    /** 관리자 OTP 초기화(uims 방식). */
    @Transactional
    public void resetOtp(String userId) {
        securityMapper.resetOtp(userId);
    }

    private static SysUserDto toDto(Map<String, Object> r) {
        return new SysUserDto(
                str(r.get("userId")), str(r.get("email")), str(r.get("displayName")),
                bool(r.get("hallManager")), str(r.get("roleCode")), str(r.get("deptId")),
                str(r.get("status")), bool(r.get("otpEnabled")), str(r.get("lastLoginAt")));
    }
    private static String str(Object o) { return o == null ? null : String.valueOf(o); }
    private static boolean bool(Object o) {
        if (o instanceof Boolean b) return b;
        return o != null && Boolean.parseBoolean(String.valueOf(o));
    }
}
