package de.duehl.vocabulary.japanese.logic.symbol.kanji.internal.io;

import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

import de.duehl.basics.datetime.date.ImmutualDate;
import de.duehl.basics.io.FileHelper;
import de.duehl.basics.text.NumberString;
import de.duehl.vocabulary.japanese.common.data.InternalAdditionalKanjiData;
import de.duehl.vocabulary.japanese.common.persistence.SessionManager;
import de.duehl.vocabulary.japanese.data.symbol.Kanji;
import de.duehl.vocabulary.japanese.logic.symbol.kanji.internal.RealInternalKanjiDataRequester;
import de.duehl.vocabulary.japanese.startup.ui.data.MessageAppender;

/**
 * Diese Klasse liest zu allen Kanji die internen, benutzerabhängigen Daten ein.
 *
 * @version 1.01     2025-11-22
 * @author Christian Dühl
 */

public class InternalKanjiDataReader {

    public static final String INTERNAL_KANJI_DATA_DIRECTORY = FileHelper.concatPathes(
            SessionManager.VOCABLE_TRAINER_DIRECTORY, "internal_kanji_data");

    /**
     * Die Extension für den Dateien mit den benutzerabhängigen, internen Daten zu jedem Kanji.
     */
    private static final String INTERNAL_KANJI_DATA_EXTENSION = ".ikd";


    /** Das Objekt zum Anhängen von Nachrichten an den Startup-Log. */
    private final MessageAppender messageAppender;

    /** Das Verzeichnis der internen Daten zu einem Kanji nach dem zugehörigen Kanji. */
    private Map<String, InternalAdditionalKanjiData> key2InternalKanjiDataMap;

    /** Das Objekt das zu einem Kanji die internen, benutzerabhängigen Daten abrufen kann. */
    private RealInternalKanjiDataRequester requester;

    /** Die Zeilen mit Warnungen. */
    private final List<String> warningLines;

    /**
     * Konstruktor.
     *
     * @param messageAppender
     *            Das Objekt zum Anhängen von Nachrichten an den Startup-Log.
     */
    public InternalKanjiDataReader(MessageAppender messageAppender) {
        this.messageAppender = messageAppender;
        warningLines = new ArrayList<>();
    }

    /** Liest die Daten ein. */
    public void read() {
        readInternalKanjiData();
        checkForNewKanji();
        checkForUnusedInternalKanjiData();
        createRequester();
    }

    /** Lädt alle bereits bekannten internen Datensätze zu Kanji. */
    private void readInternalKanjiData() {
        appendMessage("Lese interne benutzerabhängige Daten zu allen bekannten Kanji ...");
        FileHelper.createDirectoryIfNotExists(INTERNAL_KANJI_DATA_DIRECTORY);

        List<String> internalFiles = FileHelper.findFilesInMainDirectoryNio2WithExtensions(
                INTERNAL_KANJI_DATA_DIRECTORY, INTERNAL_KANJI_DATA_EXTENSION);
        appendMessage(NumberString.taupu(internalFiles.size())
                + " interne Kanji-Datensätze gefunden.");
        appendMessage("Lese interne Kanji-Datensätze ein ...");

        List<InternalAdditionalKanjiData> internalAdditionalKanjiDatas = new ArrayList<>();
        for (String filename : internalFiles) {
            InternalAdditionalKanjiData data = InternalAdditionalKanjiData.load(filename);
            internalAdditionalKanjiDatas.add(data);
        }
        appendMessage(NumberString.taupu(internalAdditionalKanjiDatas.size())
                + " interne Kanji-Datensätze eingelesen.");

        key2InternalKanjiDataMap = new HashMap<>();
        for (InternalAdditionalKanjiData data : internalAdditionalKanjiDatas) {
            String key = data.getKey();
            if (key2InternalKanjiDataMap.containsKey(key)) {
                throw new RuntimeException("Der Schlüssel '" + key + "' kommt mehrfach vor!");
            }
            key2InternalKanjiDataMap.put(key, data);
        }
        appendMessage("Verzeichnis mit " + NumberString.taupu(key2InternalKanjiDataMap.size())
                + " Verweisen von Schlüssel auf interne Kanji-Datensätze aufgebaut.");
    }

    private void checkForNewKanji() {
        int newInternalDataCreationCounter = 0;

        for (Kanji kanji : Kanji.getAllKanjiAsList()) {
            String key = kanji.name();

            if (!key2InternalKanjiDataMap.containsKey(key)) {
                InternalAdditionalKanjiData data = new InternalAdditionalKanjiData();
                data.setKey(key);
                String internalDataFilename = FileHelper.concatPathes(INTERNAL_KANJI_DATA_DIRECTORY,
                        key + INTERNAL_KANJI_DATA_EXTENSION);
                data.setFilename(internalDataFilename);
                data.setFirstSeenDate(new ImmutualDate()); // heute

                appendMessage("neu: " + internalDataFilename);
                ++newInternalDataCreationCounter;
                data.save();

                key2InternalKanjiDataMap.put(key, data);
            }
        }
        appendMessage(NumberString.taupu(newInternalDataCreationCounter)
                + " neue interne Kanji-Daten angelegt.");
    }

    private void checkForUnusedInternalKanjiData() {
        List<String> unusedKanjiKeys = new ArrayList<>();
        unusedKanjiKeys.addAll(key2InternalKanjiDataMap.keySet());

        for (Kanji kanji : Kanji.getAllKanjiAsList()) {
            String key = kanji.name();

            if (unusedKanjiKeys.contains(key)) {
                unusedKanjiKeys.remove(key);
            }
        }

        if (unusedKanjiKeys.isEmpty()) {
            appendMessage("Alle interne Daten zu Kanji wurden einem Kanji zugeordnet.");
        }
        else {
            String interneDaten = NumberString.germanPlural(unusedKanjiKeys.size(),
                    "interne Daten", "internes Datum");
            String welche = NumberString.germanPlural(unusedKanjiKeys.size(),
                    "welche", "welches");
            warningLines.add("Es gibt " + NumberString.taupu(unusedKanjiKeys.size())
                    + " " + interneDaten + " zu Kanji, " + welche
                    + " keinem Kanji zugeordnet wurden:");
            for (String unusedKey : unusedKanjiKeys) {
                warningLines.add("    " + unusedKey);
            }

            for (String warningLine : warningLines) {
                appendMessage(warningLine);
            }
        }
    }

    private void createRequester() {
        requester = new RealInternalKanjiDataRequester(key2InternalKanjiDataMap);
    }

    private void appendMessage(String message) {
        messageAppender.appendMessage(message);
    }

    /**
     * Getter für das Objekt das zu einem Kanji die internen, benutzerabhängigen Daten abrufen
     * kann.
     */
    public RealInternalKanjiDataRequester getRequester() {
        return requester;
    }

    /** Die Zeilen mit Warnungen. */
    public List<String> getWarningLines() {
        return warningLines;
    }

}
