package dev.lions.btpxpress.view;

import dev.lions.btpxpress.service.ChantierService;
import jakarta.annotation.PostConstruct;
import jakarta.faces.view.ViewScoped;
import jakarta.inject.Inject;
import jakarta.inject.Named;
import lombok.Getter;
import lombok.Setter;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import java.time.LocalDate;
import java.time.LocalDateTime;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.function.Predicate;

@Named("chantiersView")
@ViewScoped
@Getter
@Setter
public class ChantiersView extends BaseListView<ChantiersView.Chantier, Long> {

    private static final Logger LOG = LoggerFactory.getLogger(ChantiersView.class);

    @Inject
    ChantierService chantierService;

    private String filtreNom;
    private String filtreClient;
    private String filtreStatut;
    private Long chantierId;

    @PostConstruct
    public void init() {
        if (filtreStatut == null) {
            filtreStatut = "TOUS";
        }
        loadItems();
    }

    /**
     * Définit le filtre de statut (utilisé depuis les pages filtrées).
     */
    public void setFiltreStatut(String statut) {
        this.filtreStatut = statut;
    }

    @Override
    public void loadItems() {
        loading = true;
        try {
            items = new ArrayList<>();

            // Récupération depuis l'API backend
            List<Map<String, Object>> chantiersData = chantierService.getAllChantiers();

            for (Map<String, Object> data : chantiersData) {
                Chantier c = new Chantier();

                // Mapping des données de l'API vers l'objet Chantier
                // Stocker l'UUID original comme String pour les opérations CRUD
                Object idObj = data.get("id");
                if (idObj != null) {
                    String idString = idObj.toString();
                    // Stocker l'UUID comme String dans un champ caché, et utiliser hashCode pour l'affichage
                    c.setId(Long.valueOf(idString.hashCode())); // Pour compatibilité avec l'interface existante
                    c.setUuidOriginal(idString); // Stocker l'UUID original
                }
                c.setNom((String) data.get("nom"));

                // Le client peut être un objet ou une chaîne
                Object clientObj = data.get("client");
                if (clientObj instanceof Map) {
                    @SuppressWarnings("unchecked")
                    Map<String, Object> clientData = (Map<String, Object>) clientObj;
                    c.setClient((String) clientData.get("raisonSociale"));
                    // Extraire l'ID du client si disponible
                    Object clientIdObj = clientData.get("id");
                    if (clientIdObj != null) {
                        c.setClientId(clientIdObj.toString());
                    }
                } else if (clientObj instanceof String) {
                    c.setClient((String) clientObj);
                } else {
                    c.setClient("N/A");
                }
                
                // Vérifier aussi si clientId est directement dans les données
                Object clientIdDirect = data.get("clientId");
                if (clientIdDirect != null && c.getClientId() == null) {
                    c.setClientId(clientIdDirect.toString());
                }

                c.setAdresse((String) data.get("adresse"));

                // Conversion des dates
                if (data.get("dateDebut") != null) {
                    c.setDateDebut(LocalDate.parse(data.get("dateDebut").toString()));
                }
                if (data.get("dateFinPrevue") != null) {
                    c.setDateFinPrevue(LocalDate.parse(data.get("dateFinPrevue").toString()));
                }

                c.setStatut((String) data.get("statut"));

                // Avancement en pourcentage
                Object avancementObj = data.get("avancement");
                if (avancementObj != null) {
                    c.setAvancement(avancementObj instanceof Integer ?
                        (Integer) avancementObj :
                        Integer.parseInt(avancementObj.toString()));
                } else {
                    c.setAvancement(0);
                }

                // Budget et coût réel
                Object montantObj = data.get("montant");
                if (montantObj != null) {
                    c.setBudget(montantObj instanceof Number ?
                        ((Number) montantObj).doubleValue() :
                        Double.parseDouble(montantObj.toString()));
                } else {
                    c.setBudget(0.0);
                }

                Object coutReelObj = data.get("coutReel");
                if (coutReelObj != null) {
                    c.setCoutReel(coutReelObj instanceof Number ?
                        ((Number) coutReelObj).doubleValue() :
                        Double.parseDouble(coutReelObj.toString()));
                } else {
                    c.setCoutReel(0.0);
                }

                items.add(c);
            }

            LOG.info("Chantiers chargés depuis l'API : {} élément(s)", items.size());
            applyFilters(items, buildFilters());
        } catch (Exception e) {
            LOG.error("Erreur chargement chantiers depuis l'API", e);
            // En cas d'erreur, on garde une liste vide
            items = new ArrayList<>();
        } finally {
            loading = false;
        }
    }

