package dev.lions.unionflow.client.view;

import dev.lions.unionflow.server.api.dto.admin.response.AuditLogResponse;
import dev.lions.unionflow.client.service.AuditService;
import dev.lions.unionflow.client.service.NotificationClientService;
import jakarta.annotation.PostConstruct;
import jakarta.enterprise.context.SessionScoped;
import jakarta.faces.context.ExternalContext;
import jakarta.faces.context.FacesContext;
import jakarta.inject.Inject;
import jakarta.inject.Named;
import org.eclipse.microprofile.rest.client.inject.RestClient;
import org.jboss.logging.Logger;
import dev.lions.unionflow.client.service.ErrorHandlerService;
import dev.lions.unionflow.client.service.RetryService;
import java.io.OutputStream;
import java.io.Serializable;
import java.nio.charset.StandardCharsets;
import java.time.LocalDateTime;
import java.time.ZoneId;
import java.time.format.DateTimeFormatter;
import java.util.*;
import java.util.stream.Collectors;

/**
 * Bean JSF pour la gestion des logs d'audit
 * Refactorisé pour utiliser directement AuditLogResponse et se connecter au backend
 * 
 * @author UnionFlow Team
 * @version 2.0
 */
@Named("auditBean")
@SessionScoped
public class AuditBean implements Serializable {
    
    private static final long serialVersionUID = 1L;
    private static final Logger LOG = Logger.getLogger(AuditBean.class);
    
    @Inject
    @RestClient
    private AuditService auditService;
    
    @Inject
    @RestClient
    private NotificationClientService notificationService;
    
    @Inject
    private UserSession userSession;
    
    @Inject
    ErrorHandlerService errorHandler;
    
    @Inject
    RetryService retryService;
    
    private static final DateTimeFormatter DATE_FORMATTER = DateTimeFormatter.ofPattern("dd/MM/yyyy HH:mm:ss");
    
    // Filtres
    private Date dateDebut;
    private Date dateFin;
    private String typeAction = "";
    private String severite = "";
    private String utilisateur = "";
    private String module = "";
    private String ipAddress = "";
    
    // Données - Utilisation directe de AuditLogResponse
    private List<AuditLogResponse> tousLesLogs;
    private List<AuditLogResponse> logsFiltres;
    private AuditLogResponse logSelectionne;
    
    // Statistiques
    private Map<String, Object> statistiques;
    
    // Export
    private String formatExport = "EXCEL";
    private boolean inclureFiltresExport = true;
    
    @PostConstruct
    public void init() {
        LOG.info("Initialisation de AuditBean");
        // Initialiser les dates à aujourd'hui - 7 jours
        Calendar cal = Calendar.getInstance();
        dateFin = cal.getTime();
        cal.add(Calendar.DAY_OF_MONTH, -7);
        dateDebut = cal.getTime();
        
        chargerLogs();
        chargerStatistiques();
    }
    
    /**
     * Charge les logs depuis le backend
     */
    public void chargerLogs() {
        try {
            LOG.info("Chargement des logs d'audit depuis le backend");
            Map<String, Object> response = retryService.executeWithRetrySupplier(
                () -> auditService.listerTous(0, 1000, "dateHeure", "desc"),
                "chargement des logs d'audit"
            );
            
            tousLesLogs = new ArrayList<>();
            
            if (response.containsKey("data")) {
                @SuppressWarnings("unchecked")
                List<Object> data = (List<Object>) response.get("data");
                
                if (data != null) {
                    for (Object item : data) {
                        if (item instanceof AuditLogResponse) {
                            tousLesLogs.add((AuditLogResponse) item);
                        } else if (item instanceof Map) {
                            @SuppressWarnings("unchecked")
                            AuditLogResponse dto = convertMapToDTO((Map<String, Object>) item);
                            tousLesLogs.add(dto);
                        }
                    }
                }
            }
            
            appliquerFiltres();
            LOG.infof("Logs chargés: %d", tousLesLogs.size());
            
        } catch (Exception e) {
            LOG.errorf(e, "Erreur lors du chargement des logs");
            tousLesLogs = new ArrayList<>();
            errorHandler.handleException(e, "lors du chargement des logs d'audit", null);
        }
    }
    
