package de.duehl.vocabulary.japanese.startup.logic.steps;

import java.util.ArrayList;
import java.util.List;

import de.duehl.basics.collections.CollectionsHelper;
import de.duehl.basics.datetime.time.watch.StopWatch;
import de.duehl.basics.text.Text;
import de.duehl.vocabulary.japanese.common.persistence.Options;
import de.duehl.vocabulary.japanese.data.Vocable;
import de.duehl.vocabulary.japanese.data.Vocabulary;
import de.duehl.vocabulary.japanese.data.symbol.Hiragana;
import de.duehl.vocabulary.japanese.startup.ui.data.SplashScreenable;

/**
 * Diese Klasse steht für den Schritt der die Integrität der Vokabeln überprüft.
 *
 * @version 1.01     2025-11-30
 * @author Christian Dühl
 */

public class Step02CheckVocabelIntegrety extends StartupStep {

    /** Die Liste mit den bekannten Vokabularien. */
    private final List<Vocabulary> vocabularies;

    /** Die Liste mit den Meldungen zu doppelten Vokabeln (Kana + Übersetzung). */
    private List<String> doubleVocablesKanaTransltionMessages;

    /** Die Liste mit den Meldungen zu doppelten Vokabeln (Kana + Kanji). */
    private List<String> doubleVocablesKanaKanjiMessages;

    /** Die Liste mit den Meldungen zu Vokabeln mit identischem Kana und Kanji. */
    private List<String> vocablesWithEqualKanaAndKanjiMessages;

    /**
     * Konstruktor.
     *
     * @param step
     *            Der Schritt der durchgeführt wird.
     * @param options
     *            Die Programmoptionen.
     * @param splashScreen
     *            Die grafische Oberfläche beim Start in der die Meldungen angezeigt werden.
     * @param watch
     *            Misst die Laufzeit des gesamten Startups.
     * @param vocabularies
     *            Die Liste mit den bekannten Vokabularien.
     */
    public Step02CheckVocabelIntegrety(String step, Options options,
            SplashScreenable splashScreen, StopWatch watch, List<Vocabulary> vocabularies) {
        super(step, options, splashScreen, watch);
        this.vocabularies = vocabularies;
    }

    /** Führt den eigentlichen Inhalt des Schritts aus. */
    @Override
    protected void runInternalStep() {
        checkForVocablesWithoutKanaOrTranslation();
        checkIfSomeVocablesCombinationsAreUnique();

        checkDoubleTranslationAtSameVocabe();
        checkEOrUInVocableFieldAussprache();

        checkIchidanVerbs();
        checkVerbBaseFormsWithoutGodanIchidanKopulaAndSuruVerb();
        //checkBaseformVerbsEndingWithRuWithoutGodan(); macht inhaltlich keinen Sinn: いる/える-Verben!
    }

    private void checkForVocablesWithoutKanaOrTranslation() {
        appendMessage("Prüfe auf Vokabeln ohne Kanja oder Übersetzung ...");
        List<String> errors = new ArrayList<>();

        for (Vocabulary vocabulary : vocabularies) {
            errors.addAll(checkForVocablesWithoutKanaOrTranslation(vocabulary));
        }

        if (!errors.isEmpty()) {
            errorsInStep(Text.joinWithLineBreak(errors));
        }
    }

    private List<String> checkForVocablesWithoutKanaOrTranslation(Vocabulary vocabulary) {
        List<Vocable> vocables = vocabulary.getVocables();

        List<Vocable> vocablesToRemove = new ArrayList<>();
        for (Vocable vocable : vocables) {
            if (hasNoKanaAndTranslation(vocable)) {
                vocablesToRemove.add(vocable);
            }
        }

        List<String> errors = new ArrayList<>();
        if (!vocablesToRemove.isEmpty()) {
            errors.add(vocabulary.getDescription() + ": Die folgenden Variablen werden "
                    + "entfernt, weil sie nicht vollständig genug sind:");
            for (Vocable vocableToRemove : vocablesToRemove) {
                errors.add(vocableToRemove.toNiceString(4));
                errors.add("");
            }
        }
        return errors;
    }

    private boolean hasNoKanaAndTranslation(Vocable vocable) {
        return vocable.getKana().isBlank()
                || vocable.getTranslations().isEmpty()
                || vocable.getTranslations().get(0).isBlank()
                ;
    }

