package com.saas.admin.service;

import com.saas.admin.dto.request.CreateUserRequest;
import com.saas.admin.dto.request.UpdateUserRequest;
import com.saas.admin.dto.response.UserResponse;
import com.saas.admin.entity.User;
import com.saas.admin.repository.UserRepository;
import com.saas.shared.audit.Auditable;
import com.saas.shared.dto.common.PageResponse;
import com.saas.shared.dto.mapper.UserMapper;
import com.saas.shared.enums.UserType;
import com.saas.shared.exception.BusinessException;
import com.saas.shared.exception.ErrorCode;
import com.saas.shared.exception.ResourceNotFoundException;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.data.domain.Page;
import org.springframework.data.domain.PageRequest;
import org.springframework.data.domain.Pageable;
import org.springframework.security.crypto.password.PasswordEncoder;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import java.util.List;

@Service
@Slf4j
@RequiredArgsConstructor
public class UserService {

    private final UserRepository userRepository;
    private final UserMapper userMapper;
    private final PasswordEncoder passwordEncoder;

    @Transactional
    @Auditable(action = "CREATE_USER", entityType = "USER")
    public UserResponse createUser(CreateUserRequest request) {
        log.info("Creating user with email: {}", request.getEmail());

        if (userRepository.existsByEmail(request.getEmail())) {
            throw new BusinessException(ErrorCode.USER_ALREADY_EXISTS,
                    "User with email " + request.getEmail() + " already exists");
        }

        if (request.getUserType() == UserType.TENANT_USER && request.getTenantId() == null) {
            throw new BusinessException(ErrorCode.INVALID_INPUT,
                    "Tenant ID is required for TENANT_USER");
        }

        User user = userMapper.toEntity(request);
        user.setPassword(passwordEncoder.encode(request.getPassword()));

        // Ensure UserType is set (default to TENANT_USER if null)
        if (user.getUserType() == null) {
            user.setUserType(UserType.TENANT_USER);
        }

        user.setRole(request.getRole() != null ? request.getRole() : "USER");
        user.setStatus("ACTIVE");

        User savedUser = userRepository.save(user);
        log.info("User created successfully with ID: {}", savedUser.getId());

        return userMapper.toResponse(savedUser);
    }

    @Transactional(readOnly = true)
    public UserResponse getUserById(Long id) {
        log.debug("Fetching user with ID: {}", id);
        return userRepository.findById(id)
                .map(userMapper::toResponse)
                .orElseThrow(() -> new ResourceNotFoundException("User", id));
    }

    @Transactional(readOnly = true)
    public List<UserResponse> getAllUsers() {
        log.debug("Fetching all users");
        return userMapper.toResponseList(userRepository.findAll());
    }

    @Transactional(readOnly = true)
    public PageResponse<UserResponse> getUsers(int page, int size) {
        log.debug("Fetching users - page: {}, size: {}", page, size);
        Pageable pageable = PageRequest.of(page, size);
        Page<User> userPage = userRepository.findAll(pageable);

        return PageResponse.<UserResponse>builder()
                .content(userMapper.toResponseList(userPage.getContent()))
                .page(page)
                .size(size)
                .totalElements(userPage.getTotalElements())
                .totalPages(userPage.getTotalPages())
                .hasNext(userPage.hasNext())
                .hasPrevious(userPage.hasPrevious())
                .build();
    }

    @Transactional
    @Auditable(action = "UPDATE_USER", entityType = "USER")
    public UserResponse updateUser(Long id, UpdateUserRequest request) {
        log.info("Updating user with ID: {}", id);

        User user = userRepository.findById(id)
                .orElseThrow(() -> new ResourceNotFoundException("User", id));

        userMapper.updateEntity(request, user);

        User updatedUser = userRepository.save(user);
        log.info("User updated successfully with ID: {}", updatedUser.getId());

        return userMapper.toResponse(updatedUser);
    }

    @Transactional
    @Auditable(action = "DELETE_USER", entityType = "USER")
    public void deleteUser(Long id) {
        log.info("Deleting user with ID: {}", id);

        if (!userRepository.existsById(id)) {
            throw new ResourceNotFoundException("User", id);
        }

        userRepository.deleteById(id);
        log.info("User deleted successfully with ID: {}", id);
    }
}
