package com.imyeyu.fx.ui.components.navigation;

import javafx.collections.FXCollections;
import javafx.collections.ListChangeListener;
import javafx.collections.ObservableList;
import javafx.geometry.Insets;
import javafx.scene.Node;
import javafx.scene.control.TitledPane;
import javafx.scene.control.ToggleButton;
import javafx.scene.layout.HBox;
import javafx.scene.layout.Region;
import javafx.scene.layout.VBox;
import lombok.Getter;

/**
 * 导航组
 *
 * @author 夜雨
 * @since 2026-06-16
 */
public class NavigationGroup extends TitledPane {

	/** 子节点列表 */
	@Getter
	private final ObservableList<Node> items;

	/** 内容容器 */
	private final VBox contentBox;

	/** 结构变更回调 */
	private Runnable structureChangedAction;

	/** 原始标题 */
	private String rawTitle;

	/** 原始图标 */
	private Node rawGraphic;

	/** 标题同步锁 */
	private boolean textSyncing;

	/** 图标同步锁 */
	private boolean graphicSyncing;

	/** 标题缩进偏移量 */
	private int levelOffset;

	/**
	 * 默认构造器
	 */
	public NavigationGroup() {
		items = FXCollections.observableArrayList();
		contentBox = new VBox();
		structureChangedAction = () -> {};
		rawTitle = "";

		getStylesheets().add(NavigationGroup.class.getResource("/timifx/style/components/navigation.css").toExternalForm());
		contentBox.setPadding(Insets.EMPTY);
		setContent(contentBox);
		getStyleClass().add("group-pane");
		items.addListener(this::handleItemsChanged);
		initRawStateListener();
	}

	/**
	 * 通过标题创建导航组
	 *
	 * @param title 标题
	 */
	public NavigationGroup(String title) {
		this();
		setText(title);
	}

	/**
	 * 添加导航项
	 *
	 * @param buttons 导航项
	 * @return 当前导航组
	 */
	public NavigationGroup add(ToggleButton... buttons) {
		items.addAll(buttons);
		return this;
	}

	/**
	 * 添加导航组
	 *
	 * @param groups 导航组
	 * @return 当前导航组
	 */
	public NavigationGroup add(NavigationGroup... groups) {
		items.addAll(groups);
		return this;
	}

	/**
	 * 添加子节点
	 *
	 * @param nodes 子节点
	 * @return 当前导航组
	 */
	public NavigationGroup add(Node... nodes) {
		items.addAll(nodes);
		return this;
	}

	/**
	 * 设置是否默认展开
	 *
	 * @param expanded true 为默认展开
	 * @return 当前导航组
	 */
	public NavigationGroup expanded(boolean expanded) {
		setExpanded(expanded);
		return this;
	}

	/**
	 * 设置标题缩进偏移量
	 *
	 * @param levelOffset 偏移量
	 * @return 当前导航组
	 */
	public NavigationGroup levelOffset(int levelOffset) {
		this.levelOffset = Math.max(0, levelOffset);
		return this;
	}

	/**
	 * 获取内容容器
	 *
	 * @return 内容容器
	 */
	VBox getContentBox() {
		return contentBox;
	}

	/**
	 * 获取标题缩进偏移量
	 *
	 * @return 标题缩进偏移量
	 */
	int getLevelOffset() {
		return levelOffset;
	}

	/**
	 * 设置结构变更回调
	 *
	 * @param structureChangedAction 回调
	 */
	void setStructureChangedAction(Runnable structureChangedAction) {
		this.structureChangedAction = structureChangedAction == null ? () -> {} : structureChangedAction;
		for (Node node : items) {
			if (node instanceof NavigationGroup group) {
				group.setStructureChangedAction(this::notifyStructureChanged);
			}
		}
	}

	/**
	 * 应用显示层级
	 *
	 * @param level      层级
	 * @param indentSize 缩进像素
	 */
	void applyLevel(int level, double indentSize) {
		textSyncing = true;
		super.setText(rawTitle);
		textSyncing = false;
		applyGraphicIndent(Math.max(0, level) * indentSize);
	}

	/**
	 * 处理子节点变更
	 *
	 * @param change 变更内容
	 */
	private void handleItemsChanged(ListChangeListener.Change<? extends Node> change) {
		while (change.next()) {
			if (change.wasAdded()) {
				for (Node node : change.getAddedSubList()) {
					if (node instanceof NavigationGroup group) {
						group.setStructureChangedAction(this::notifyStructureChanged);
					}
				}
			}
			if (change.wasRemoved()) {
				for (Node node : change.getRemoved()) {
					if (node instanceof NavigationGroup group) {
						group.setStructureChangedAction(null);
					}
				}
			}
		}
		notifyStructureChanged();
	}

	/**
	 * 触发结构变更通知
	 */
	private void notifyStructureChanged() {
		structureChangedAction.run();
	}

	/**
	 * 初始化原始状态监听
	 */
	private void initRawStateListener() {
		rawTitle = getText() == null ? "" : getText();
		rawGraphic = getGraphic();

		textProperty().addListener((obs, o, n) -> {
			if (!textSyncing) {
				rawTitle = n == null ? "" : n;
			}
		});
		graphicProperty().addListener((obs, o, n) -> {
			if (!graphicSyncing) {
				rawGraphic = n;
			}
		});
	}

	/**
	 * 应用标题缩进
	 *
	 * @param indentWidth 缩进宽度
	 */
	private void applyGraphicIndent(double indentWidth) {
		if (0 == indentWidth) {
			graphicSyncing = true;
			super.setGraphic(rawGraphic);
			graphicSyncing = false;
			return;
		}

		Region spacer = new Region();
		spacer.setMinWidth(indentWidth);
		spacer.setPrefWidth(indentWidth);
		spacer.setMaxWidth(indentWidth);

		HBox graphicBox = new HBox();
		graphicBox.getChildren().add(spacer);
		if (rawGraphic != null) {
			graphicBox.getChildren().add(rawGraphic);
		}
		graphicSyncing = true;
		super.setGraphic(graphicBox);
		graphicSyncing = false;
	}
}