    /**
     * Prüft, ob der Schlüssel aus Kana + erste Übersetzung eindeutig und beides nicht leer ist und
     * ob der die Kombination von Kanji und Kana eindeutig ist.
     */
    private void checkIfSomeVocablesCombinationsAreUnique() {
        appendMessage("Prüfe die Vokabeln auf gültigen Schlüssel aus Kana + erste Übersetzung ...");
        doubleVocablesKanaTransltionMessages = new ArrayList<>();
        doubleVocablesKanaKanjiMessages = new ArrayList<>();
        vocablesWithEqualKanaAndKanjiMessages = new ArrayList<>();

        List<Vocable> allVocables = createAllVocablesList();

        for (int index1 = 0; index1 < allVocables.size() - 1; ++index1) {
            Vocable vocable1 = allVocables.get(index1);
            for (int index2 = index1 + 1; index2 < allVocables.size(); ++index2) {
                Vocable vocable2 = allVocables.get(index2);
                checkIfVocablesAreUniqueWithKeyFromKanaAndFirstTranslations(vocable1, vocable2);
                checkIfVocablesAreUniqueWithKanjiAndKana(vocable1, vocable2);
            }
        }

        for (Vocable vocable : allVocables) {
            checkIfVocableHasEqualKanaAndKanji(vocable);
        }

        List<String> errors = new ArrayList<>();
        List<String> warnings = new ArrayList<>();
        if (!doubleVocablesKanaTransltionMessages.isEmpty()) {
            errors.add("Es gibt Vokabeln, welche nach Kana und der ersten Übersetzung gleich sind:");
            errors.addAll(doubleVocablesKanaTransltionMessages);
        }
        if (!doubleVocablesKanaKanjiMessages.isEmpty()) {
            errors.add("Es gibt Vokabeln, welche nach Kana und Kanji gleich sind:");
            errors.addAll(doubleVocablesKanaKanjiMessages);
        }
        if (!vocablesWithEqualKanaAndKanjiMessages.isEmpty()) {
            List<String> lines = new ArrayList<>();
            lines.add("Es gibt Vokabeln, welche identische Inhalte von Kana und Kanji haben:");
            lines.addAll(vocablesWithEqualKanaAndKanjiMessages);
            if (options.isInformAboutEqualKanaAndKanjiAtStartup()) {
                errors.addAll(lines);
            }
            else {
                warnings.addAll(lines);
            }
        }

        if (!errors.isEmpty()) {
            errorsInStep(Text.joinWithLineBreak(errors));
        }
        if (!warnings.isEmpty()) {
            String warningsString = Text.joinWithLineBreak(warnings);
            appendMessage(warningsString);
            warningsInStep(warningsString);
        }
    }

    private List<Vocable> createAllVocablesList() {
        List<Vocable> allVocables = new ArrayList<>();

        for (Vocabulary vocabulary : vocabularies) {
            allVocables.addAll(vocabulary.getVocables());
        }

        return allVocables;
    }

    /**
     * Prüft, ob der Schlüssel aus Kana + erste Übersetzung eindeutig und beides nicht leer ist.
     *
     * Wegen removeVocablesWithoutKanaOrTranslation wissen wir, dass beides gefüllt ist und es
     * mindestens eine Übersetzung gibt.
     */
    private void checkIfVocablesAreUniqueWithKeyFromKanaAndFirstTranslations(Vocable vocable1,
            Vocable vocable2) {
        String kana1 = vocable1.getKana();
        String kana2 = vocable2.getKana();

        String translation1 = vocable1.getTranslations().get(0);
        String translation2 = vocable2.getTranslations().get(0);

        if (kana1.equals(kana2) && translation1.equals(translation2)) {
            StringBuilder builder = new StringBuilder();
            builder.append(vocable1.toNiceString(12)).append("\n");
            builder.append("    ").append("und").append("\n");
            builder.append(vocable2.toNiceString(12)).append("\n");
            doubleVocablesKanaTransltionMessages.add(builder.toString());
        }
    }

    /** Prüft, ob der die Kombination von Kanji und Kana eindeutig ist. */
    private void checkIfVocablesAreUniqueWithKanjiAndKana(Vocable vocable1, Vocable vocable2) {
        String kana1 = vocable1.getKana();
        String kana2 = vocable2.getKana();

        String kanji1 = vocable1.getKanji();
        String kanji2 = vocable2.getKanji();

        if (kana1.equals(kana2) && kanji1.equals(kanji2)) {
            StringBuilder builder = new StringBuilder();
            builder.append(vocable1.toNiceString(12)).append("\n");
            builder.append("    ").append("und").append("\n");
            builder.append(vocable2.toNiceString(12)).append("\n");
            doubleVocablesKanaKanjiMessages.add(builder.toString());
        }
    }

    private void checkIfVocableHasEqualKanaAndKanji(Vocable vocable) {
        String kana = vocable.getKana();
        String kanji = vocable.getKanji();

        if (kana.equals(kanji)) {
            StringBuilder builder = new StringBuilder();
            builder.append(vocable.toNiceString(12)).append("\n");
            vocablesWithEqualKanaAndKanjiMessages.add(builder.toString());
        }
    }

