package projetMusee.backend;

import java.io.IOException;
import java.util.ArrayList;
import java.util.List;

/**
 * La classe DataProcessor se charge de transformer le contenu JSON (sous forme
 * de String)
 * en une liste d'objets Museum.
 *
 * @requires Le contenu JSON passé doit être conforme au format attendu.
 * @ensures Retourne une liste de Museum correctement initialisée.
 */
public class DataProcessor {
    // Liste des musées
    private List<Museum> musees;

    /**
     * Constructeur qui initialise DataProcessor en lisant et analysant les données
     * JSON.
     * 
     * @throws Exception
     *
     * @requires Le fichier JSON doit exister et être accessible en lecture.
     * @ensures La liste des musées est remplie avec les données du JSON.
     */
    public DataProcessor() throws Exception {
        try {
            // Création d'un JSONReader pour lire le fichier JSON
            JSONReader jsonReader = new JSONReader();
            String jsonContent = jsonReader.lireJSON();

            assert jsonContent != null && !jsonContent.trim().isEmpty()
                    : "Le contenu JSON ne doit pas être vide ou null";

            // Remplissage de la liste avec les musées extraits du JSON
            this.musees = parseMuseumData(jsonContent);
        } catch (IOException e) {
            e.printStackTrace();
            this.musees = new ArrayList<>(); // Liste vide en cas d'erreur
        }
    }

    /**
     * Retourne la liste des musées.
     *
     * @return La liste d'objets Museum.
     * @ensures Retourne la liste complète des musées stockés.
     */
    public List<Museum> getMusees() {
        return musees;
    }

    /**
     * Met à jour la liste interne des musées avec une nouvelle liste.
     *
     * @param musees La nouvelle liste de musées.
     * @requires {@code musees} ne doit pas être null.
     * @ensures La liste interne est remplacée par {@code musees}.
     */
    public void updateMuseeList(List<Museum> musees) {
        this.musees = musees;
    }

    /**
     * Parse le contenu JSON et crée une liste d'objets Museum.
     *
     * @param jsonContent Le contenu JSON sous forme de chaîne.
     * @return Une liste d'objets Museum.
     * @throws Exception Si le parsing échoue.
     *
     * @requires {@code jsonContent} doit être non null et respecter le format JSON
     *           attendu.
     * @ensures Une liste de Museum est créée à partir du JSON.
     */
    public List<Museum> parseMuseumData(String jsonContent) throws Exception {
        // Vérification du contenu JSON
        if (jsonContent == null || jsonContent.trim().isEmpty()) {
            throw new IllegalArgumentException("Le contenu JSON ne doit pas être vide ou null");
        }
        List<Museum> museums = new ArrayList<>();

        // Suppression des crochets extérieurs [ et ]
        String trimmed = jsonContent.trim();
        if (trimmed.startsWith("[")) {
            trimmed = trimmed.substring(1);
        }
        if (trimmed.endsWith("]")) {
            trimmed = trimmed.substring(0, trimmed.length() - 1);
        }

        // Supposons que chaque objet se termine par "}," ou "}"
        String[] objects = trimmed.split("\\},");
        for (String obj : objects) {
            // Remettre la dernière accolade si elle manque
            if (!obj.trim().endsWith("}")) {
                obj = obj + "}";
            }
            // Extraction des différents champs à partir de l'objet JSON
            String id = parseField(obj, "identifiant");
            String officialName = parseField(obj, "nom_officiel");
            String address = parseField(obj, "adresse");
            String place = parseField(obj, "lieu");
            String postalCode = parseField(obj, "code_postal");
            String city = parseField(obj, "ville");
            String region = parseField(obj, "region");
            String department = parseField(obj, "departement");
            String url = parseField(obj, "url");
            String phone = parseField(obj, "telephone");
            String category = parseField(obj, "categorie");
            String thematicDomainStr = parseField(obj, "domaine_thematique");
            List<String> thematicDomain = parseArray(thematicDomainStr);
            String history = parseField(obj, "histoire");
            String asset = parseField(obj, "atout");
            String themes = parseField(obj, "themes");
            String artist = parseField(obj, "artiste");
            String keyFigure = parseField(obj, "personnage_phare");
            String interest = parseField(obj, "interet");
            String buildingProtection = parseField(obj, "protection_batiment");
            String spaceProtection = parseField(obj, "protection_espace");
            String refmer = parseField(obj, "refmer");
            String creationYearStr = parseField(obj, "annee_creation");
            int creationYear = 0;
            try {
                creationYear = Integer.parseInt(creationYearStr);
            } catch (NumberFormatException e) {
                // Valeur par défaut ou log de l'erreur
            }
            String lastUpdatedDate = parseField(obj, "date_de_mise_a_jour");

            // Traitement des coordonnées
            Coordinates coordinates = extractCoordinates(obj);

            // Création de l'objet Museum avec le constructeur complet
            Museum museum = new Museum(id, officialName, address, place, postalCode, city,
                    region, department, url, phone, category, thematicDomain,
                    history, asset, themes, artist, keyFigure, interest,
                    buildingProtection, spaceProtection, refmer, creationYear,
                    lastUpdatedDate, coordinates);

            museums.add(museum);
        }
        return museums;
    }

