package com.eactive.testmaster.client.dto;

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

public class ApiLayoutItemTreeBuilder {

    public static ApiLayoutItemDTO createTree(List<ApiLayoutItemDTO> layoutItems) {
        layoutItems.sort(Comparator.comparing(ApiLayoutItemDTO::getLoutItemSerno));

        Long rootId = layoutItems.get(0).getParent();
        ApiLayoutItemDTO root = new ApiLayoutItemDTO();
        root.setLevel(0);
        root.setId(rootId);

        Map<Long, ApiLayoutItemDTO> nodeMap = new HashMap<>();
        nodeMap.put(rootId, root);

        for (ApiLayoutItemDTO item : layoutItems) {
            if (item.getLoutItemType().equals("Grid") || item.getLoutItemType().equals("Group")) {
                if (item.getLoutItemOccRef() != null && item.getLoutItemOccCnt().equals("*")) {
                    for (ApiLayoutItemDTO parent : layoutItems) {
                        if (parent.getLoutItemName().equals(item.getLoutItemOccRef())) {
                            item.setChildOccCnt(Integer.parseInt(parent.getValue()));
                            break;
                        }
                    }
                } else if (item.getLoutItemOccCnt() != null) {
                    item.setChildOccCnt(Integer.parseInt(item.getLoutItemOccCnt()));
                }
            }

            if (nodeMap.containsKey(item.getParent())) {
                nodeMap.get(item.getParent()).getChildren().add(item);
            }
            nodeMap.put(item.getId(), item);
        }

        return root;
    }

    public static void moveNode(ApiLayoutItemDTO root, ApiLayoutItemDTO node, boolean moveUp) {
        ApiLayoutItemDTO parent = getParentNode(root, node);
        if (parent == null) {
            // Node is the root node, cannot be moved
            return;
        }

        List<ApiLayoutItemDTO> siblings = parent.getChildren();
        int currentIndex = siblings.indexOf(node);
        if (currentIndex == -1) {
            // Node not found in parent's children list
            return;
        }

        int newIndex = moveUp ? currentIndex - 1 : currentIndex + 1;
        if (newIndex >= 0 && newIndex < siblings.size()) {
            // Swap the positions of the current node and the node at the new index
            siblings.set(currentIndex, siblings.get(newIndex));
            siblings.set(newIndex, node);
        }
    }

    private static ApiLayoutItemDTO getParentNode(ApiLayoutItemDTO root, ApiLayoutItemDTO node) {
        if (node.getParent() == null) {
            return null; // Node is the root node
        }

        return getNodeById(root, node.getParent());
    }

    private static int getNodeIndex(ApiLayoutItemDTO parent, ApiLayoutItemDTO node) {
        return parent.getChildren().indexOf(node);
    }

    public static ApiLayoutItemDTO getNodeById(ApiLayoutItemDTO node, Long id) {
        if (node.getId().equals(id)) {
            return node;
        }

        for (ApiLayoutItemDTO child : node.getChildren()) {
            ApiLayoutItemDTO found = getNodeById(child, id);
            if (found != null) {
                return found;
            }
        }

        return null;
    }

    private static ApiLayoutItemDTO findParent(ApiLayoutItemDTO root, ApiLayoutItemDTO node) {
        if (root == null || node == null) {
            return null; // Invalid input
        }

        if (root.getChildren().contains(node)) {
            return root; // The root is the parent of the node
        }

        for (ApiLayoutItemDTO child : root.getChildren()) {
            ApiLayoutItemDTO parent = findParent(child, node);
            if (parent != null) {
                return parent;
            }
        }

        return null; // Node not found in the tree
    }

//    private static ApiLayoutItemDTO findParentAtLevel(ApiLayoutItemDTO node, int targetLevel, ApiLayoutItemDTO excludedNode) {
//        if (node.getLoutItemDepth() == targetLevel && !node.equals(excludedNode)) {
//            return node;
//        }
//
//        if (node.getChildren() == null) {
//            return null;
//        }
//        for (ApiLayoutItemDTO child : node.getChildren()) {
//            if (!child.equals(excludedNode)) {
//                ApiLayoutItemDTO foundParent = findParentAtLevel(child, targetLevel, excludedNode);
//                if (foundParent != null) {
//                    return foundParent;
//                }
//            }
//        }
//
//        return null;
//    }