    private void checkDoubleTranslationAtSameVocabe() {
        appendMessage("Prüfe die Vokabeln auf doppelte Übersetzungen bei einer Vokabel ...");
        List<String> errors = new ArrayList<>();

        for (Vocabulary vocabulary : vocabularies) {
            for (Vocable vocable : vocabulary.getVocables()) {
                String error = checkDoubleTranslationAtSameVocabel(vocable);
                if (!error.isBlank()) {
                    errors.add(error + " (im Vokabular '" + vocabulary.getDescription() + "').");
                }
            }
        }

        if (!errors.isEmpty()) {
            errorsInStep(Text.joinWithLineBreak(errors));
        }
    }

    /** Überprüft die übergebene Vokabel, ob es bei ihren Übersetzungen doppelte gibt. */
    private String checkDoubleTranslationAtSameVocabel(Vocable vocable) {
        List<String> notDisjunctTexts = CollectionsHelper.getNotDisjunktTexts(
                vocable.getTranslations());
        if (notDisjunctTexts.isEmpty()) {
            return "";
        }
        else {
            return "Zur Vokabel '" + vocable.getKanjiKanaRomajiWithJapaneseBraces()
                    + "' gibt es mehrfach vorkommende Übersetzungen: '"
                    + Text.join("', '", notDisjunctTexts) + "'";
        }
    }

    private void checkEOrUInVocableFieldAussprache() {
        appendMessage("Prüfe die Vokabeln auf 'e' oder 'u' in der Aussprache ...");
        if (options.isInformAboutEOrUInVocableFieldAusspracheAtStartup()) {
            reallyCheckEOrUInVocableFieldAussprache();
        }
    }

    /** Überprüft jede Vokabel, ob es in ihrer Aussprache ein "e" oder ein "u" gibt. */
    private void reallyCheckEOrUInVocableFieldAussprache() {
        List<String> errors = new ArrayList<>();

        for (Vocabulary vocabulary : vocabularies) {
            for (Vocable vocable : vocabulary.getVocables()) {
                String aussprache = vocable.getPronunciation();
                if (aussprache.contains("e") || aussprache.contains("u")) {
                    String error = "Die Vokabel '" + vocable.getKanjiKanaRomajiWithJapaneseBraces()
                        + "' (im Vokabular '" + vocabulary.getDescription() + "' enthält 'e' "
                                + "oder 'u' in ihrer Aussprache: '" + aussprache + "'.";
                    errors.add(error);
                }
            }
        }

        if (!errors.isEmpty()) {
            errorsInStep(Text.joinWithLineBreak(errors));
        }
    }

    private static final String ICHIDAN_VERB = "Ichidan verb";
    private static final String IRU_ERU_VERB = "いる/える-Verb";

    /**
     * Überprüft Ichidan-Verben, die nur "Ichidan verb" oder nur "いる/える-Verb" enthalten, aber nicht
     * beides.
     */
    private void checkIchidanVerbs() {
        appendMessage("Prüfe Ichidan-Verben, die nur \"" + ICHIDAN_VERB + "\" oder nur \""
                + IRU_ERU_VERB + "\" enthalten, aber nicht beides ...");
        List<String> warnings = new ArrayList<>();

        for (Vocabulary vocabulary : vocabularies) {
            for (Vocable vocable : vocabulary.getVocables()) {
                if (vocable.isVerbInWoerterbuchform()) {
                    String comment = vocable.getComment();
                    boolean containesIchidan = comment.contains(ICHIDAN_VERB);
                    boolean containesIruEru = comment.contains(IRU_ERU_VERB);
                    String text = "";
                    if (containesIchidan && !containesIruEru) {
                        text = "enthält '" + ICHIDAN_VERB + "' aber nicht '" + IRU_ERU_VERB + "'";
                    }
                    else if (!containesIchidan && containesIruEru) {
                        text = "enthält '" + IRU_ERU_VERB + "' aber nicht '" + ICHIDAN_VERB + "'";
                    }
                    if (!text.isBlank()) {
                        String warning = "    Die Vokabel "
                                + vocable.getKanjiKanaRomajiWithJapaneseBraces() + " " + text + "."
                                + "\n"
                                + "    Vokabular: " + vocable.getVocabularyDescription();
                        warnings.add(warning);
                    }
                }
            }
        }

        if (!warnings.isEmpty()) {
            warnings.add(0, "Warnungen bei Ichidan-Verben in Wörterbuchform:");
            String warningsString = Text.joinWithLineBreak(warnings);
            appendMessage(warningsString);
            warningsInStep(warningsString);
        }
    }

