package at.vvo.omds.helpers;

import at.vvo.omds.types.omds3.r2025_05.common.APrioriProduktbausteinType;
import at.vvo.omds.types.omds3.r2025_05.common.ProduktbausteinType;
import javafx.collections.FXCollections;
import javafx.scene.control.TreeItem;

import java.util.Comparator;
import java.util.HashMap;
import java.util.Map;

public class TreeHelper {
    Map<String, Integer> timesItemisIncludedById = new HashMap<>();

    public Map<String, Integer> getTimesItemisIncludedById() {
        return timesItemisIncludedById;
    }

    public void setTimesItemisIncludedById(Map<String, Integer> timesItemisIncludedById) {
        this.timesItemisIncludedById = timesItemisIncludedById;
    }

    public TreeItem<GuiProdukt> cleanTree(TreeItem<GuiProdukt> original) {
        if (original.getValue().isNotIncluded()) {
            return null;
        }

        TreeItem<GuiProdukt> newItem = new TreeItem<>(original.getValue());
        for (TreeItem<GuiProdukt> child : original.getChildren()) {
            TreeItem<GuiProdukt> cleanedChild = cleanTree(child);
            if (cleanedChild != null) {
                newItem.getChildren().add(cleanedChild);
            }
        }
        return newItem;
    }

    public TreeItem<GuiProdukt> cloneTreeItem(TreeItem<GuiProdukt> original) {
        ProduktbausteinType cloneOriginalValue;

        cloneOriginalValue = new ObjectFactoryFactory().create(((ProduktbausteinType) original.getValue().getProdukt()).getTyp());

        cloneOriginalValue.setId(
                ((ProduktbausteinType) original.getValue().getProdukt()).getId());
        cloneOriginalValue.setBezeichnung(
                ((ProduktbausteinType) original.getValue().getProdukt()).getBezeichnung());
        cloneOriginalValue.setVerkaufsoffenVon(
                ((ProduktbausteinType) original.getValue().getProdukt()).getVerkaufsoffenVon());
        cloneOriginalValue.setVerkaufsoffenBis(
                ((ProduktbausteinType) original.getValue().getProdukt()).getVerkaufsoffenBis());
        cloneOriginalValue.getAttribute().addAll(
                ((ProduktbausteinType) original.getValue().getProdukt()).getAttribute());
        cloneOriginalValue.getMeldungen().addAll(
                ((ProduktbausteinType) original.getValue().getProdukt()).getMeldungen());
        cloneOriginalValue.setMinVorkommen(
                ((ProduktbausteinType) original.getValue().getProdukt()).getMinVorkommen());
        cloneOriginalValue.setMaxVorkommen(
                ((ProduktbausteinType) original.getValue().getProdukt()).getMaxVorkommen());
        cloneOriginalValue.setTyp(
                ((ProduktbausteinType) original.getValue().getProdukt()).getTyp());
        cloneOriginalValue.setRisikoobjektErforderlich(
                ((ProduktbausteinType) original.getValue().getProdukt()).isRisikoobjektErforderlich());
        cloneOriginalValue.getVersicherteObjekte().addAll(
                ((ProduktbausteinType) original.getValue().getProdukt()).getVersicherteObjekte());
        cloneOriginalValue.setPraemienfaktor(
                ((ProduktbausteinType) original.getValue().getProdukt()).getPraemienfaktor());

        TreeItem<GuiProdukt> clonedItem = new TreeItem<>(new GuiProdukt(cloneOriginalValue));

        for (TreeItem<GuiProdukt> child : original.getChildren()) {
            clonedItem.getChildren().add(cloneTreeItem(child));
        }
        clonedItem.setExpanded(true);

        clonedItem.getValue().setNotIncluded(original.getValue().isNotIncluded());

        return clonedItem;
    }

    public TreeItem<GuiProdukt> findTreeItem(ProduktbausteinType item, TreeItem<GuiProdukt> root) {
        if (((ProduktbausteinType) root.getValue().getProdukt()).getId().equals(item.getId())) {
            return root;
        }
        for (TreeItem<GuiProdukt> child : root.getChildren()) {
            TreeItem<GuiProdukt> found = findTreeItem(item, child);
            if (found != null) return found;
        }
        return null;
    }

    public void aprioriProduktToTree(APrioriProduktbausteinType produkt, TreeItem<GuiProdukt> treeItem) throws Exception {
        if (!produkt.getBausteine().isEmpty()) {
            for (int i = 0; i < produkt.getBausteine().size(); i++) {
                TreeItem<GuiProdukt> up = aprioriItemToCalcItem(new TreeItem<>(new GuiProdukt(produkt.getBausteine().get(i))));
                up.getValue().setNotIncluded(true);
                up.setExpanded(true);
                if (!produkt.getBausteine().get(i).getBausteine().isEmpty()) {
                    aprioriProduktToTree(produkt.getBausteine().get(i), up);
                }
                treeItem.getChildren().add(up);
            }
        }
    }