    public static void printTree(ApiLayoutItemDTO root) {
        System.out.println("====================================");
        printNode(root, 0);
        System.out.println("====================================");
    }

    private static void printNode(ApiLayoutItemDTO node, int level) {
        if (node == null) {
            return;
        }

        // Print indentation based on the level
        for (int i = 0; i < level; i++) {
            System.out.print("    ");
        }

        // Print the node information
        System.out.println(node.getId() + ": " + node.getLoutName() + "[" + node.getLoutItemType() + ", Level: " + node.getLoutItemDepth() + ", Serno: " + node.getLoutItemSerno() + "]");

        // Recursively print the children
        if (node.getChildren() != null) {
            for (ApiLayoutItemDTO child : node.getChildren()) {
                printNode(child, level + 1);
            }
        }
    }

    public static void moveNodeDepth(ApiLayoutItemDTO root, ApiLayoutItemDTO node, int newDepth) {
        if (node == null || root == null || newDepth < 0) {
            // Invalid input
            return;
        }

        ApiLayoutItemDTO parent = getParentNode(root, node);
        if (parent == null) {
            // Node is the root node, cannot be moved to a different level
            return;
        }

        int currentDepth = node.getLoutItemDepth();
        int nodeIndex = getNodeIndex(parent, node);
        ApiLayoutItemDTO nextSibling = findClosestSibling(parent.getChildren(), nodeIndex);
        parent.getChildren().remove(node);

        if (newDepth > currentDepth) {
            // Moving to a higher depth

            if (nextSibling != null) {
                nextSibling.getChildren().add(node);
                node.setParent(nextSibling.getId());
                node.setLoutItemDepth(newDepth);
                adjustChildrenDepth(node, newDepth + 1);
            } else {
                // No next sibling found, add the node back to the original parent
                parent.getChildren().add(node);
            }
        } else {
            // Moving to a lower depth
            ApiLayoutItemDTO newParent = findParent(root, parent);
            if (newParent != null) {
                newParent.getChildren().add(node);
                node.setParent(newParent.getId());
                node.setLoutItemDepth(newDepth);
                adjustChildrenDepth(node, newDepth + 1);
            } else {
                // No parent found at the desired level, add the node back to the original parent
                parent.getChildren().add(node);
            }
        }
    }

    private static void adjustChildrenDepth(ApiLayoutItemDTO node, int depth) {
        for (ApiLayoutItemDTO child : node.getChildren()) {
            child.setLoutItemDepth(depth);
            adjustChildrenDepth(child, depth + 1);
        }
    }

    private static ApiLayoutItemDTO findNextSibling(List<ApiLayoutItemDTO> siblings) {
        if (siblings == null || siblings.isEmpty()) {
            return null;
        }
        return siblings.get(0);
    }

    private static ApiLayoutItemDTO findClosestSibling(List<ApiLayoutItemDTO> siblings, int nodeIndex) {
        if (siblings == null || siblings.isEmpty()) {
            return null;
        }
        if (nodeIndex < 1 || nodeIndex >= siblings.size()) {
            return findNextSibling(siblings);
        }
        return siblings.get(nodeIndex - 1);

    }