    /**
     * Charge les statistiques depuis le backend
     */
    public void chargerStatistiques() {
        try {
            LOG.info("Chargement des statistiques d'audit");
            statistiques = retryService.executeWithRetrySupplier(
                () -> auditService.getStatistiques(),
                "chargement des statistiques d'audit"
            );
        } catch (Exception e) {
            LOG.errorf(e, "Erreur lors du chargement des statistiques");
            statistiques = new HashMap<>();
            statistiques.put("total", 0L);
            statistiques.put("success", 0L);
            statistiques.put("errors", 0L);
            statistiques.put("warnings", 0L);
        }
    }
    
    /**
     * Convertit une Map en AuditLogResponse
     */
    private AuditLogResponse convertMapToDTO(Map<String, Object> map) {
        AuditLogResponse dto = new AuditLogResponse();
        
        try {
            if (map.get("id") != null) {
                if (map.get("id") instanceof UUID) {
                    dto.setId((UUID) map.get("id"));
                } else {
                    dto.setId(UUID.fromString(map.get("id").toString()));
                }
            }
            
            if (map.get("typeAction") != null) dto.setTypeAction(map.get("typeAction").toString());
            if (map.get("severite") != null) dto.setSeverite(map.get("severite").toString());
            if (map.get("utilisateur") != null) dto.setUtilisateur(map.get("utilisateur").toString());
            if (map.get("role") != null) dto.setRole(map.get("role").toString());
            if (map.get("module") != null) dto.setModule(map.get("module").toString());
            if (map.get("description") != null) dto.setDescription(map.get("description").toString());
            if (map.get("details") != null) dto.setDetails(map.get("details").toString());
            if (map.get("ipAddress") != null) dto.setIpAddress(map.get("ipAddress").toString());
            if (map.get("userAgent") != null) dto.setUserAgent(map.get("userAgent").toString());
            if (map.get("sessionId") != null) dto.setSessionId(map.get("sessionId").toString());
            if (map.get("donneesAvant") != null) dto.setDonneesAvant(map.get("donneesAvant").toString());
            if (map.get("donneesApres") != null) dto.setDonneesApres(map.get("donneesApres").toString());
            if (map.get("entiteId") != null) dto.setEntiteId(map.get("entiteId").toString());
            if (map.get("entiteType") != null) dto.setEntiteType(map.get("entiteType").toString());
            
            // Conversion des dates
            if (map.get("dateHeure") != null) {
                Object date = map.get("dateHeure");
                if (date instanceof LocalDateTime) {
                    dto.setDateHeure((LocalDateTime) date);
                } else if (date instanceof String) {
                    dto.setDateHeure(LocalDateTime.parse(date.toString()));
                }
            }
            
        } catch (Exception e) {
            LOG.warnf(e, "Erreur lors de la conversion Map vers DTO");
        }
        
        return dto;
    }
    
    /**
     * Applique les filtres sur les logs
     */
    public void appliquerFiltres() {
        if (tousLesLogs == null) {
            logsFiltres = new ArrayList<>();
            return;
        }
        
        logsFiltres = tousLesLogs.stream()
            .filter(this::correspondAuxFiltres)
            .collect(Collectors.toList());
    }
    