    public void produktToTree(ProduktbausteinType produkt, TreeItem<GuiProdukt> treeItem) throws Exception {
        if (produkt != null && !produkt.getBausteine().isEmpty()) {
            for (int i = 0; i < produkt.getBausteine().size(); i++) {
                TreeItem<GuiProdukt> up = new TreeItem<>(new GuiProdukt(produkt.getBausteine().get(i)));
                up.setExpanded(true);

                if (!produkt.getBausteine().get(i).getBausteine().isEmpty()) {
                    produktToTree(produkt.getBausteine().get(i), up);
                }
                treeItem.getChildren().add(up);
                if (!timesItemisIncludedById.containsKey(((ProduktbausteinType) up.getValue().getProdukt()).getId())) {
                    timesItemisIncludedById.put(((ProduktbausteinType) up.getValue().getProdukt()).getId(), 1);
                }
            }
        }
    }

    public TreeItem<GuiProdukt> aprioriItemToCalcItem(TreeItem<GuiProdukt> produkt) throws Exception {

        TreeItem<GuiProdukt> ergItem = new TreeItem<>();
        ProduktbausteinType erg = new ObjectFactoryFactory().create(
                ((APrioriProduktbausteinType) produkt.getValue().getProdukt()).getType());

        if (erg != null) {
            if (ProduktbausteinType.class.isAssignableFrom(erg.getClass())) {
                erg.setId(
                        ((APrioriProduktbausteinType) produkt.getValue().getProdukt()).getId());
                erg.setBezeichnung(
                        ((APrioriProduktbausteinType) produkt.getValue().getProdukt()).getName());
                erg.setVerkaufsoffenVon(
                        ((APrioriProduktbausteinType) produkt.getValue().getProdukt()).getFrom());
                erg.setVerkaufsoffenBis(
                        ((APrioriProduktbausteinType) produkt.getValue().getProdukt()).getTo());
                erg.getMeldungen().addAll(
                        ((APrioriProduktbausteinType) produkt.getValue().getProdukt()).getMeldungen());
                erg.getAttribute().addAll(
                        ((APrioriProduktbausteinType) produkt.getValue().getProdukt()).getAttribute());
                erg.setMinVorkommen(
                        ((APrioriProduktbausteinType) produkt.getValue().getProdukt()).getMinOccurrences());
                erg.setMaxVorkommen(
                        ((APrioriProduktbausteinType) produkt.getValue().getProdukt()).getMaxOccurrences());
                erg.setTyp(
                        ((APrioriProduktbausteinType) produkt.getValue().getProdukt()).getType());
                erg.setRisikoobjektErforderlich(
                        ((APrioriProduktbausteinType) produkt.getValue().getProdukt()).getRisikoobjektType() != null);
                erg.setPraemienfaktor(
                        ((APrioriProduktbausteinType) produkt.getValue().getProdukt()).getPraemienfaktor());

                for (int i = 0; i < ((APrioriProduktbausteinType) produkt.getValue().getProdukt()).getBausteine().size(); i++) {
                    erg.getBausteine().add((ProduktbausteinType) aprioriItemToCalcItem(
                            new TreeItem<>(new GuiProdukt(((APrioriProduktbausteinType) produkt.getValue().getProdukt())
                                    .getBausteine().get(i)))).getValue().getProdukt());
                }

                for (int i = 0; i < produkt.getChildren().size(); i++) {
                    ergItem.getChildren().add(aprioriItemToCalcItem(produkt.getChildren().get(i)));
                }
            }
        } else {
            throw new Exception("Objekt kann nicht erzeugt werden. Unbekannte Klasse "
                    + ((APrioriProduktbausteinType) produkt.getValue().getProdukt()).getType());
        }
        ergItem.setValue(new GuiProdukt(erg));
        return ergItem;
    }

    public TreeItem<GuiProdukt> addAprioriToCalc(TreeItem<GuiProdukt> calcItem, TreeItem<GuiProdukt> aprioriItem) {
        for (TreeItem<GuiProdukt> aprioriChild : aprioriItem.getChildren()) {
            boolean isInCalc = false;
            int i = 0;
            for (TreeItem<GuiProdukt> calcChild : calcItem.getChildren()) {
                if (((ProduktbausteinType) calcChild.getValue().getProdukt()).getId().equals(
                        ((ProduktbausteinType) aprioriChild.getValue().getProdukt()).getId())
                        && !calcChild.getValue().isNotIncluded()) {
                    isInCalc = true;
                    if (!aprioriChild.getChildren().isEmpty()) {
                        addAprioriToCalc(calcChild, aprioriChild);
                    }
                }
                i++;
            }
            if (!isInCalc) {
                TreeItem<GuiProdukt> missingCalcItem = cloneTreeItem(aprioriChild);
                calcItem.getChildren().add(missingCalcItem);
            }
        }
        return calcItem;
    }

    public TreeItem<GuiProdukt> sortTree(TreeItem<GuiProdukt> parent) {
        FXCollections.sort(parent.getChildren(), Comparator.comparing(
                item -> ((ProduktbausteinType)item.getValue().getProdukt()).getBezeichnung()
        ));

        for (TreeItem<GuiProdukt> child : parent.getChildren()) {
            sortTree(child);
        }
        return parent;
    }
}
