package de.duehl.vocabulary.japanese.website.update.ownlists;

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

import javax.swing.SwingUtilities;

import de.duehl.swing.ui.GuiTools;
import de.duehl.vocabulary.japanese.data.FumikoDataStructures;
import de.duehl.vocabulary.japanese.ui.data.FumikoUiObjects;
import de.duehl.vocabulary.japanese.website.download.NewestOwnListVersionListDownloader;
import de.duehl.vocabulary.japanese.website.update.ownlists.data.NewestOwnListVersionListEntry;
import de.duehl.vocabulary.japanese.website.update.ownlists.data.OwnListInterestAndVersionEntry;
import de.duehl.vocabulary.japanese.website.update.ownlists.interest.OwnListVersionAndInterestIo;

import static de.duehl.vocabulary.japanese.common.website.update.UpdateTools.*;

/**
 * Diese Klasse kümmert sich um Updates der bereitgestellten Gruppen von eigenen Listen.
 *
 * Siehe Klasse OwnListGroupsUpdater zum Download von solchen Gruppen unabhängig von Updates.
 *
 * @version 1.01     2025-11-20
 * @author Christian Dühl
 */

public class OwnListGroupsUpdater {

    public static final OwnListInterestAndVersionEntry NOT_FOUND_INTEREST_AND_VERSION =
            new OwnListInterestAndVersionEntry("####NICHT GEFUNDEN###", false, -1);


    /** Die Datenstrukturen des Vokabeltrainers. */
    private final FumikoDataStructures dataStructures;

    /** Die häufig verwendeten Funktionen der grafischen Oberfläche des Vokabeltrainers. */
    private final FumikoUiObjects uiObjects;

    /**
     * Gibt an, dass die Prüfungen auf neue Versionen beim Start des Programms stattfinden.
     * Anderenfalls hat der Benutzer sie über das Update-Menü angestoßen.
     */
    private final boolean checkingNewVersionsOnStartUp;

    /** Die Liste der Einträge in der Datei Neuste_Version_Listen.txt auf dem Server. */
    private List<NewestOwnListVersionListEntry> newestOwnListVersionEntries;

    /**
     * Die Liste der Einträge in der Datei Eigene_Listen_Interessen_und_Versionen.txt auf dem
     * Rechner des Benutzers.
     */
    private List<OwnListInterestAndVersionEntry> interestAndVersionEntries;

    /**
     * Die Liste derjenigen Gruppennamen, die der Benutzer bisher noch nicht oder nur in einer
     * älteren Version kannte.
     */
    private List<String> newerOrUnseenGroupNames;

    /**
     * Konstruktor.
     *
     * @param dataStructures
     *            Die Datenstrukturen des Vokabeltrainers.
     * @param uiObjects
     *            Die häufig verwendeten Funktionen der grafischen Oberfläche des Vokabeltrainers.
     * @param checkingNewVersionsOnStartUp
     *            Gibt an, dass die Prüfungen auf neue Versionen beim Start des Programms
     *            stattfinden. Anderenfalls hat der Benutzer sie über das Update-Menü angestoßen.
     */
    public OwnListGroupsUpdater(FumikoDataStructures dataStructures, FumikoUiObjects uiObjects,
            boolean checkingNewVersionsOnStartUp) {
        this.dataStructures = dataStructures;
        this.uiObjects = uiObjects;
        this.checkingNewVersionsOnStartUp = checkingNewVersionsOnStartUp;
    }

    /** Prüft auf eine aktuellere Version der eigenen Listen. */
    public void update() {
        newestOwnListVersionEntries = NewestOwnListVersionListDownloader.download();

        if (newestOwnListVersionEntries.isEmpty()) {
            if (!checkingNewVersionsOnStartUp) {
                informUserAboutDownloadFailure("der eigenen Listen", uiObjects.getGuiLocation(),
                        uiObjects.getProgramImage());
            }
        }
        else {
            checkOwnListVersionsAgainstWebsiteVersions();
        }
    }

    private void checkOwnListVersionsAgainstWebsiteVersions() {
        interestAndVersionEntries = OwnListVersionAndInterestIo.loadOwnListVersionAndInterests();
        newerOrUnseenGroupNames = determineNewerVersionsOrUnknownGroupsOnTheServer();

        if (newerOrUnseenGroupNames.isEmpty()) {
            if (!checkingNewVersionsOnStartUp) {
                informUserAboutOwnListsAreActual();
            }
        }
        else {
            updateNewerOwnListVersionAndInterestEntries();
            storeInterestAndVersionFile();

            if (isUserInterestedInChanges()) {
                showOwnListInterestSelectionAndDownloadDialog();
            }
            else if (!checkingNewVersionsOnStartUp) {
                informUserAboutOwnListsAreActual();
            }
        }
    }

