package sn.ladoum.bergerie.service;

import lombok.RequiredArgsConstructor;
import org.springframework.security.crypto.password.PasswordEncoder;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.web.multipart.MultipartFile;
import sn.ladoum.bergerie.dto.ProfilUpdateDto;
import sn.ladoum.bergerie.dto.UtilisateurDto;
import sn.ladoum.bergerie.entity.Utilisateur;
import sn.ladoum.bergerie.entity.enums.Role;
import sn.ladoum.bergerie.exception.ResourceNotFoundException;
import sn.ladoum.bergerie.repository.UtilisateurRepository;
import sn.ladoum.bergerie.storage.FileStorageService;

import java.util.List;

@Service
@RequiredArgsConstructor
@Transactional
public class UtilisateurService {

    private final UtilisateurRepository utilisateurRepository;
    private final PasswordEncoder passwordEncoder;
    private final FileStorageService fileStorageService;

    @Transactional(readOnly = true)
    public List<UtilisateurDto> findAll() {
        return utilisateurRepository.findAll().stream().map(this::toDto).toList();
    }

    /** Liste publique des éleveurs (id/nom/bergerie uniquement) pour le choix d'un propriétaire. */
    @Transactional(readOnly = true)
    public List<UtilisateurDto> findEleveurs() {
        return utilisateurRepository.findByRole(Role.ELEVEUR).stream()
                .map(u -> UtilisateurDto.builder()
                        .id(u.getId())
                        .nom(u.getNom())
                        .nomBergerie(u.getNomBergerie())
                        .role(u.getRole())
                        .email(u.getEmail())
                        .build())
                .toList();
    }

    @Transactional(readOnly = true)
    public UtilisateurDto findById(Long id) {
        return toDto(getEntity(id));
    }

    @Transactional(readOnly = true)
    public UtilisateurDto findByEmail(String email) {
        return toDto(utilisateurRepository.findByEmail(email)
                .orElseThrow(() -> new ResourceNotFoundException("Utilisateur non trouvé: " + email)));
    }

    public UtilisateurDto create(UtilisateurDto dto) {
        if (utilisateurRepository.existsByEmail(dto.getEmail())) {
            throw new IllegalArgumentException("Un utilisateur existe déjà avec cet email");
        }
        Utilisateur user = Utilisateur.builder()
                .nom(dto.getNom())
                .email(dto.getEmail())
                .motDePasse(passwordEncoder.encode(dto.getMotDePasse()))
                .role(dto.getRole())
                .actif(dto.getActif() != null ? dto.getActif() : true)
                .nomBergerie(dto.getNomBergerie())
                .logo(dto.getLogo())
                .adresse(dto.getAdresse())
                .telephone(dto.getTelephone())
                .build();
        return toDto(utilisateurRepository.save(user));
    }

    public UtilisateurDto bloquer(Long id) {
        Utilisateur user = getEntity(id);
        user.setActif(false);
        return toDto(utilisateurRepository.save(user));
    }

    public UtilisateurDto debloquer(Long id) {
        Utilisateur user = getEntity(id);
        user.setActif(true);
        return toDto(utilisateurRepository.save(user));
    }

    public UtilisateurDto update(Long id, UtilisateurDto dto) {
        Utilisateur user = getEntity(id);
        user.setNom(dto.getNom());
        user.setEmail(dto.getEmail());
        user.setRole(dto.getRole());
        user.setNomBergerie(dto.getNomBergerie());
        user.setLogo(dto.getLogo());
        user.setAdresse(dto.getAdresse());
        user.setTelephone(dto.getTelephone());
        if (dto.getMotDePasse() != null && !dto.getMotDePasse().isBlank()) {
            user.setMotDePasse(passwordEncoder.encode(dto.getMotDePasse()));
        }
        return toDto(utilisateurRepository.save(user));
    }

    public UtilisateurDto uploadLogo(Long id, MultipartFile file) {
        Utilisateur user = getEntity(id);
        if (user.getLogo() != null) {
            fileStorageService.delete(user.getLogo());
        }
        String url = fileStorageService.store(file, "logos");
        user.setLogo(url);
        return toDto(utilisateurRepository.save(user));
    }

    /**
     * Mise à jour du profil par l'utilisateur connecté lui-même (endpoint PUT /utilisateurs/me).
     * L'email et le rôle restent inchangés. Le téléphone doit rester unique.
     */
    public UtilisateurDto updateMe(String email, ProfilUpdateDto dto) {
        Utilisateur user = utilisateurRepository.findByEmail(email)
                .orElseThrow(() -> new ResourceNotFoundException("Utilisateur non trouvé: " + email));

        String nouveauTelephone = dto.getTelephone() != null ? dto.getTelephone().trim() : "";
        if (!nouveauTelephone.equals(user.getTelephone())) {
            if (utilisateurRepository.existsByTelephone(nouveauTelephone)) {
                throw new IllegalArgumentException("Un utilisateur existe déjà avec ce téléphone.");
            }
            user.setTelephone(nouveauTelephone);
        }

        user.setNom(dto.getNom());
        user.setNomBergerie(dto.getNomBergerie());
        user.setAdresse(dto.getAdresse());

        if (dto.getMotDePasse() != null && !dto.getMotDePasse().isBlank()) {
            if (dto.getMotDePasse().length() < 6) {
                throw new IllegalArgumentException("Le mot de passe doit contenir au moins 6 caractères.");
            }
            user.setMotDePasse(passwordEncoder.encode(dto.getMotDePasse()));
        }

        return toDto(utilisateurRepository.save(user));
    }

    public UtilisateurDto uploadLogoByEmail(String email, MultipartFile file) {
        Utilisateur user = utilisateurRepository.findByEmail(email)
                .orElseThrow(() -> new ResourceNotFoundException("Utilisateur non trouvé: " + email));
        return uploadLogo(user.getId(), file);
    }

    public void delete(Long id) {
        Utilisateur user = getEntity(id);
        if (user.getLogo() != null) {
            fileStorageService.delete(user.getLogo());
        }
        utilisateurRepository.delete(user);
    }

    private Utilisateur getEntity(Long id) {
        return utilisateurRepository.findById(id)
                .orElseThrow(() -> ResourceNotFoundException.of("Utilisateur", id));
    }

    private UtilisateurDto toDto(Utilisateur u) {
        return UtilisateurDto.builder()
                .id(u.getId())
                .nom(u.getNom())
                .email(u.getEmail())
                .role(u.getRole())
                .actif(u.getActif())
                .nomBergerie(u.getNomBergerie())
                .logo(u.getLogo())
                .adresse(u.getAdresse())
                .telephone(u.getTelephone())
                .build();
    }
}
