package br.gov.sigefp.admin.service;

import br.gov.sigefp.admin.api.dto.UserAccountDTO;
import br.gov.sigefp.admin.domain.Role;
import br.gov.sigefp.admin.domain.UserAccount;
import br.gov.sigefp.admin.domain.UserRole;
import br.gov.sigefp.admin.repository.RoleRepository;
import br.gov.sigefp.admin.repository.UserAccountRepository;
import br.gov.sigefp.admin.repository.UserRoleRepository;
import br.gov.sigefp.common.service.GlobalAuditLogService;
import lombok.RequiredArgsConstructor;
import org.springframework.data.domain.Page;
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;
import java.util.UUID;
import java.util.stream.Collectors;

/**
 * Serviço de aplicação para gestão de utilizadores.
 */
@Service
@RequiredArgsConstructor
@Transactional
public class UserService {

    private final UserAccountRepository userAccountRepository;
    private final RoleRepository roleRepository;
    private final UserRoleRepository userRoleRepository;
    private final PasswordEncoder passwordEncoder;
    private final GlobalAuditLogService auditLogService;

    public UserAccountDTO create(UserAccountDTO dto) {
        if (userAccountRepository.existsByUsername(dto.getUsername())) {
            throw new IllegalArgumentException("Username já existe: " + dto.getUsername());
        }
        if (userAccountRepository.existsByEmail(dto.getEmail())) {
            throw new IllegalArgumentException("Email já existe: " + dto.getEmail());
        }

        UserAccount user = UserAccount.builder()
                .username(dto.getUsername())
                .fullName(dto.getFullName())
                .email(dto.getEmail())
                .passwordHash(passwordEncoder.encode(dto.getPassword()))
                .isActive(dto.getIsActive() != null ? dto.getIsActive() : true)
                .build();

        UserAccount saved = userAccountRepository.save(user);

        // Associar perfis se fornecidos
        if (dto.getRoleIds() != null && !dto.getRoleIds().isEmpty()) {
            assignRoles(saved.getId(), dto.getRoleIds());
        }

        return toDTO(saved);
    }

    public UserAccountDTO update(UUID id, UserAccountDTO dto) {
        UserAccount user = userAccountRepository.findById(id)
                .orElseThrow(() -> new IllegalArgumentException("Utilizador não encontrado: " + id));

        StringBuilder log = new StringBuilder();

        if (dto.getEmail() != null && !dto.getEmail().equals(user.getEmail())) {
            if (userAccountRepository.existsByEmail(dto.getEmail())) {
                throw new IllegalArgumentException("Email já existe: " + dto.getEmail());
            }
            log.append("Email: [").append(user.getEmail()).append("] -> [").append(dto.getEmail()).append("]. ");
            user.setEmail(dto.getEmail());
        }

        if (dto.getFullName() != null && !dto.getFullName().equals(user.getFullName())) {
            log.append("Nome: [").append(user.getFullName()).append("] -> [").append(dto.getFullName()).append("]. ");
            user.setFullName(dto.getFullName());
        }
        if (dto.getIsActive() != null && !dto.getIsActive().equals(user.getIsActive())) {
            log.append("Ativo: [").append(user.getIsActive()).append("] -> [").append(dto.getIsActive()).append("]. ");
            user.setIsActive(dto.getIsActive());
        }
        if (dto.getPassword() != null && !dto.getPassword().isEmpty()) {
            log.append("Senha alterada. ");
            user.setPasswordHash(passwordEncoder.encode(dto.getPassword()));
        }

        UserAccount saved = userAccountRepository.save(user);

        if (log.length() > 0) {
            // Nota: Em um cenário real, o userId viria do SecurityContextHolder
            auditLogService.logAction(id, saved.getUsername(), "ADMIN", "UPDATE", "UserAccount", id, log.toString());
        }

        return toDTO(saved);
    }

    @Transactional(readOnly = true)
    public UserAccountDTO findById(UUID id) {
        UserAccount user = userAccountRepository.findById(id)
                .orElseThrow(() -> new IllegalArgumentException("Utilizador não encontrado: " + id));
        return toDTO(user);
    }

    @Transactional(readOnly = true)
    public Page<UserAccountDTO> findAll(Pageable pageable) {
        Page<UserAccount> users = userAccountRepository.findAllWithRoles(pageable);
        return users.map(this::toDTO);
    }

    public void assignRoles(UUID userId, List<UUID> roleIds) {
        UserAccount user = userAccountRepository.findById(userId)
                .orElseThrow(() -> new IllegalArgumentException("Utilizador não encontrado: " + userId));

        // Remover perfis existentes
        userRoleRepository.deleteByUserId(userId);

        // Adicionar novos perfis
        for (UUID roleId : roleIds) {
            Role role = roleRepository.findById(roleId)
                    .orElseThrow(() -> new IllegalArgumentException("Perfil não encontrado: " + roleId));

            if (!userRoleRepository.existsByUserIdAndRoleId(userId, roleId)) {
                UserRole userRole = UserRole.builder()
                        .user(user)
                        .role(role)
                        .build();
                userRoleRepository.save(userRole);
            }
        }

        auditLogService.logAction(userId, user.getUsername(), "ADMIN", "ASSIGN_ROLES", "UserAccount", userId,
                "Perfis atualizados: " + roleIds.toString());
    }

    public void removeRoles(UUID userId, List<UUID> roleIds) {
        UserAccount user = userAccountRepository.findById(userId)
                .orElseThrow(() -> new IllegalArgumentException("Utilizador não encontrado: " + userId));

        for (UUID roleId : roleIds) {
            List<UserRole> userRoles = userRoleRepository.findByUserId(userId);
            userRoles.stream()
                    .filter(ur -> ur.getRole().getId().equals(roleId))
                    .forEach(userRoleRepository::delete);
        }
    }

    private UserAccountDTO toDTO(UserAccount user) {
        List<UUID> roleIds = List.of();
        try {
            // Tentar usar o relacionamento carregado primeiro
            if (user.getUserRoles() != null && !user.getUserRoles().isEmpty()) {
                roleIds = user.getUserRoles().stream()
                        .map(ur -> {
                            try {
                                return ur.getRole().getId();
                            } catch (Exception e) {
                                // Se houver erro de lazy loading, buscar via repository
                                return null;
                            }
                        })
                        .filter(id -> id != null)
                        .collect(Collectors.toList());
            }

            // Se não conseguiu via relacionamento, buscar via repository
            if (roleIds.isEmpty()) {
                roleIds = userRoleRepository.findByUserId(user.getId()).stream()
                        .map(ur -> ur.getRole().getId())
                        .collect(Collectors.toList());
            }
        } catch (Exception e) {
            System.err.println("Erro ao buscar roles para usuário " + user.getId() + ": " + e.getMessage());
            e.printStackTrace();
            roleIds = List.of();
        }

        return UserAccountDTO.builder()
                .id(user.getId())
                .username(user.getUsername())
                .fullName(user.getFullName())
                .email(user.getEmail())
                .isActive(user.getIsActive())
                .createdAt(user.getCreatedAt())
                .updatedAt(user.getUpdatedAt())
                .roleIds(roleIds)
                .build();
    }
}