    private static ApiLayoutItemDTO findClosestSibling(List<ApiLayoutItemDTO> siblings, ApiLayoutItemDTO node) {
        int currentIndex = siblings.indexOf(node);
        if (currentIndex == -1) {
            return null; // Node not found in the siblings list
        }

        int closestIndex = -1;
        int minDistance = Integer.MAX_VALUE;

        // Find the sibling with the minimum distance from the current node
        for (int i = 0; i < siblings.size(); i++) {
            if (i != currentIndex) {
                int distance = Math.abs(siblings.get(i).getLoutItemSerno() - node.getLoutItemSerno());
                if (distance < minDistance) {
                    minDistance = distance;
                    closestIndex = i;
                }
            }
        }

        if (closestIndex == -1) {
            return null; // No other siblings found
        }

        return siblings.get(closestIndex);
    }

//    public static void main(String[] args) {
//        ApiLayoutItemDTO root = new ApiLayoutItemDTO();
//        root.setId(1L);
//        root.setLoutName("ROOT");
//        root.setLoutItemDepth(0);
//        root.setChildren(new ArrayList<>(List.of(
//            new ApiLayoutItemDTO(2L, 1L, "Field", "Child 2", 1, 10, "TEST11"),
//            new ApiLayoutItemDTO(3L, 1L, "Group", "Child 3", 1, 0, ""),
//            new ApiLayoutItemDTO(4L, 1L, "Field", "Child 4", 1, 10, "TEST21"),
//            new ApiLayoutItemDTO(5L, 1L, "Field", "Child 5", 1, 10, "TEST22"),
////            new ApiLayoutItemDTO(6L, 1L, "Group", "Child 6", 1, 0, ""),
//            new ApiLayoutItemDTO(7L, 1L, "Field", "Child 7", 1, 10, "TEST31"),
//            new ApiLayoutItemDTO(8L, 1L, "Field", "Child 8", 1, 10, "TEST33"),
//            new ApiLayoutItemDTO(9L, 1L, "Group", "Child 9", 1, 0, ""),
//            new ApiLayoutItemDTO(10L, 1L, "Field", "Child 10", 1, 10, "TEST31"),
//            new ApiLayoutItemDTO(11L, 1L, "Field", "Child 11", 1, 10, "TEST33")
//        )));
//
//        for (ApiLayoutItemDTO item : root.getChildren()) {
//            item.setChildOccCnt(1);
//        }
//
//        System.out.println("Before moving:");
//        printTree(root);
//
//        moveNodeDepth(root, getNodeById(root, 4L), getNodeById(root, 4L).getLoutItemDepth() + 1);
//        printTree(root);
//        moveNodeDepth(root, getNodeById(root, 5L), getNodeById(root, 5L).getLoutItemDepth() + 1);
//        printTree(root);
//        moveNodeDepth(root, getNodeById(root, 7L), getNodeById(root, 7L).getLoutItemDepth() + 1);
//        printTree(root);
//        moveNodeDepth(root, getNodeById(root, 8L), getNodeById(root, 8L).getLoutItemDepth() + 1);
//        printTree(root);
//        moveNodeDepth(root, getNodeById(root, 10L), getNodeById(root, 10L).getLoutItemDepth() + 1);
//        printTree(root);
//        moveNodeDepth(root, getNodeById(root, 11L), getNodeById(root, 11L).getLoutItemDepth() + 1);
//        printTree(root);
//        moveNodeDepth(root, getNodeById(root, 9L), getNodeById(root, 9L).getLoutItemDepth() + 1);
//        printTree(root);
////        moveNodeDepth(root, getNodeById(root, 6L), getNodeById(root, 6L).getLoutItemDepth() + 1);
////        printTree(root);
//        moveNodeDepth(root, getNodeById(root, 9L), getNodeById(root, 9L).getLoutItemDepth() - 1);
//        printTree(root);
//
//        moveNodeDepth(root, getNodeById(root, 9L), getNodeById(root, 9L).getLoutItemDepth() - 1);
//        printTree(root);
//
//        moveNode(root, getNodeById(root, 9L), true);
//
//        moveNodeDepth(root, getNodeById(root, 3L), getNodeById(root, 3L).getLoutItemDepth() + 1);
//        printTree(root);
//
//        ApiLayoutDTO layout = new ApiLayoutDTO();
//        layout.setComputedLayoutItemRoot(root);
//        System.out.println(layout.buildMessage());
//    }
}