    private List<Predicate<Chantier>> buildFilters() {
        List<Predicate<Chantier>> filters = new ArrayList<>();
        if (filtreNom != null && !filtreNom.trim().isEmpty()) {
            filters.add(c -> c.getNom().toLowerCase().contains(filtreNom.toLowerCase()));
        }
        if (filtreClient != null && !filtreClient.trim().isEmpty()) {
            filters.add(c -> c.getClient().toLowerCase().contains(filtreClient.toLowerCase()));
        }
        if (filtreStatut != null && !filtreStatut.trim().isEmpty() && !"TOUS".equals(filtreStatut)) {
            filters.add(c -> c.getStatut().equals(filtreStatut));
        }
        return filters;
    }

    @Override
    protected void resetFilterFields() {
        filtreNom = null;
        filtreClient = null;
        filtreStatut = "TOUS";
    }

    @Override
    protected String getDetailsPath() {
        return "/chantiers/";
    }

    @Override
    protected String getCreatePath() {
        return "/chantiers/nouveau";
    }

    @Override
    protected void performDelete() {
        if (selectedItem == null || selectedItem.getId() == null) {
            LOG.warn("Aucun chantier sélectionné pour la suppression");
            return;
        }
        
        LOG.info("Suppression chantier : {}", selectedItem.getId());
        
        // Convertir l'ID Long en String UUID (le backend utilise UUID)
        String idString = convertIdToString(selectedItem);
        
        boolean success = chantierService.deleteChantier(idString, false);
        if (success) {
            LOG.info("Chantier supprimé avec succès : {}", idString);
            // Recharger la liste après suppression
            loadItems();
        } else {
            LOG.error("Échec de la suppression du chantier : {}", idString);
        }
    }

    @Override
    protected Chantier createNewEntity() {
        Chantier c = new Chantier();
        c.setStatut("PLANIFIE");
        c.setAvancement(0);
        c.setDateDebut(LocalDate.now());
        c.setDateCreation(LocalDateTime.now());
        return c;
    }

    @Override
    protected void performCreate() {
        if (entity == null) {
            LOG.warn("Aucune entité à créer");
            return;
        }
        
        LOG.info("Création d'un nouveau chantier : {}", entity.getNom());
        
        // Convertir l'entité Chantier en Map pour l'API
        Map<String, Object> chantierData = convertChantierToMap(entity);
        
        Map<String, Object> createdChantier = chantierService.createChantier(chantierData);
        if (createdChantier != null) {
            LOG.info("Chantier créé avec succès : {}", createdChantier.get("id"));
            // Recharger la liste après création
            loadItems();
        } else {
            LOG.error("Échec de la création du chantier : {}", entity.getNom());
        }
    }

    @Override
    protected void performUpdate() {
        if (entity == null || entity.getId() == null) {
            LOG.warn("Aucune entité à mettre à jour ou ID manquant");
            return;
        }
        
        LOG.info("Mise à jour du chantier : {} (ID: {})", entity.getNom(), entity.getId());
        
        // Convertir l'ID Long en String UUID
        String idString = convertIdToString(entity);
        
        // Convertir l'entité Chantier en Map pour l'API
        Map<String, Object> chantierData = convertChantierToMap(entity);
        
        Map<String, Object> updatedChantier = chantierService.updateChantier(idString, chantierData);
        if (updatedChantier != null) {
            LOG.info("Chantier mis à jour avec succès : {}", idString);
            // Recharger la liste après mise à jour
            loadItems();
        } else {
            LOG.error("Échec de la mise à jour du chantier : {}", idString);
        }
    }

    @Override
    protected Long getEntityId(Chantier chantier) {
        return chantier.getId();
    }