    private boolean correspondAuxFiltres(AuditLogResponse log) {
        if (log.getDateHeure() == null) return false;
        
        // Filtre par dates
        LocalDateTime dateDebutLDT = dateDebut != null ? 
            LocalDateTime.ofInstant(dateDebut.toInstant(), ZoneId.systemDefault()) : null;
        LocalDateTime dateFinLDT = dateFin != null ? 
            LocalDateTime.ofInstant(dateFin.toInstant(), ZoneId.systemDefault()).plusDays(1) : null;
        
        if (dateDebutLDT != null && log.getDateHeure().isBefore(dateDebutLDT)) {
            return false;
        }
        if (dateFinLDT != null && log.getDateHeure().isAfter(dateFinLDT)) {
            return false;
        }
        
        // Filtre par type d'action
        if (!typeAction.isEmpty() && !typeAction.equals(log.getTypeAction())) {
            return false;
        }
        
        // Filtre par sévérité
        if (!severite.isEmpty() && !severite.equals(log.getSeverite())) {
            return false;
        }
        
        // Filtre par utilisateur
        if (!utilisateur.isEmpty() && log.getUtilisateur() != null && 
            !log.getUtilisateur().toLowerCase().contains(utilisateur.toLowerCase())) {
            return false;
        }
        
        // Filtre par module
        if (!module.isEmpty() && !module.equals(log.getModule())) {
            return false;
        }
        
        // Filtre par IP
        if (!ipAddress.isEmpty() && log.getIpAddress() != null && 
            !log.getIpAddress().contains(ipAddress)) {
            return false;
        }
        
        return true;
    }
    
    /**
     * Recherche avec filtres via le backend
     */
    public void rechercher() {
        try {
            LOG.info("Recherche de logs avec filtres");
            
            String dateDebutStr = dateDebut != null ? 
                LocalDateTime.ofInstant(dateDebut.toInstant(), ZoneId.systemDefault()).toString() : null;
            String dateFinStr = dateFin != null ? 
                LocalDateTime.ofInstant(dateFin.toInstant(), ZoneId.systemDefault()).toString() : null;
            
            Map<String, Object> response = retryService.executeWithRetrySupplier(
                () -> auditService.rechercher(
                    dateDebutStr, dateFinStr, 
                    typeAction.isEmpty() ? null : typeAction,
                    severite.isEmpty() ? null : severite,
                    utilisateur.isEmpty() ? null : utilisateur,
                    module.isEmpty() ? null : module,
                    ipAddress.isEmpty() ? null : ipAddress,
                    0, 1000),
                "recherche de logs d'audit"
            );
            
            logsFiltres = new ArrayList<>();
            
            if (response.containsKey("data")) {
                @SuppressWarnings("unchecked")
                List<Object> data = (List<Object>) response.get("data");
                
                if (data != null) {
                    for (Object item : data) {
                        if (item instanceof AuditLogResponse) {
                            logsFiltres.add((AuditLogResponse) item);
                        } else if (item instanceof Map) {
                            @SuppressWarnings("unchecked")
                            AuditLogResponse dto = convertMapToDTO((Map<String, Object>) item);
                            logsFiltres.add(dto);
                        }
                    }
                }
            }
            
            errorHandler.showSuccess("Recherche", logsFiltres.size() + " log(s) trouvé(s)");
            
        } catch (Exception e) {
            LOG.errorf(e, "Erreur lors de la recherche");
            errorHandler.handleException(e, "lors de la recherche de logs d'audit", null);
        }
    }
    
    /**
     * Réinitialise les filtres
     */
    public void reinitialiserFiltres() {
        Calendar cal = Calendar.getInstance();
        dateFin = cal.getTime();
        cal.add(Calendar.DAY_OF_MONTH, -7);
        dateDebut = cal.getTime();
        
        typeAction = "";
        severite = "";
        utilisateur = "";
        module = "";
        ipAddress = "";
        
        appliquerFiltres();
    }
    
    /**
     * Actualise les données
     */
    public void actualiser() {
        chargerLogs();
        chargerStatistiques();
    }
    
    /**
     * Sélectionne un log pour voir les détails
     */
    public void selectionnerLog(AuditLogResponse log) {
        this.logSelectionne = log;
    }
    
    /**
     * Méthode pour compatibilité avec l'ancienne page
     */
    public void voirDetails(AuditLogResponse log) {
        selectionnerLog(log);
    }
    
