package de.avatic.lcc.service.users;

import de.avatic.lcc.config.LccOidcUser;
import de.avatic.lcc.dto.users.UserDTO;
import de.avatic.lcc.model.db.users.Group;
import de.avatic.lcc.repositories.pagination.SearchQueryPagination;
import de.avatic.lcc.repositories.pagination.SearchQueryResult;
import de.avatic.lcc.repositories.users.UserRepository;
import de.avatic.lcc.service.transformer.users.UserTransformer;
import de.avatic.lcc.util.exception.badrequest.NotFoundException;
import de.avatic.lcc.util.exception.base.BadRequestException;
import jakarta.validation.constraints.NotNull;
import org.hibernate.validator.constraints.Length;
import org.springframework.security.core.Authentication;
import org.springframework.security.core.context.SecurityContextHolder;
import org.springframework.stereotype.Service;

import java.util.*;
import java.util.stream.Collectors;

/**
 * Service class responsible for handling business logic related to users.
 * Provides methods to retrieve and update user information.
 */
@Service
public class UserService {

    private final UserRepository userRepository;
    private final UserTransformer userTransformer;
    private final UserAuthorityCacheService userAuthorityCacheService;

    public UserService(UserRepository userRepository, UserTransformer userTransformer, UserAuthorityCacheService userAuthorityCacheService) {
        this.userRepository = userRepository;
        this.userTransformer = userTransformer;
        this.userAuthorityCacheService = userAuthorityCacheService;
    }

    /**
     * Retrieves a paginated list of users.
     *
     * @param page  the page number to retrieve
     * @param limit the number of users per page
     * @return a SearchQueryResult containing the paginated user data
     */
    public SearchQueryResult<UserDTO> listUsers(int page, int limit) {
        return SearchQueryResult.map(userRepository.listUsers(new SearchQueryPagination(page, limit)), userTransformer::toUserDTO);
    }

    /**
     * Retrieves a single user by its workday id.
     *
     * @param workdayId The workday id of the user to retrieve.
     * @return A ResponseEntity containing the user, along with pagination headers.
     */
    public Optional<UserDTO> getUser(String workdayId) {
        return userRepository.getByWorkdayId(workdayId).map(userTransformer::toUserDTO);
    }

    /**
     * Updates an existing user's information.
     *
     * @param user the UserDTO containing updated user information
     */
    public void updateUser(UserDTO user) {
        var userId = userRepository.update(userTransformer.fromUserDTO(user));
        userAuthorityCacheService.invalidateUserAuthorities(userId);
    }


    public void appendGroups(String workdayId, String group) {
        var optUser = userRepository.getByWorkdayId(workdayId);

        if (optUser.isEmpty()) {
            throw new NotFoundException(NotFoundException.NotFoundType.USER, "workday id", workdayId);
        }
        var user = optUser.get();

        var existingGroups = new TreeSet<>(Comparator.comparing(Group::getName));
        existingGroups.addAll(user.getGroups());
        existingGroups.add(userTransformer.fromGroupDTO(group));
        user.setGroups(new ArrayList<>(existingGroups));

        userRepository.update(user);
    }

    public void deleteGroups(String workdayId, String groupToDelete) {
        var optUser = userRepository.getByWorkdayId(workdayId);

        if (optUser.isEmpty()) throw new NotFoundException(NotFoundException.NotFoundType.USER, "workday id", workdayId);
        var user = optUser.get();

        List<Group> uniqueGroups = user.getGroups().stream()
                .filter(group -> !group.getName().equals(groupToDelete))
                .collect(Collectors.toMap(
                        Group::getName,
                        group -> group,
                        (existing, replacement) -> existing,
                        LinkedHashMap::new
                ))
                .values()
                .stream()
                .toList();

        user.setGroups(uniqueGroups);
        userRepository.update(user);
    }

}