    /**
     * Ermittelt die Gruppennamen, die der Benutzer bislang nicht zu Gesicht bekommen hat oder bei
     * der die Gruppen auf dem Server eine höhere Version haben.
     */
    private List<String> determineNewerVersionsOrUnknownGroupsOnTheServer() {
        List<String> groupNames = new ArrayList<>();

        for (NewestOwnListVersionListEntry newestEntry : newestOwnListVersionEntries) {
            String groupName = newestEntry.getGroupName();

            OwnListInterestAndVersionEntry interestEntry =
                    findOwnListInterestAndVersionEntryByGroupname(groupName);

            if (NOT_FOUND_INTEREST_AND_VERSION.equals(interestEntry)) {
                groupNames.add(groupName); // neuer Gruppenname, den der Benutzer bisher nicht kennt
            }
            else {
                int versionSeenByUser = interestEntry.getVersion();
                if (versionSeenByUser < newestEntry.getVersion()) {
                    groupNames.add(groupName); // ist auf dem Server neuer
                }
            }
        }

        return groupNames;
    }

    private OwnListInterestAndVersionEntry findOwnListInterestAndVersionEntryByGroupname(
            String groupName) {
        return findOwnListInterestAndVersionEntryByGroupname(groupName, interestAndVersionEntries);
    }

    /**
     * Ermittelt den passenden OwnListInterestAndVersionEntry-Eintrag zum Gruppennamen.
     *
     * Wird kein solcher gefunden, wird NOT_FOUND_INTEREST_AND_VERSION zurückgegeben.
     *
     * @param groupName
     *            Der Gruppenname.
     * @param interestAndVersionEntries
     *            Die Liste der bekannten Interessen des Benutzers.
     */
    public static OwnListInterestAndVersionEntry findOwnListInterestAndVersionEntryByGroupname(
            String groupName, List<OwnListInterestAndVersionEntry> interestAndVersionEntries) {
        for (OwnListInterestAndVersionEntry interestEntry : interestAndVersionEntries) {
            if (interestEntry.getGroupName().equals(groupName)) {
                return interestEntry;
            }
        }

        return NOT_FOUND_INTEREST_AND_VERSION;
    }

    private void informUserAboutOwnListsAreActual() {
        String title = "Es gibt keine Neuerungen bei den Gruppen mit eigenen Listen";
        String message = title;
        GuiTools.informUserInEdt(uiObjects.getWindowAsComponent(), title, message);
    }

    /**
     * Aktualisiert die Einträge in Daten des Benutzers was bislang nicht gesehene Gruppen oder
     * höhere Versionen bereits gesehener Gruppen angeht.
     *
     * Hierbei orientiert man sich an den Einträgen vom Server.
     */
    private void updateNewerOwnListVersionAndInterestEntries() {
        List<OwnListInterestAndVersionEntry> updatedInterestAndVersionEntries = new ArrayList<>();

        for (NewestOwnListVersionListEntry newestEntry : newestOwnListVersionEntries) {
            String groupName = newestEntry.getGroupName();

            OwnListInterestAndVersionEntry interestEntry =
                    findOwnListInterestAndVersionEntryByGroupname(groupName);

            if (NOT_FOUND_INTEREST_AND_VERSION.equals(interestEntry)) {
                interestEntry = new OwnListInterestAndVersionEntry(groupName, true,
                        newestEntry.getVersion());
            }
            else {
                interestEntry = new OwnListInterestAndVersionEntry(groupName,
                        interestEntry.isInterested(), newestEntry.getVersion());
            }
            updatedInterestAndVersionEntries.add(interestEntry);
        }

        interestAndVersionEntries = updatedInterestAndVersionEntries;
    }

    /**
     * Speichert die aktualisierte Datei "Eigene_Listen_Interessen_und_Versionen.txt" auf dem
     * Rechner des Benutzer.
     */
    private void storeInterestAndVersionFile() {
        OwnListVersionAndInterestIo.storeInterestAndVersionFile(interestAndVersionEntries);
    }

    /** Prüft, ob der Benutzer an mindestens einer der Gruppen interessiert ist. */
    private boolean isUserInterestedInChanges() {
        for (String groupName : newerOrUnseenGroupNames) {
            OwnListInterestAndVersionEntry interestEntry =
                    findOwnListInterestAndVersionEntryByGroupname(groupName);

            if (NOT_FOUND_INTEREST_AND_VERSION.equals(interestEntry)) {
                /*
                 * Das kann nicht passieren, da die interestAndVersionEntries mit Hilfe der
                 * newestOwnListVersionEntries aktualisiert wurden (siehe
                 * updateNewerOwnListVersionAndInterestEntries()) und somit zu jedem groupName
                 * ein interestEntry gefunden werden müsste.
                 */
            }
            else {
                if (interestEntry.isInterested()) {
                    return true;
                }
            }
        }

        return false;
    }

    private void showOwnListInterestSelectionAndDownloadDialog() {
        SwingUtilities.invokeLater(() -> showOwnListInterestSelectionAndDownloadDialogInEdt());
    }

    private void showOwnListInterestSelectionAndDownloadDialogInEdt() {
        GroupsDownloader downloader = new GroupsDownloader(dataStructures, uiObjects,
                newestOwnListVersionEntries, interestAndVersionEntries, newerOrUnseenGroupNames);
        downloader.download();
    }

}