    /**
     * Signale un événement d'audit suspect
     */
    public void signalerEvenement(AuditLogResponse log) {
        if (log == null) {
            errorHandler.showWarning("Attention", "Aucun log sélectionné");
            return;
        }
        
        try {
            LOG.infof("Signalement de l'événement: %s", log.getId());
            
            // Envoyer une notification aux administrateurs
            String message = String.format(
                "Événement signalé - Type: %s, Utilisateur: %s, Date: %s, IP: %s",
                log.getTypeAction(),
                log.getUtilisateur(),
                log.getDateHeure() != null ? log.getDateHeure().format(DATE_FORMATTER) : "N/A",
                log.getIpAddress()
            );
            
            // Récupérer l'ID de l'utilisateur courant pour le signalement
            String signaleurId = userSession.getCurrentUser() != null 
                ? userSession.getCurrentUser().getId().toString() 
                : "anonyme";
            
            retryService.executeWithRetrySupplier(
                () -> {
                    if (!"anonyme".equals(signaleurId)) {
                        notificationService.envoyerNotificationGroupe(
                            java.util.Map.of(
                                "sujet", "Signalement d'un événement d'audit",
                                "corps", message,
                                "membreIds", List.of(java.util.UUID.fromString(signaleurId))
                            ));
                    }
                    return null;
                },
                "envoi d'une notification de signalement"
            );
            
            errorHandler.showSuccess("Signalement", "L'événement a été signalé aux administrateurs");
        } catch (Exception e) {
            LOG.errorf(e, "Erreur lors du signalement");
            errorHandler.handleException(e, "lors du signalement d'un événement d'audit", null);
        }
    }
    
    /**
     * Exporte les logs d'audit en CSV
     */
    public void exporter() {
        try {
            LOG.infof("Export de %d logs d'audit", logsFiltres != null ? logsFiltres.size() : 0);
            
            List<AuditLogResponse> logsAExporter = logsFiltres != null && !logsFiltres.isEmpty() 
                ? logsFiltres 
                : tousLesLogs;
            
            if (logsAExporter == null || logsAExporter.isEmpty()) {
                errorHandler.showWarning("Attention", "Aucun log à exporter");
                return;
            }
            
            // Générer le CSV
            StringBuilder csv = new StringBuilder();
            csv.append("Date/Heure;Type Action;Utilisateur;Module;IP;Sévérité;Détails\n");
            
            for (AuditLogResponse log : logsAExporter) {
                csv.append(String.format("%s;%s;%s;%s;%s;%s;%s\n",
                    log.getDateHeure() != null ? log.getDateHeure().format(DATE_FORMATTER) : "",
                    log.getTypeAction() != null ? log.getTypeAction() : "",
                    log.getUtilisateur() != null ? log.getUtilisateur() : "",
                    log.getModule() != null ? log.getModule() : "",
                    log.getIpAddress() != null ? log.getIpAddress() : "",
                    log.getSeverite() != null ? log.getSeverite() : "",
                    log.getDetails() != null ? log.getDetails().replace(";", ",").replace("\n", " ") : ""
                ));
            }
            
            byte[] csvData = csv.toString().getBytes(StandardCharsets.UTF_8);
            telechargerFichier(csvData, "audit-logs-export.csv", "text/csv");
            
            errorHandler.showSuccess("Export", "Export de " + logsAExporter.size() + " log(s) terminé");
        } catch (Exception e) {
            LOG.errorf(e, "Erreur lors de l'export");
            errorHandler.handleException(e, "lors de l'export des logs d'audit", null);
        }
    }
    
