package projetMusee.facade;

import projetMusee.backend.enums.Granularite;
import projetMusee.backend.Museum;
import projetMusee.backend.MuseumFilter;
import projetMusee.backend.enums.CritereFiltrage;
import projetMusee.backend.enums.TypeDiagramme;
import projetMusee.backend.enums.TypeStatistique;
import projetMusee.backend.reports.ReportConfiguration;
import projetMusee.ui.HTMLReportGenerator;
import projetMusee.visustats.VisustatEngine;

import java.io.IOException;
import java.text.Normalizer;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.stream.Collectors;

/**
 * Classe façade permettant de gérer le filtrage des musées et la génération des
 * diagrammes.
 */
public class ApplicationFacade {

    private VisustatEngine visustatEngine;

    public ApplicationFacade() {
        this.visustatEngine = new VisustatEngine();
    }

    public List<Museum> getFilteredMuseumsByGranularity(Map<Granularite, List<String>> granulariteValeurs,
            List<Museum> musees) {
        List<Museum> filteredMuseums = musees;

        for (Map.Entry<Granularite, List<String>> entry : granulariteValeurs.entrySet()) {
            Granularite granularite = entry.getKey();
            List<String> valeurs = entry.getValue().stream()
                    .map(ApplicationFacade::encode)
                    .toList();

            switch (granularite) {
                case REGION:
                    filteredMuseums = MuseumFilter.filterByRegions(filteredMuseums, valeurs.toArray(new String[0]));
                    break;
                case DEPARTEMENT:
                    filteredMuseums = MuseumFilter.filterByDepartement(filteredMuseums, valeurs.toArray(new String[0]));
                    break;
            }
        }
        return filteredMuseums;
    }

    public List<Museum> getFilteredMuseums(Map<CritereFiltrage, List<String>> critereValeurs, List<Museum> musees) {
        List<Museum> filteredMuseums = musees;

        for (Map.Entry<CritereFiltrage, List<String>> entry : critereValeurs.entrySet()) {
            CritereFiltrage critere = entry.getKey();
            List<String> valeurs = entry.getValue().stream()
                    .map(ApplicationFacade::encode)
                    .toList();

            switch (critere) {
                case REGION:
                    filteredMuseums = MuseumFilter.filterByRegions(filteredMuseums, valeurs.toArray(new String[0]));
                    break;
                case DEPARTEMENT:
                    filteredMuseums = MuseumFilter.filterByDepartement(filteredMuseums, valeurs.toArray(new String[0]));
                    break;
                case VILLE:
                    filteredMuseums = MuseumFilter.filterByVille(filteredMuseums, valeurs.toArray(new String[0]));
                    break;
                case THEME:
                    filteredMuseums = MuseumFilter.filterByTheme(filteredMuseums, valeurs.toArray(new String[0]));
                    break;
                default:
                    break;
            }
        }
        return filteredMuseums;
    }

    public void generateDiagramme(TypeDiagramme diagrType, List<Museum> filteredMuseums, TypeStatistique typestats,
            String[] colors)
            throws IOException {
        String csvFile = visustatEngine.generateCSV(filteredMuseums, typestats);
        switch (diagrType) {
            case CAMEMBERT -> visustatEngine.genereDiagramme(csvFile, "DiagCamembert", colors);
            case DIAGRAMME_EN_BARRES -> visustatEngine.genereDiagramme(csvFile, "DiagBarres", colors);
            default -> visustatEngine.genereDiagramme(csvFile, "DiagColonnes", colors);
        }
    }

    public Set<String> getDepartments(List<Museum> musees) {
        return musees.stream()
                .map(Museum::getDepartment)
                .map(ApplicationFacade::unescapeUnicode)
                .collect(Collectors.toSet());
    }

    public Set<String> getCities(List<Museum> musees) {
        return musees.stream()
                .map(Museum::getCity)
                .map(ApplicationFacade::unescapeUnicode)
                .collect(Collectors.toSet());
    }

    public Set<String> getThemes(List<Museum> musees) {
        return musees.stream()
                .flatMap(m -> m.getThematicDomain().stream()) // → 1 élément par thème
                .map(ApplicationFacade::unescapeUnicode) // Décodage des accents
                .collect(Collectors.toSet());
    }

    public Set<String> getRegions(List<Museum> musees) {
        return musees.stream()
                .map(Museum::getRegion)
                .map(ApplicationFacade::unescapeUnicode)
                .collect(Collectors.toSet());
    }

    /**
     * Supprime les accents et standardise une chaîne (pour le filtrage).
     */
    public static String encode(String input) {
        if (input == null)
            return "";
        return Normalizer.normalize(input, Normalizer.Form.NFD)
                .replaceAll("\\p{M}", "") // Supprime les accents
                .toLowerCase()
                .trim();
    }

    /**
     * Décode une chaîne contenant des séquences Unicode (ex: \u00e9 -> é).
     */
    public static String unescapeUnicode(String input) {
        if (input == null)
            return "";
        StringBuilder result = new StringBuilder();
        int i = 0;
        while (i < input.length()) {
            char c = input.charAt(i++);
            if (c == '\\' && i < input.length() && input.charAt(i) == 'u') {
                i++;
                try {
                    int code = Integer.parseInt(input.substring(i, i + 4), 16);
                    result.append((char) code);
                    i += 4;
                } catch (Exception e) {
                    result.append("\\u");
                }
            } else {
                result.append(c);
            }
        }
        return result.toString();
    }

    public String generateReportHTML(List<Museum> filteredMuseums, List<String> selectedFields) {
        // Configurer les champs sélectionnés
        ReportConfiguration config = new ReportConfiguration(selectedFields);

        // Créer le générateur HTML
        HTMLReportGenerator generator = new HTMLReportGenerator();

        // Générer le rapport HTML à partir des musées filtrés et des champs
        return generator.generateHTMLReport(filteredMuseums, config.getReportFields());
    }
}