    /**
     * Initialise un nouveau chantier pour la création.
     */
    @Override
    public String createNew() {
        prepareNew();
        return getCreatePath() + "?faces-redirect=true";
    }

    /**
     * Sauvegarde un nouveau chantier.
     */
    public String saveNew() {
        if (selectedItem == null) {
            selectedItem = new Chantier();
        }
        selectedItem.setId(System.currentTimeMillis()); // Simulation ID
        selectedItem.setDateCreation(LocalDateTime.now());
        selectedItem.setDateModification(LocalDateTime.now());
        items.add(selectedItem);
        LOG.info("Nouveau chantier créé : {}", selectedItem.getNom());
        return "/chantiers?faces-redirect=true";
    }

    /**
     * Charge un chantier par son ID depuis les paramètres de la requête.
     */
    public void loadChantierById() {
        if (chantierId != null) {
            loadItems(); // S'assurer que les items sont chargés
            selectedItem = items.stream()
                    .filter(c -> c.getId().equals(chantierId))
                    .findFirst()
                    .orElse(null);
            if (selectedItem == null) {
                LOG.warn("Chantier avec ID {} non trouvé", chantierId);
            }
        }
    }

    /**
     * Affiche les détails d'un chantier.
     */
    public String viewDetails(Long id) {
        selectedItem = items.stream()
                .filter(c -> c.getId().equals(id))
                .findFirst()
                .orElse(null);
        if (selectedItem != null) {
            return getDetailsPath() + "details?id=" + id + "&faces-redirect=true";
        }
        return "/chantiers?faces-redirect=true";
    }

    /**
     * Convertit un Chantier en Map pour l'API backend.
     * Le backend attend un ChantierCreateDTO avec des champs spécifiques.
     */
    private Map<String, Object> convertChantierToMap(Chantier chantier) {
        Map<String, Object> map = new HashMap<>();
        
        if (chantier.getNom() != null) {
            map.put("nom", chantier.getNom());
        }
        if (chantier.getAdresse() != null) {
            map.put("adresse", chantier.getAdresse());
        }
        if (chantier.getDateDebut() != null) {
            map.put("dateDebut", chantier.getDateDebut().toString());
        }
        if (chantier.getDateFinPrevue() != null) {
            map.put("dateFinPrevue", chantier.getDateFinPrevue().toString());
        }
        if (chantier.getStatut() != null) {
            map.put("statut", chantier.getStatut());
        }
        if (chantier.getBudget() > 0) {
            map.put("montantPrevu", chantier.getBudget());
        }
        if (chantier.getCoutReel() > 0) {
            map.put("montantReel", chantier.getCoutReel());
        }
        
        // Ajouter le clientId si disponible
        if (chantier.getClientId() != null && !chantier.getClientId().trim().isEmpty()) {
            map.put("clientId", chantier.getClientId());
        }
        
        return map;
    }
    
    /**
     * Convertit un ID Long en String UUID.
     * Utilise l'UUID original stocké dans l'entité si disponible.
     */
    private String convertIdToString(Chantier chantier) {
        if (chantier == null) {
            return null;
        }
        // Utiliser l'UUID original si disponible
        if (chantier.getUuidOriginal() != null) {
            return chantier.getUuidOriginal();
        }
        // Sinon, essayer de convertir depuis l'ID Long
        // Note: Cette conversion n'est pas idéale, mais nécessaire pour compatibilité
        if (chantier.getId() != null) {
            return chantier.getId().toString();
        }
        return null;
    }

    @lombok.Getter
    @lombok.Setter
    public static class Chantier {
        private Long id; // ID pour affichage (hashCode de l'UUID)
        private String uuidOriginal; // UUID original depuis l'API (pour les opérations CRUD)
        private String nom;
        private String client; // Raison sociale du client
        private String clientId; // UUID du client (pour les opérations CRUD)
        private String adresse;
        private LocalDate dateDebut;
        private LocalDate dateFinPrevue;
        private String statut;
        private int avancement;
        private double budget;
        private double coutReel;
        private LocalDateTime dateCreation;
        private LocalDateTime dateModification;
    }
}