    /**
     * Méthode utilitaire pour extraire la valeur d'un champ dans un objet JSON sous
     * forme de chaîne.
     * Ce parsing est basique et suppose que la valeur est soit entre guillemets,
     * soit un nombre.
     *
     * @param json      Le fragment JSON contenant le champ.
     * @param fieldName Le nom du champ à extraire.
     * @return La valeur du champ sous forme de String.
     */
    private String parseField(String json, String fieldName) {
        String pattern = "\"" + fieldName + "\":";
        int index = json.indexOf(pattern);
        if (index == -1) {
            return "";
        }
        int valueStart = index + pattern.length();
        // Ignorer les espaces
        while (valueStart < json.length() && Character.isWhitespace(json.charAt(valueStart))) {
            valueStart++;
        }
        char firstChar = json.charAt(valueStart);
        if (firstChar == '"') {
            // Valeur string entre guillemets
            int endQuote = json.indexOf("\"", valueStart + 1);
            if (endQuote == -1)
                return "";
            return json.substring(valueStart + 1, endQuote);
        } else if (firstChar == '[') {
            // Valeur tableau
            int endBracket = json.indexOf("]", valueStart);
            if (endBracket == -1)
                return "";
            return json.substring(valueStart, endBracket + 1);
        } else if (firstChar == '{') {
            // Objet
            int endBrace = json.indexOf("}", valueStart);
            if (endBrace == -1)
                return "";
            return json.substring(valueStart, endBrace + 1);
        } else {
            // Nombre ou null
            int endIndex = valueStart;
            while (endIndex < json.length() && json.charAt(endIndex) != ',' && json.charAt(endIndex) != '}') {
                endIndex++;
            }
            return json.substring(valueStart, endIndex).trim();
        }
    }

    /**
     * Extrait un tableau de chaînes à partir d'une valeur JSON de type tableau.
     *
     * @param arrayContent La valeur du champ tableau, par exemple: ["val1","val2"].
     * @return Une liste de chaînes contenant les éléments du tableau.
     */
    private List<String> parseArray(String arrayContent) {
        List<String> list = new ArrayList<>();
        String trimmed = arrayContent.trim();
        if (trimmed.startsWith("[") && trimmed.endsWith("]")) {
            trimmed = trimmed.substring(1, trimmed.length() - 1);
            // Supposons que les éléments sont séparés par des virgules
            String[] parts = trimmed.split(",");
            for (String part : parts) {
                part = part.trim();
                if (part.startsWith("\"") && part.endsWith("\"")) {
                    list.add(part.substring(1, part.length() - 1));
                } else {
                    list.add(part);
                }
            }
        }
        return list;
    }

    /**
     * Extrait les coordonnées (longitude et latitude) de l'objet JSON.
     *
     * @param json Le fragment JSON contenant le champ "coordonnees".
     * @return Un objet Coordinates initialisé avec les valeurs extraites, ou des
     *         valeurs par défaut si l'extraction échoue.
     */
    private Coordinates extractCoordinates(String json) {
        Coordinates coordinates = new Coordinates(0, 0);
        int index = json.indexOf("\"coordonnees\"");
        if (index != -1) {
            int start = json.indexOf("{", index);
            int end = json.indexOf("}", start);
            if (start != -1 && end != -1) {
                String coordStr = json.substring(start + 1, end);
                String lonStr = parseField(coordStr, "lon");
                String latStr = parseField(coordStr, "lat");
                double lon = 0;
                double lat = 0;
                try {
                    lon = Double.parseDouble(lonStr);
                    lat = Double.parseDouble(latStr);
                } catch (NumberFormatException e) {
                    // Valeurs par défaut en cas d'erreur
                }
                coordinates = new Coordinates(lon, lat);
            }
        }
        return coordinates;
    }

}
