package kr.co.accura.wtm.domain.user.service;

import kr.co.accura.wbx.spring.common.BusinessException;
import kr.co.accura.wbx.spring.common.NotFoundException;
import kr.co.accura.wtm.domain.user.EmploymentType;
import kr.co.accura.wtm.domain.user.dto.*;
import kr.co.accura.wtm.domain.user.entity.Role;
import kr.co.accura.wtm.domain.user.entity.User;
import kr.co.accura.wtm.domain.user.entity.UserRole;
import kr.co.accura.wtm.domain.user.repository.RoleRepository;
import kr.co.accura.wtm.domain.user.repository.UserRepository;
import kr.co.accura.wtm.domain.user.repository.UserRoleRepository;
import lombok.RequiredArgsConstructor;
import org.springframework.data.domain.Page;
import org.springframework.data.domain.Pageable;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import java.util.List;

@Service
@Transactional
@RequiredArgsConstructor
public class UserService {

    private final UserRepository userRepository;
    private final RoleRepository roleRepository;
    private final UserRoleRepository userRoleRepository;

    @Transactional(readOnly = true)
    public Page<UserDto> findAll(Pageable pageable) {
        return userRepository.findAll(pageable).map(UserDto::from);
    }

    @Transactional(readOnly = true)
    public UserDto findById(Long id) {
        User user = userRepository.findById(id)
                .orElseThrow(() -> new NotFoundException("User not found: " + id));
        return UserDto.from(user);
    }

    @Transactional(readOnly = true)
    public UserDto findByEmail(String email) {
        User user = userRepository.findByEmail(email)
                .orElseThrow(() -> new NotFoundException("User not found: " + email));
        return UserDto.from(user);
    }

    public UserDto create(UserCreateRequest request) {
        if (userRepository.existsByEmail(request.email())) {
            throw new BusinessException("Email already exists: " + request.email());
        }
        if (userRepository.existsByEmployeeId(request.employeeId())) {
            throw new BusinessException("Employee ID already exists: " + request.employeeId());
        }

        User user = User.builder()
                .employeeId(request.employeeId())
                .email(request.email())
                .username(request.username())
                .fullName(request.fullName())
                .department(request.department())
                .discipline(request.discipline())
                .positionTitle(request.positionTitle())
                .location(request.location())
                .employmentType(request.employmentType() != null
                        ? EmploymentType.valueOf(request.employmentType())
                        : EmploymentType.INTERNAL)
                .build();

        userRepository.save(user);
        return UserDto.from(user);
    }

    public UserDto update(Long id, UserUpdateRequest request) {
        User user = userRepository.findById(id)
                .orElseThrow(() -> new NotFoundException("User not found: " + id));

        if (request.username() != null) user.setUsername(request.username());
        if (request.fullName() != null) user.setFullName(request.fullName());
        if (request.department() != null) user.setDepartment(request.department());
        if (request.discipline() != null) user.setDiscipline(request.discipline());
        if (request.positionTitle() != null) user.setPositionTitle(request.positionTitle());
        if (request.location() != null) user.setLocation(request.location());
        if (request.employmentType() != null) {
            user.setEmploymentType(EmploymentType.valueOf(request.employmentType()));
        }
        if (request.isActive() != null) user.setIsActive(request.isActive());

        userRepository.save(user);
        return UserDto.from(user);
    }

    public void assignRoles(Long userId, RoleAssignRequest request) {
        User user = userRepository.findById(userId)
                .orElseThrow(() -> new NotFoundException("User not found: " + userId));

        // Remove existing roles
        userRoleRepository.deleteByUser_IdAndProjectId(userId, null);

        // Assign new roles
        for (RoleAssignRequest.RoleEntry entry : request.roles()) {
            Role role = roleRepository.findByCode(entry.roleCode())
                    .orElseThrow(() -> new NotFoundException("Role not found: " + entry.roleCode()));

            UserRole userRole = UserRole.builder()
                    .user(user)
                    .role(role)
                    .projectId(entry.projectId())
                    .build();
            userRoleRepository.save(userRole);
        }
    }

    @Transactional(readOnly = true)
    public List<UserRole> getUserRoles(Long userId) {
        return userRoleRepository.findByUser_Id(userId);
    }
}
