package de.thpeetz.kontor.admin.services;

import de.thpeetz.kontor.admin.data.Assignment;
import de.thpeetz.kontor.admin.data.Profile;
import de.thpeetz.kontor.admin.data.Permission;
import de.thpeetz.kontor.admin.repository.AssignmentRepository;
import de.thpeetz.kontor.admin.repository.ProfileRepository;
import de.thpeetz.kontor.admin.repository.PermissionRepository;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.security.core.GrantedAuthority;
import org.springframework.security.core.authority.SimpleGrantedAuthority;
import org.springframework.security.core.userdetails.UserDetails;
import org.springframework.security.core.userdetails.UserDetailsService;
import org.springframework.security.core.userdetails.UsernameNotFoundException;
import org.springframework.stereotype.Service;

import java.math.BigInteger;
import java.security.SecureRandom;
import java.util.Collection;
import java.util.List;
import java.util.stream.Collectors;

@Slf4j
@Service("userDetailsService")
public class KontorUserDetailsService implements UserDetailsService {

    private static final SecureRandom random = new SecureRandom();

    @Autowired
    private ProfileRepository profileRepository;

    @Autowired
    private PermissionRepository permissionRepository;

    @Autowired
    private AssignmentRepository assignmentRepository;

    public Collection<Profile> findAllProfiles(String stringFilter) {
        if (stringFilter == null || stringFilter.isEmpty()) {
            return profileRepository.findAll();
        } else {
            return profileRepository.search(stringFilter);
        }
    }

    public void saveProfile(Profile profile) {
        if (profile == null) {
            log.warn("Profile is null. Can't save it.");
            return;
        }
        log.info("saveProfile: profile={}", profile);
        profileRepository.save(profile);
    }

    public void saveProfile(Profile profile, List<Permission> permissions) {
        if (profile == null) {
            log.warn("Profile is null. Can't save it.");
            return;
        }
        log.info("First save Profile: {}", profile);
        profile = profileRepository.save(profile);
        List<Permission> copy = permissions.stream().collect(Collectors.toList());
        List<Assignment> assignments = profile.getAssignments();
        assignments.forEach(assignment -> {
            if (permissions.contains(assignment.getPermission())) {
                log.info("Permission {} already assigned", assignment.getPermission());
                copy.remove(assignment.getPermission());
            } else {
                log.info("Permission {} has to be removed", assignment.getPermission());
                assignmentRepository.delete(assignment);
            }
        });
        log.info("remaining roles: {}", copy);
        for (Permission permission : copy) {
            Assignment assignment = new Assignment();
            assignment.setProfile(profile);
            assignment.setPermission(permission);
            assignmentRepository.save(assignment);
        }
    }

    public void deleteProfile(Profile profile) {
        if (profile == null) {
            log.warn("Profile is null. Can't delete it.");
            return;
        }
        log.info("deleteProfile: profile={}", profile);
        profileRepository.delete(profile);
    }

    @Override
    public UserDetails loadUserByUsername(String userName) throws UsernameNotFoundException {

        log.info("loadUserByUsername: userName={}", userName);
        Profile profile = profileRepository.findByUserName(userName);
        if (profile == null) {
            log.info("User not found");
            return null;
        }

        Collection<? extends GrantedAuthority> authorities = getAuthorities(profile);
        log.info("Profile {} hat Permissions: {}", userName, authorities);
        return new org.springframework.security.core.userdetails.User(profile.getUserName(), profile.getPassword(),
                authorities);
    }

    private Collection<? extends GrantedAuthority> getAuthorities(Profile profile) {
        return assignmentRepository.findByProfile(profile).stream()
                .map(assignment -> assignment.getPermission().getName())
                .map(SimpleGrantedAuthority::new)
                .collect(Collectors.toList());
    }

    public String getRememberedUser(String id) {
        log.info("getRememberedUser: id={}", id);
        return "admin";
    }

    public String rememberUser(String username) {
        String randomId = new BigInteger(130, random).toString(32);
        log.info("rememberUser: username={}", username);
        return randomId;
    }

    public void removeRememberedUser(String id) {
        log.info("removeRememberedUser: id={}", id);
    }

    public List<Permission> findAllPermissions() {
        return permissionRepository.findAll();
    }
}