    /**
     * Télécharge un fichier via le navigateur
     */
    private void telechargerFichier(byte[] data, String nomFichier, String contentType) {
        try {
            FacesContext fc = FacesContext.getCurrentInstance();
            ExternalContext ec = fc.getExternalContext();
            
            ec.responseReset();
            ec.setResponseContentType(contentType + "; charset=UTF-8");
            ec.setResponseContentLength(data.length);
            ec.setResponseHeader("Content-Disposition", "attachment; filename=\"" + nomFichier + "\"");
            
            OutputStream output = ec.getResponseOutputStream();
            output.write(data);
            output.flush();
            
            fc.responseComplete();
        } catch (Exception e) {
            LOG.errorf(e, "Erreur téléchargement fichier");
            throw new RuntimeException("Erreur lors du téléchargement", e);
        }
    }
    
    // Getters pour KPIs
    public int getTotalEvenements() {
        if (statistiques != null && statistiques.containsKey("total")) {
            Object total = statistiques.get("total");
            if (total instanceof Number) {
                return ((Number) total).intValue();
            }
        }
        return tousLesLogs != null ? tousLesLogs.size() : 0;
    }
    
    public long getConnexionsReussies() {
        if (tousLesLogs == null) return 0;
        LocalDateTime aujourdhui = LocalDateTime.now().toLocalDate().atStartOfDay();
        return tousLesLogs.stream()
            .filter(log -> "CONNEXION".equals(log.getTypeAction()) && 
                          "SUCCESS".equals(log.getSeverite()) &&
                          log.getDateHeure() != null &&
                          log.getDateHeure().isAfter(aujourdhui))
            .count();
    }
    
    public long getTentativesEchouees() {
        if (tousLesLogs == null) return 0;
        LocalDateTime semainePassee = LocalDateTime.now().minusWeeks(1);
        return tousLesLogs.stream()
            .filter(log -> "CONNEXION".equals(log.getTypeAction()) && 
                          !"SUCCESS".equals(log.getSeverite()) &&
                          log.getDateHeure() != null &&
                          log.getDateHeure().isAfter(semainePassee))
            .count();
    }
    
    public long getAlertesSecurite() {
        if (tousLesLogs == null) return 0;
        return tousLesLogs.stream()
            .filter(log -> "CRITICAL".equals(log.getSeverite()) || 
                          "ERROR".equals(log.getSeverite()))
            .count();
    }
    
    // Getters et Setters
    public Date getDateDebut() { return dateDebut; }
    public void setDateDebut(Date dateDebut) { 
        this.dateDebut = dateDebut;
        appliquerFiltres();
    }
    
    public Date getDateFin() { return dateFin; }
    public void setDateFin(Date dateFin) { 
        this.dateFin = dateFin;
        appliquerFiltres();
    }
    
    public String getTypeAction() { return typeAction; }
    public void setTypeAction(String typeAction) { 
        this.typeAction = typeAction;
        appliquerFiltres();
    }
    
    public String getSeverite() { return severite; }
    public void setSeverite(String severite) { 
        this.severite = severite;
        appliquerFiltres();
    }
    
    public String getUtilisateur() { return utilisateur; }
    public void setUtilisateur(String utilisateur) { 
        this.utilisateur = utilisateur;
        appliquerFiltres();
    }
    
    public String getModule() { return module; }
    public void setModule(String module) { 
        this.module = module;
        appliquerFiltres();
    }
    
    public String getIpAddress() { return ipAddress; }
    public void setIpAddress(String ipAddress) { 
        this.ipAddress = ipAddress;
        appliquerFiltres();
    }
    
    public List<AuditLogResponse> getEvenementsFiltres() {
        return logsFiltres != null ? logsFiltres : new ArrayList<>();
    }
    
    public AuditLogResponse getEvenementSelectionne() { return logSelectionne; }
    public void setEvenementSelectionne(AuditLogResponse log) { this.logSelectionne = log; }
    
    public String getFormatExport() { return formatExport; }
    public void setFormatExport(String formatExport) { this.formatExport = formatExport; }
    
    public boolean isInclureFiltresExport() { return inclureFiltresExport; }
    public void setInclureFiltresExport(boolean inclureFiltresExport) { 
        this.inclureFiltresExport = inclureFiltresExport; 
    }
}
