package de.duehl.vocabulary.japanese.common.website.update.vocables;

import java.util.List;

import de.duehl.basics.datetime.date.ImmutualDate;
import de.duehl.basics.io.FileHelper;
import de.duehl.basics.text.Text;
import de.duehl.vocabulary.japanese.common.website.update.vocables.parser.VocableVersionParser;

import static de.duehl.vocabulary.japanese.common.website.VocaluaryTrainerWebsiteConstants.NEWEST_VOCABLES_VERSION_ON_SERVER_URL;
import static de.duehl.vocabulary.japanese.common.website.VocaluaryTrainerWebsiteConstants.VOCABLES_VERSION_BARE_FILENAME_IN_LOCAL_VOCABLES_DIR;
import static de.duehl.vocabulary.japanese.common.website.download.WebsiteFileContentDownloader.NO_DOWNLOAD_SUCCESS;
import static de.duehl.vocabulary.japanese.common.website.download.WebsiteFileContentDownloader.downloadFileViaUrlFromServerAndGetContentOfFirstLine;

/**
 * Diese Klasse prüft, ob die verwendeten Vokabeln aktuell sind.
 *
 * @version 1.01     2024-12-11
 * @author Christian Dühl
 */

public class VocablesActualityChecker {

    /** Der Pfad zu den Vokabularien und den Sound-Files. */
    private final String vocabulariesPath;

    /**
     * Gibt an, ob die Version auf dem Webserver in Erfahrung gebracht und geparsed werden konnte.
     */
    private boolean versionOnWebserverDetermined;

    /** Enthält die Version auf der Webseite, wenn diese ermittelt werden konnte. */
    private String websiteVocableVersion;

    /**
     * Gibt an, ob die Version der lokalen Vokabeln in Erfahrung gebracht und geparsed werden
     * konnte.
     */
    private boolean localVersionDetermined;

    /** Enthält die Version der lokalen Vokabeln, wenn diese ermittelt werden konnte. */
    private String localVocableVersion;

    /** Der Datumteil der Version als Datum von der Webseite. */
    private ImmutualDate websiteDate;

    /** Die Nummer aus der Version von der Webseite. */
    private int websiteNumber;

    /** Der Datumteil der Version als Datum der lokalen Vokabeln. */
    private ImmutualDate localDate;

    /** Die Nummer aus der Version der lokalen Vokabeln. */
    private int localNumber;

    /** Gibt an, ob die verwendeten Vokabeln aktuell sind. */
    private boolean actual;

    /**
     * Konstruktor.
     *
     * @param vocabulariesPath
     *            Der Pfad zu den Vokabularien und den Sound-Files.
     */
    public VocablesActualityChecker(String vocabulariesPath) {
        this.vocabulariesPath = vocabulariesPath;
    }

    /** Führt die Prüfung durch. */
    public void check() {
        init();

        downloadVersionFromWebsite();
        if (versionOnWebserverDetermined) {
            parseVocableVersionFromWebsite();
            if (versionOnWebserverDetermined) {
                determineLocalVocablesVersion();
                if (localVersionDetermined) {
                    parseLocalVocableVersion();
                    if (localVersionDetermined) {
                        compareWebsiteAndLocalVocableVersion();
                    }
                }
            }
        }
    }

    private void init() {
        versionOnWebserverDetermined = false;
        websiteVocableVersion = "";
        localVersionDetermined = false;
        localVocableVersion = "";
        actual = false;
    }

    /** Lädt die Version auf der Webseite als String herunter. */
    private void downloadVersionFromWebsite() {
        websiteVocableVersion = downloadFileViaUrlFromServerAndGetContentOfFirstLine(
                NEWEST_VOCABLES_VERSION_ON_SERVER_URL);

        if (websiteVocableVersion.equals(NO_DOWNLOAD_SUCCESS)) {
            versionOnWebserverDetermined = false;
            websiteVocableVersion = "";
        }
        else {
            versionOnWebserverDetermined = true;
        }
    }

    private void parseVocableVersionFromWebsite() {
        VocableVersionParser websiteVersionParser =
                new VocableVersionParser(websiteVocableVersion);
        websiteVersionParser.parse();
        if (websiteVersionParser.isSuccess()) {
            websiteDate = websiteVersionParser.getDate();
            websiteNumber = websiteVersionParser.getNumber();
        }
        else {
            versionOnWebserverDetermined = false;
        }
    }

