import sys
import json
from PyQt5.QtWidgets import (
    QApplication, QMainWindow, QTreeWidget, QTreeWidgetItem,
    QVBoxLayout, QWidget, QPushButton, QFileDialog, QInputDialog, QMenu
)
from PyQt5.QtCore import Qt

class TreeEditor(QMainWindow):
    def __init__(self):
        super().__init__()
        self.setWindowTitle("Tree Data Editor")
        self.resize(600, 400)

        self.tree = QTreeWidget()
        self.tree.setHeaderLabels(["Key", "Value"])
        self.tree.setContextMenuPolicy(Qt.CustomContextMenu)
        self.tree.customContextMenuRequested.connect(self.open_menu)

        self.export_btn = QPushButton("Export to JSON")
        self.export_btn.clicked.connect(self.export_json)

        layout = QVBoxLayout()
        layout.addWidget(self.tree)
        layout.addWidget(self.export_btn)

        container = QWidget()
        container.setLayout(layout)
        self.setCentralWidget(container)

    def open_menu(self, pos):
        item = self.tree.itemAt(pos)
        menu = QMenu()
        add_action = menu.addAction("Add Child")
        edit_action = menu.addAction("Edit")
        del_action = menu.addAction("Delete")
        action = menu.exec_(self.tree.viewport().mapToGlobal(pos))
        if action == add_action:
            self.add_child(item)
        elif action == edit_action:
            self.edit_item(item)
        elif action == del_action:
            self.delete_item(item)

    def add_child(self, parent=None):
        key, ok = QInputDialog.getText(self, "Key", "Enter key:")
        if not ok or not key:
            return
        value, ok = QInputDialog.getText(self, "Value", "Enter value:")
        if not ok:
            return
        child = QTreeWidgetItem([key, value])
        if parent:
            parent.addChild(child)
            parent.setExpanded(True)
        else:
            self.tree.addTopLevelItem(child)

    def edit_item(self, item):
        if not item:
            return
        key, ok = QInputDialog.getText(self, "Edit Key", "Key:", text=item.text(0))
        if not ok or not key:
            return
        value, ok = QInputDialog.getText(self, "Edit Value", "Value:", text=item.text(1))
        if not ok:
            return
        item.setText(0, key)
        item.setText(1, value)

    def delete_item(self, item):
        if not item:
            return
        parent = item.parent()
        if parent:
            parent.removeChild(item)
        else:
            idx = self.tree.indexOfTopLevelItem(item)
            self.tree.takeTopLevelItem(idx)

    def export_json(self):
        def item_to_dict(item):
            d = {}
            key = item.text(0)
            value = item.text(1)
            if item.childCount() == 0:
                d[key] = value
            else:
                children = {}
                for i in range(item.childCount()):
                    children.update(item_to_dict(item.child(i)))
                d[key] = children
            return d

        data = {}
        for i in range(self.tree.topLevelItemCount()):
            data.update(item_to_dict(self.tree.topLevelItem(i)))

        path, _ = QFileDialog.getSaveFileName(self, "Save JSON", "", "JSON Files (*.json)")
        if path:
            with open(path, "w", encoding="utf-8") as f:
                json.dump(data, f, indent=2)

if __name__ == "__main__":
    app = QApplication(sys.argv)
    win = TreeEditor()
    win.show()
    sys.exit(app.exec_())