package com.goldenchart.service;

import com.goldenchart.auth.UserRole;
import com.goldenchart.dto.*;
import com.goldenchart.entity.GcUser;
import com.goldenchart.repository.GcUserRepository;
import lombok.RequiredArgsConstructor;
import org.springframework.http.HttpStatus;
import org.springframework.security.crypto.password.PasswordEncoder;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.web.server.ResponseStatusException;

import java.util.List;

@Service
@RequiredArgsConstructor
public class AdminService {

    private final GcUserRepository userRepo;
    private final PasswordEncoder passwordEncoder;
    private final RolePermissionService rolePermissionService;

    @Transactional(readOnly = true)
    public void verifyAdminPassword(Long userId, String password) {
        rolePermissionService.requireAdmin(userId);
        if (password == null || password.isBlank()) {
            throw new ResponseStatusException(HttpStatus.BAD_REQUEST, "비밀번호를 입력하세요.");
        }
        GcUser user = userRepo.findById(userId)
                .orElseThrow(() -> new ResponseStatusException(HttpStatus.UNAUTHORIZED, "사용자를 찾을 수 없습니다."));
        if (!passwordEncoder.matches(password, user.getPasswordHash())) {
            throw new ResponseStatusException(HttpStatus.UNAUTHORIZED, "관리자 비밀번호가 올바르지 않습니다.");
        }
    }

    @Transactional(readOnly = true)
    public List<UserDto> listUsers() {
        return userRepo.findAll().stream().map(this::toDto).toList();
    }

    @Transactional
    public UserDto createUser(CreateUserRequest req) {
        if (req.getUsername() == null || req.getUsername().isBlank()) {
            throw new ResponseStatusException(HttpStatus.BAD_REQUEST, "아이디를 입력하세요.");
        }
        if (req.getPassword() == null || req.getPassword().isBlank()) {
            throw new ResponseStatusException(HttpStatus.BAD_REQUEST, "비밀번호를 입력하세요.");
        }
        String username = req.getUsername().trim();
        if (userRepo.findByUsername(username).isPresent()) {
            throw new ResponseStatusException(HttpStatus.CONFLICT, "이미 사용 중인 아이디입니다.");
        }
        String role = UserRole.fromString(req.getRole() != null ? req.getRole() : UserRole.USER.name()).name();
        GcUser user = GcUser.builder()
                .username(username)
                .passwordHash(passwordEncoder.encode(req.getPassword()))
                .displayName(req.getDisplayName() != null ? req.getDisplayName().trim() : username)
                .role(role)
                .enabled(req.getEnabled() == null || req.getEnabled())
                .build();
        return toDto(userRepo.save(user));
    }

    @Transactional
    public UserDto updateUser(Long id, UpdateUserRequest req, Long actorId) {
        GcUser user = userRepo.findById(id)
                .orElseThrow(() -> new ResponseStatusException(HttpStatus.NOT_FOUND, "사용자를 찾을 수 없습니다."));
        if (req.getDisplayName() != null) {
            user.setDisplayName(req.getDisplayName().trim());
        }
        if (req.getRole() != null) {
            user.setRole(UserRole.fromString(req.getRole()).name());
        }
        if (req.getEnabled() != null) {
            user.setEnabled(req.getEnabled());
        }
        if (req.getPassword() != null && !req.getPassword().isBlank()) {
            user.setPasswordHash(passwordEncoder.encode(req.getPassword()));
        }
        // 마지막 ADMIN 비활성화/강등 방지
        if (UserRole.ADMIN.name().equalsIgnoreCase(user.getRole())
                && (!Boolean.TRUE.equals(user.getEnabled())
                || (req.getRole() != null && !UserRole.ADMIN.name().equalsIgnoreCase(req.getRole())))) {
            long adminCount = userRepo.findAll().stream()
                    .filter(u -> UserRole.ADMIN.name().equalsIgnoreCase(u.getRole())
                            && Boolean.TRUE.equals(u.getEnabled())
                            && !u.getId().equals(id))
                    .count();
            if (adminCount == 0) {
                throw new ResponseStatusException(HttpStatus.BAD_REQUEST, "최소 한 명의 활성 관리자가 필요합니다.");
            }
        }
        return toDto(userRepo.save(user));
    }

    @Transactional
    public void deleteUser(Long id, Long actorId) {
        if (id.equals(actorId)) {
            throw new ResponseStatusException(HttpStatus.BAD_REQUEST, "본인 계정은 삭제할 수 없습니다.");
        }
        GcUser user = userRepo.findById(id)
                .orElseThrow(() -> new ResponseStatusException(HttpStatus.NOT_FOUND, "사용자를 찾을 수 없습니다."));
        if (UserRole.ADMIN.name().equalsIgnoreCase(user.getRole())) {
            long adminCount = userRepo.findAll().stream()
                    .filter(u -> UserRole.ADMIN.name().equalsIgnoreCase(u.getRole())
                            && Boolean.TRUE.equals(u.getEnabled()))
                    .count();
            if (adminCount <= 1) {
                throw new ResponseStatusException(HttpStatus.BAD_REQUEST, "최소 한 명의 관리자가 필요합니다.");
            }
        }
        userRepo.delete(user);
    }

    private UserDto toDto(GcUser u) {
        return UserDto.builder()
                .id(u.getId())
                .username(u.getUsername())
                .displayName(u.getDisplayName() != null ? u.getDisplayName() : u.getUsername())
                .role(u.getRole() != null ? u.getRole() : UserRole.USER.name())
                .enabled(u.getEnabled())
                .createdAt(u.getCreatedAt() != null ? u.getCreatedAt().toString() : null)
                .build();
    }
}