    public static final String GODAN_VERB = "Godan verb";
    private static final String AUSNAHME_VERB = "Unregelmäßiges Verb"; // "Ausnahme-Verb";
    private static final String KOPULA = "Kopula";
    private static final String COPULA = "Copula";
    private static final String SURU_VERB_COMMENT = "Suru verb";
    private static final String SURU_VERB_SEARCH_WORD = "Suru-Verb";
    public static final String NIDAN_VERB = "Nidan verb";
    public static final String YODAN_VERB = "Yodan verb";

    /**
     * Überprüft Verben in Grundform ohne "Ichidan verb", "Godan verb", "Ausnahme-Verb" und
     * "Suru-Verb".
     */
    private void checkVerbBaseFormsWithoutGodanIchidanKopulaAndSuruVerb() {
        appendMessage("Prüfe Verben in Wörterbuchform ohne \"" + GODAN_VERB + "\", \""
                + ICHIDAN_VERB + "\", \"" + AUSNAHME_VERB + "\" und \"" + KOPULA + "\" und \""
                + SURU_VERB_COMMENT + "\" ...");
        List<String> warnings = new ArrayList<>();

        for (Vocabulary vocabulary : vocabularies) {
            for (Vocable vocable : vocabulary.getVocables()) {
                if (vocable.isVerbInWoerterbuchform()) {
                    String comment = vocable.getComment();
                    boolean containesGodan = comment.contains(GODAN_VERB);
                    boolean containesIchidan = comment.contains(ICHIDAN_VERB);
                    boolean containesAusnahme = comment.contains(AUSNAHME_VERB);
                    boolean containesKopula = comment.contains(KOPULA) || comment.contains(COPULA);
                    boolean containesSuruInComment = comment.contains(SURU_VERB_COMMENT);
                    boolean containesNidan = comment.contains(NIDAN_VERB);
                    boolean containesYodan = comment.contains(YODAN_VERB);

                    List<String> searchWords = vocable.getSearchWords();
                    boolean containesSuruInSearchWord = searchWords.contains(SURU_VERB_SEARCH_WORD);

                    boolean containesSuru = containesSuruInComment || containesSuruInSearchWord;

                    String text = "";
                    if (!containesGodan && !containesIchidan && !containesAusnahme
                            && !containesKopula && !containesSuru && !containesNidan
                            && !containesYodan) {
                        text = "enthält weder '" + GODAN_VERB + "' noch '" + ICHIDAN_VERB + "\" "
                                + "noch eine andere geprüfte Verbform";
                    }
                    if (!text.isBlank()) {
                        String warning = "    Die Vokabel "
                                + vocable.getKanjiKanaRomajiWithJapaneseBraces() + " " + text + "."
                                + "\n"
                                + "    Vokabular: " + vocable.getVocabularyDescription();
                        warnings.add(warning);
                    }
                }
            }
        }

        if (!warnings.isEmpty()) {
            warnings.add(0, "Warnungen bei Verben in Wörterbuchform:");
            String warningsString = Text.joinWithLineBreak(warnings);
            appendMessage(warningsString);
            warningsInStep(warningsString);
        }
    }

    /** Überprüft Verben in Grundform die auf る enden, aber nicht "Godan verb" enthalten. */
    @SuppressWarnings("unused") // macht inhaltlich keinen Sinn: いる/える-Verben!
    private void checkBaseformVerbsEndingWithRuWithoutGodan() {
        appendMessage("Prüfe Verben in Wörterbuchform die auf る enden, aber nicht \"" + GODAN_VERB
                + "\" enthalten...");
        List<String> warnings = new ArrayList<>();

        for (Vocabulary vocabulary : vocabularies) {
            for (Vocable vocable : vocabulary.getVocables()) {
                if (vocable.isVerbInWoerterbuchform()) {
                    String kana = vocable.getKana();
                    boolean endsWithRu = kana.endsWith(Hiragana.RU.getCharacter());

                    String comment = vocable.getComment();
                    boolean containesGodan = comment.contains(GODAN_VERB);

                    String text = "";
                    if (!containesGodan && endsWithRu) {
                        text = "enfdet auf る, enthält aber kein '" + GODAN_VERB + "'";
                    }

                    if (!text.isBlank()) {
                        String warning = "    Die Vokabel "
                                + vocable.getKanjiKanaRomajiWithJapaneseBraces() + " " + text + "."
                                + "\n"
                                + "    Vokabular: " + vocable.getVocabularyDescription();
                        warnings.add(warning);
                    }
                }
            }
        }

        if (!warnings.isEmpty()) {
            warnings.add(0, "Warnungen bei Verben in Wörterbuchform die auf る enden:");
            String warningsString = Text.joinWithLineBreak(warnings);
            appendMessage(warningsString);
            warningsInStep(warningsString);
        }
    }

}
