package com.psg.dlsysinfo.dl_sysinfo_server.service;

import com.psg.dlsysinfo.dl_sysinfo_server.dto.ChangePasswordRequest;
import com.psg.dlsysinfo.dl_sysinfo_server.dto.UserDTO;
import com.psg.dlsysinfo.dl_sysinfo_server.dto.UserProfileDTO;
import com.psg.dlsysinfo.dl_sysinfo_server.entity.Client;
import com.psg.dlsysinfo.dl_sysinfo_server.entity.UserAuth;
import com.psg.dlsysinfo.dl_sysinfo_server.repository.ClientRepository;
import com.psg.dlsysinfo.dl_sysinfo_server.repository.UserAuthRepository;
import com.psg.dlsysinfo.dl_sysinfo_server.security.EncryptionService;
import jakarta.transaction.Transactional;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Primary;
import org.springframework.security.core.userdetails.User;
import org.springframework.security.core.userdetails.UserDetails;
import org.springframework.security.core.userdetails.UsernameNotFoundException;
import org.springframework.security.crypto.password.PasswordEncoder;
import org.springframework.stereotype.Service;

import java.time.LocalDateTime;
import java.util.List;
import java.util.Optional;
import java.util.stream.Collectors;

@Service
@Primary
@Transactional
public class UserService implements CustomUserDetailsService {

    private final UserAuthRepository userAuthRepository;
    private final PasswordEncoder passwordEncoder;

    @Autowired
    private EncryptionService encryptionService;

    @Autowired
    public UserService(UserAuthRepository userAuthRepository, PasswordEncoder passwordEncoder) {
        this.userAuthRepository = userAuthRepository;
        this.passwordEncoder = passwordEncoder;
    }

    @Autowired
    private ClientRepository clientRepository;

    public UserAuth registerUser(String username, String password, String role, Long clientId) {
        if (password == null || password.isEmpty()) {
            throw new IllegalArgumentException("Password cannot be null or empty");
        }

        String hashedPassword = passwordEncoder.encode(password);

        UserAuth userAuth = new UserAuth();
        userAuth.setUsername(username);
        userAuth.setPasswordHash(hashedPassword);
        userAuth.setRole(role);
        userAuth.setCreatedAt(java.time.LocalDateTime.now());

        Client client = clientRepository.findById(clientId)
                .orElseThrow(() -> new IllegalArgumentException("Invalid client ID"));
        userAuth.setClient(client);

        return userAuthRepository.save(userAuth);
    }

    @Override
    public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException {
        Optional<UserAuth> userOpt = userAuthRepository.findByUsername(username);
        UserAuth userAuth = userOpt.orElseThrow(() -> new UsernameNotFoundException("User not found"));

        return User.builder()
                .username(userAuth.getUsername())
                .password(userAuth.getPasswordHash())
                .roles(userAuth.getRole())
                .build();
    }

    public UserAuth findByUsername(String username) {
        return userAuthRepository.findByUsernameWithClient(username)
                .orElse(null);
    }

    public boolean changePassword(String username, ChangePasswordRequest request) {
        Optional<UserAuth> userOpt = userAuthRepository.findByUsername(username);
        if (userOpt.isPresent()) {
            UserAuth user = userOpt.get();

            try {
                String decryptedCurrentPassword = encryptionService.decryptData(request.getCurrentPassword());
                String decryptedNewPassword = encryptionService.decryptData(request.getNewPassword());

                if (passwordEncoder.matches(decryptedCurrentPassword, user.getPasswordHash())) {
                    user.setPasswordHash(passwordEncoder.encode(decryptedNewPassword));
                    user.setPasswordChangedAt(LocalDateTime.now());
                    userAuthRepository.save(user);
                    return true;
                }

            } catch (Exception e) {
                System.out.println("❌ Decryption failed: " + e.getMessage());
            }
        }
        return false;
    }

    public UserProfileDTO getUserProfile(String username) {
        UserAuth user = userAuthRepository.findByUsername(username)
                .orElseThrow(() -> new UsernameNotFoundException("User not found"));

        try {
            return new UserProfileDTO(
                    user.getUsername(),
                    user.getDisplayNameHash() != null ? encryptionService.decryptData(user.getDisplayNameHash()) : "",
                    user.getFirstNameHash() != null ? encryptionService.decryptData(user.getFirstNameHash()) : "",
                    user.getLastNameHash() != null ? encryptionService.decryptData(user.getLastNameHash()) : "",
                    user.getEmailHash() != null ? encryptionService.decryptData(user.getEmailHash()) : ""
            );
        } catch (Exception e) {
            throw new RuntimeException("Failed to decrypt user profile data", e);
        }
    }

    public void updateUserProfile(String username, UserProfileDTO profileDto) {
        UserAuth user = userAuthRepository.findByUsername(username)
                .orElseThrow(() -> new UsernameNotFoundException("User not found"));

        try {
            user.setDisplayNameHash(encryptionService.encryptData(profileDto.getDisplayName()));
            user.setFirstNameHash(encryptionService.encryptData(profileDto.getFirstName()));
            user.setLastNameHash(encryptionService.encryptData(profileDto.getLastName()));
            user.setEmailHash(encryptionService.encryptData(profileDto.getEmail()));
        } catch (Exception e) {
            throw new RuntimeException("Failed to encrypt user profile fields", e);
        }

        userAuthRepository.save(user);
    }

    public List<UserDTO> getAllDecryptedUsers() {
        return userAuthRepository.findAll().stream()
                .map(user -> {
                    try {
                        return new UserDTO(
                                user.getId(),
                                user.getUsername(),
                                encryptionService.decryptData(user.getDisplayNameHash()),
                                encryptionService.decryptData(user.getFirstNameHash()),
                                encryptionService.decryptData(user.getLastNameHash()),
                                encryptionService.decryptData(user.getEmailHash()),
                                user.getRole(),
                                user.getClient().getClientIdentifier(),
                                encryptionService.decryptData(user.getClient().getClientNameEncrypted()),
                                user.isEnabled()
                        );
                    } catch (Exception e) {
                        throw new RuntimeException("Failed to decrypt user data for user: " + user.getUsername(), e);
                    }
                })
                .collect(Collectors.toList());
    }

    public UserAuth save(UserAuth user) {
        return userAuthRepository.save(user);
    }

    public void setUserEnabledState(Long userId, boolean enabled) {
        UserAuth user = userAuthRepository.findById(userId)
                .orElseThrow(() -> new UsernameNotFoundException("User not found"));
        user.setEnabled(enabled);
        userAuthRepository.save(user);
    }

}