    private void determineLocalVocablesVersion() {
        localVocableVersion = determineLocalVocableVersion(vocabulariesPath);

        if (localVocableVersion.isEmpty()) {
            localVersionDetermined = false;
        }
        else {
            localVersionDetermined = true;
        }
    }

    /**
     * Bestimmt die Version der lokalen Vokabeln.
     *
     * Normalerweise liegt diese direkt in der Datei
     * VOCABLES_VERSION_BARE_FILENAME_IN_LOCAL_VOCABLES_DIR im Vokabelverzeichnis, aber je nachdem,
     * wie andere Benutzer die Vokabeln entpacken und das Vokabelverzeichnis wählen, kann sie auch
     * in einem Unterverzeichnis dort liegen.
     *
     * Daher durchsuche ich die Unterverzeichnisse, wenn ich sie nicht direkt finde.
     *
     * Gibt den leeren String zurück, wenn das Verzeichnis nicht gefunden wird oder es Probleme mit
     * der Datei mit der Version gibt.
     *
     * @param localVocableDirectory
     *            Der Pfad zu den Vokabularien und den Sound-Files.
     */
    static String determineLocalVocableVersion(String localVocableDirectory) {
        String versionFilename = FileHelper.concatPathes(localVocableDirectory,
                VOCABLES_VERSION_BARE_FILENAME_IN_LOCAL_VOCABLES_DIR);

        if (!FileHelper.isFile(versionFilename)) {
            List<String> subDirectories = FileHelper.findAllDirectoriesNio2(localVocableDirectory);
            boolean found = false;
            for (String subDirectory : subDirectories) {
                versionFilename = FileHelper.concatPathes(subDirectory,
                        VOCABLES_VERSION_BARE_FILENAME_IN_LOCAL_VOCABLES_DIR);
                if (FileHelper.isFile(versionFilename)) {
                    found = true;
                    break;
                }
            }
            if (!found) {
                Text.say("Keine Version der lokalen Vokabeln vorhanden, denn die lokale "
                        + "Datei '" + versionFilename + "' existiert nicht.");
                return "";
            }
        }

        List<String> lines = FileHelper.readFileToList(versionFilename);
        if (lines.isEmpty()) {
            Text.say("Keine Version der lokalen Vokabeln vorhanden, denn die lokale "
                    + "Datei '" + versionFilename + "' ist leer.");
            return "";
        }
        else {
            return lines.get(0).trim();
        }
    }

    private void parseLocalVocableVersion() {
        VocableVersionParser localVersionParser = new VocableVersionParser(localVocableVersion);
        localVersionParser.parse();
        if (localVersionParser.isSuccess()) {
            localDate = localVersionParser.getDate();
            localNumber = localVersionParser.getNumber();
        }
        else {
            localVersionDetermined = false;
        }
    }

    private void compareWebsiteAndLocalVocableVersion() {
        if (websiteDate.before(localDate)) {
            actual = true;
        }
        else if (websiteDate.after(localDate)) {
            actual = false;
        }
        else {
            actual = localNumber >= websiteNumber;
        }
    }

    /**
     * Gibt an, ob die Version auf dem Webserver in Erfahrung gebracht und geparsed werden konnte.
     */
    public boolean isVersionOnWebserverDetermined() {
        return versionOnWebserverDetermined;
    }

    /** Getter für die Version auf der Webseite, wenn diese ermittelt werden konnte. */
    public String getWebsiteVocableVersion() {
        return websiteVocableVersion;
    }

    /**
     * Gibt an, ob die Version der lokalen Vokabeln in Erfahrung gebracht und geparsed werden
     * konnte.
     */
    public boolean isLocalVersionDetermined() {
        return localVersionDetermined;
    }

    /** Getter für die Version der lokalen Vokabeln, wenn diese ermittelt werden konnte. */
    public String getLocalVocableVersion() {
        return localVocableVersion;
    }

    /** Gibt an, ob die verwendeten Vokabeln aktuell sind. */
    public boolean isActual() {
        return actual;
    }

}
