content: package br.com.alevhvm.adotai.administrador.service;
  import org.slf4j.Logger;
  import org.slf4j.LoggerFactory;
  import org.springframework.data.domain.Page;
  import org.springframework.data.domain.Pageable;
  import org.springframework.data.web.PagedResourcesAssembler;
  import org.springframework.hateoas.EntityModel;
  import org.springframework.hateoas.Link;
  import org.springframework.hateoas.PagedModel;
  import org.springframework.security.authentication.AuthenticationManager;
  import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
  import org.springframework.security.crypto.password.PasswordEncoder;
  import org.springframework.stereotype.Service;
  import org.springframework.transaction.annotation.Transactional;
  import org.springframework.util.ReflectionUtils;
  
  import com.fasterxml.jackson.databind.ObjectMapper;
  
  import static org.springframework.hateoas.server.mvc.WebMvcLinkBuilder.linkTo;
  import static org.springframework.hateoas.server.mvc.WebMvcLinkBuilder.methodOn;
  
  import java.lang.reflect.Field;
  import java.util.Map;
  import java.util.Set;
  
  import br.com.alevhvm.adotai.administrador.controller.AdministradorController;
  import br.com.alevhvm.adotai.auth.dto.LoginDTO;
  import br.com.alevhvm.adotai.auth.dto.TokenDTO;
  import br.com.alevhvm.adotai.administrador.dto.AdministradorDTO;
  import br.com.alevhvm.adotai.administrador.dto.AdministradorUpdateDTO;
  import br.com.alevhvm.adotai.administrador.exception.AdmIsMasterException;
  import br.com.alevhvm.adotai.administrador.exception.AdministradorNotFoundException;
  import br.com.alevhvm.adotai.common.enums.StatusConta;
  import br.com.alevhvm.adotai.common.mapper.DozerMapper;
  import br.com.alevhvm.adotai.administrador.model.Administrador;
  import br.com.alevhvm.adotai.auth.model.LoginIdentityView;
  import br.com.alevhvm.adotai.administrador.repository.AdministradorRepository;
  import br.com.alevhvm.adotai.auth.enums.Roles;
  import br.com.alevhvm.adotai.auth.service.TokenService;
  import br.com.alevhvm.adotai.administrador.validations.AdministradorValidacao;
  import jakarta.validation.ConstraintViolation;
  import jakarta.validation.ConstraintViolationException;
  import jakarta.validation.Validator;
  import lombok.RequiredArgsConstructor;
  
  @Service
  @RequiredArgsConstructor
  public class AdministradorService {
  
      private static final Logger logger = LoggerFactory.getLogger(AdministradorService.class);
  
      private final AdministradorValidacao administradorValidacao;
  
      private final AdministradorRepository administradorRepository;
  
      private final PasswordEncoder passwordEncoder;
  
      private final PagedResourcesAssembler<AdministradorDTO> assembler;
  
      private final Validator validator;
  
      private final AuthenticationManager authenticationManager;
  
      private final TokenService tokenService;
  
      @Transactional(readOnly = true)
      public PagedModel<EntityModel<AdministradorDTO>> findAll(Pageable pageable) {
          logger.debug("Iniciando busca de todos os administradores");
  
          Page<Administrador> administradorPage = administradorRepository.findAllByStatus(StatusConta.ATIVA, pageable);
  
          logger.info("Encontrada(s) {} página(s), com {} Administrador(es)", administradorPage.getTotalPages(), administradorPage.getTotalElements());
  
          Page<AdministradorDTO> administradorDtosPage = administradorPage
                  .map(a -> DozerMapper.parseObject(a, AdministradorDTO.class));
          administradorDtosPage
                  .map(u -> u.add(linkTo(methodOn(AdministradorController.class).acharAdministradorPorId(u.getKey()))
                          .withSelfRel()));
  
          Link link = linkTo(methodOn(AdministradorController.class).listarAdministradores(pageable.getPageNumber(),
                  pageable.getPageSize(), "asc")).withSelfRel();
          return assembler.toModel(administradorDtosPage, link);
      }
  
      @Transactional
      public AdministradorDTO create(AdministradorDTO administradorDTO) {
          logger.debug("Iniciando a criação de um Administrador");
  
          administradorValidacao.validate(administradorDTO);
  
          Administrador entity = DozerMapper.parseObject(administradorDTO, Administrador.class);
          entity.setRole(Roles.ADMIN);
          entity.setSenha(passwordEncoder.encode(administradorDTO.getSenha()));
          entity.setEmail(administradorDTO.getEmail().toLowerCase());
  
          AdministradorDTO dto = DozerMapper.parseObject(administradorRepository.save(entity), AdministradorDTO.class);
          dto.add(
                  linkTo(
                          methodOn(AdministradorController.class).acharAdministradorPorId(dto.getKey())).withSelfRel());
  
          logger.info("Administrador {} criado com sucesso.", entity.getNomeUsuario());
          return dto;
      }
  
      @Transactional(readOnly = true)
      public AdministradorDTO findById(Long id) {
          logger.debug("Iniciando busca do administrador com id = {}", id);
  
          Administrador entity = administradorRepository.findById(id)
                  .orElseThrow(() -> {
                      logger.warn("Administrador não encontrado para id = {}", id);
                      return new AdministradorNotFoundException("Administrador não encontrado.");
                  });
  
          AdministradorDTO dto = DozerMapper.parseObject(entity, AdministradorDTO.class);
          dto.add(linkTo(methodOn(AdministradorController.class).acharAdministradorPorId(id)).withSelfRel());
  
          logger.info("Administrador encontrado com sucesso: id={}, nomeUsuario={}", entity.getIdAdministrador(), entity.getNomeUsuario());
          return dto;
      }
  
      @Transactional
      public AdministradorDTO update(AdministradorUpdateDTO administradorUpdateDTO, String nomeUsuario) {
          logger.debug("Iniciando atualização de administrador com nomeUsuario = {}", nomeUsuario);
  
          Administrador entity = getAdministradorEntityByNomeUsuario(nomeUsuario);
  
          entity.setNome(administradorUpdateDTO.getNome());
          entity.setFotoPerfil(administradorUpdateDTO.getFotoPerfil());
          entity.setEmail(administradorUpdateDTO.getEmail().toLowerCase());
          entity.setSenha(passwordEncoder.encode(administradorUpdateDTO.getSenha()));
          entity.setCell(administradorUpdateDTO.getCell());
  
          administradorValidacao.validateUpdate(entity);
  
          AdministradorDTO dto = DozerMapper.parseObject(administradorRepository.save(entity), AdministradorDTO.class);
          dto.add(linkTo(methodOn(AdministradorController.class).acharAdministradorPorId(dto.getKey())).withSelfRel());
  
          logger.info("Administrador {} atualizado com sucesso.", entity.getNomeUsuario());
          return dto;
      }
  
      @Transactional(readOnly = true)
      public AdministradorDTO findByNomeUsuario(String nomeUsuario) {
          logger.debug("Iniciando busca do administrador com nomeUsuario = {}", nomeUsuario);
  
          Administrador entity = getAdministradorEntityByNomeUsuario(nomeUsuario);
  
          AdministradorDTO dto = DozerMapper.parseObject(entity, AdministradorDTO.class);
          dto.add(linkTo(methodOn(AdministradorController.class).acharAdministradorPorNomeUsuario(nomeUsuario))
                  .withSelfRel());
  
          logger.info("Administrador encontrado com sucesso: id={}, nomeUsuario={}", entity.getIdAdministrador(), entity.getNomeUsuario());
          return dto;
      }
  
      @Transactional
      public AdministradorDTO partialUpdate(String nomeUsuario, Map<String, Object> updates) {
          logger.debug("Iniciando atualização parcial do administrador com nomeUsuario = {}", nomeUsuario);
  
          Administrador administrador = getAdministradorEntityByNomeUsuario(nomeUsuario);
  
          administradorValidacao.validatePartialUpdate(nomeUsuario, updates);
  
          ObjectMapper mapper = new ObjectMapper();
          updates.forEach((campo, valor) -> {
              Field field = ReflectionUtils.findField(Administrador.class, campo);
              if (field != null) {
                  field.setAccessible(true);
  
                  if (campo.equalsIgnoreCase("email") && valor instanceof String) {
                      valor = ((String) valor).toLowerCase();
                  }
  
                  ReflectionUtils.setField(field, administrador, mapper.convertValue(valor, field.getType()));
              }
          });
  
          AdministradorDTO administradorDTO = DozerMapper.parseObject(administrador, AdministradorDTO.class);
  
          Set<ConstraintViolation<AdministradorDTO>> violations = validator.validate(administradorDTO);
  
          if (!violations.isEmpty()) {
              StringBuilder errors = new StringBuilder();
              for (ConstraintViolation<AdministradorDTO> violation : violations) {
                  errors.append(violation.getMessage());
              }
              throw new ConstraintViolationException("Erro de validação: " + errors.toString(), violations);
          }
  
          administradorRepository.save(administrador);
          
          AdministradorDTO dto = DozerMapper.parseObject(administrador, AdministradorDTO.class);
          dto.add(linkTo(methodOn(AdministradorController.class).acharAdministradorPorNomeUsuario(nomeUsuario)).withSelfRel());
  
          logger.info("Administrador {} atualizado parcialmente com sucesso.", administrador.getNomeUsuario());
          return dto;
      }
  
      public TokenDTO logar(LoginDTO data) {
          logger.debug("Iniciando login do administrador {}", data.identifier());
  
          String identifier = data.identifier();
  
          if (identifier.contains("@")) {
              identifier = identifier.toLowerCase();
          }
  
          var usernamePassword = new UsernamePasswordAuthenticationToken(identifier, data.password());
  
          var auth = this.authenticationManager.authenticate(usernamePassword);
  
          var token = tokenService.generateToken((LoginIdentityView) auth.getPrincipal());
  
          logger.info("Administrador {} logado com sucesso.", data.identifier());
          return new TokenDTO(token);
      }
  
      @Transactional
      public void delete(String nomeUsuario) {
          logger.debug("Iniciando delecao do administrador {}", nomeUsuario);
          var admin = getAdministradorEntityByNomeUsuario(nomeUsuario);
          administradorRepository.delete(admin);
          logger.info("Administrador {} deletado com sucesso.", nomeUsuario);
      }
  
      @Transactional
      public AdministradorDTO atualizarAdmNormalParaMaster(String nomeUsuario) {
          logger.debug("Iniciando atualizacao do administrador normal {} para administrador master", nomeUsuario);
  
          Administrador entity = getAdministradorEntityByNomeUsuario(nomeUsuario);
  
          if (entity.getRole() == Roles.ADMINMASTER) {
              logger.warn("O Administrador {} já é Master", nomeUsuario);
              throw new AdmIsMasterException("Esse administrador já é ADMINMASTER.");
          }
  
          entity.setRole(Roles.ADMINMASTER);
  
          AdministradorDTO dto = DozerMapper.parseObject(administradorRepository.save(entity), AdministradorDTO.class);
          dto.add(linkTo(methodOn(AdministradorController.class).acharAdministradorPorId(dto.getKey())).withSelfRel());
          
          logger.info("Administrador {} atualizado para Master com sucesso.", nomeUsuario);
          return dto;
      }
  
      @Transactional
      public String toggleStatus(String nomeUsuario) {
          Administrador adm = getAdministradorEntityByNomeUsuario(nomeUsuario);
  
          if (adm.getStatus() == StatusConta.ATIVA) {
              logger.debug("Iniciando desativacao da conta {}", nomeUsuario);
              administradorRepository.desativarAdministrador(nomeUsuario);
              logger.info("Conta {} desativada com sucesso", nomeUsuario);
          } else {
              logger.debug("Iniciando ativacao da conta {}", nomeUsuario);
              administradorRepository.ativarAdministrador(nomeUsuario);
              logger.info("Conta {} ativada com sucesso", nomeUsuario);
          }
  
          return "Status atualizado com sucesso!";
      }
  
      @Transactional
      public StatusConta bloquearConta(String nomeUsuario) {
          logger.debug("Iniciando o bloqueio do administrador {}", nomeUsuario);
          administradorRepository.bloquearAdministrador(nomeUsuario);
          logger.info("Administrador {} bloqueado com sucesso", nomeUsuario);
          return StatusConta.BLOQUEADA;
      }
  
      @Transactional(readOnly = true)
      public Administrador getAdministradorEntityByNomeUsuario(String nomeUsuario) {
          return administradorRepository.findByNomeUsuario(nomeUsuario)
                  .orElseThrow(() -> {
                      logger.warn("Administrador não encontrado para nomeUsuario = {}", nomeUsuario);
                      return new AdministradorNotFoundException("Administrador não encontrado.");
                  });
      }
  }
  