package com.imyeyu.fx.ui.components;

import com.imyeyu.fx.utils.Nodes;
import javafx.beans.property.BooleanProperty;
import javafx.beans.property.DoubleProperty;
import javafx.beans.property.IntegerProperty;
import javafx.beans.property.ObjectProperty;
import javafx.beans.property.ReadOnlyObjectProperty;
import javafx.beans.property.ReadOnlyObjectWrapper;
import javafx.beans.property.SimpleBooleanProperty;
import javafx.beans.property.SimpleDoubleProperty;
import javafx.beans.property.SimpleIntegerProperty;
import javafx.beans.property.SimpleObjectProperty;
import javafx.collections.FXCollections;
import javafx.collections.ListChangeListener;
import javafx.collections.ObservableList;
import javafx.event.EventHandler;
import javafx.geometry.Pos;
import javafx.scene.control.Control;
import javafx.scene.control.Skin;
import javafx.scene.control.ToggleButton;
import javafx.scene.control.ToggleGroup;
import javafx.scene.input.KeyCode;
import javafx.scene.input.KeyEvent;
import javafx.scene.input.MouseEvent;
import lombok.Getter;

/**
 * 单选按钮组组件
 *
 * @author 夜雨
 * @since 2026-06-26
 */
public class RadioButtonGroup extends Control {

	/**
	 * 按钮组模式
	 *
	 * @author 夜雨
	 * @since 2026-06-26
	 */
	public enum Mode {

		/** 按钮模式 */
		BUTTON,

		/** 滑块模式 */
		SLIDER
	}

	/** 根样式 */
	public static final String STYLE_CLASS = "radio-button-group";

	/** 按钮模式样式 */
	public static final String STYLE_CLASS_BUTTON_MODE = "button-mode";

	/** 滑块模式样式 */
	public static final String STYLE_CLASS_SLIDER_MODE = "slider-mode";

	/** 容器样式 */
	public static final String STYLE_CLASS_BOX = "radio-button-group-box";

	/** 滑块样式 */
	public static final String STYLE_CLASS_SLIDER = "radio-button-group-slider";

	/** 按钮样式 */
	public static final String STYLE_CLASS_BUTTON = "radio-button-group-button";

	/** 首按钮样式 */
	public static final String STYLE_CLASS_FIRST = "first";

	/** 中间按钮样式 */
	public static final String STYLE_CLASS_MIDDLE = "middle";

	/** 末按钮样式 */
	public static final String STYLE_CLASS_LAST = "last";

	/** 单按钮样式 */
	public static final String STYLE_CLASS_SINGLE = "single";

	/** 按钮列表 */
	@Getter
	private final ObservableList<ToggleButton> buttons;

	/** 按钮组 */
	private final ToggleGroup toggleGroup;

	/** 当前选中按钮 */
	private final ReadOnlyObjectWrapper<ToggleButton> selectedButton;

	/** 当前选中下标 */
	private final IntegerProperty selectedIndex;

	/** 按钮间距 */
	private final DoubleProperty spacing;

	/** 布局对齐方式 */
	private final ObjectProperty<Pos> alignment;

	/** 当前模式 */
	private final ObjectProperty<Mode> mode;

	/** true 为允许清空选中 */
	private final BooleanProperty allowClearSelection;

	/** 禁止重复点击清空 */
	private final EventHandler<MouseEvent> keepSelectionEvent = event -> {
		if (shouldKeepSelection(event.getSource())) {
			event.consume();
		}
	};

	/** 禁止通过键盘重复触发清空 */
	private final EventHandler<KeyEvent> keepSelectionKeyEvent = event -> {
		if ((event.getCode() == KeyCode.SPACE || event.getCode() == KeyCode.ENTER) && shouldKeepSelection(event.getSource())) {
			event.consume();
		}
	};

	/**
	 * 默认构造器
	 */
	public RadioButtonGroup() {
		buttons = FXCollections.observableArrayList();
		toggleGroup = new ToggleGroup();
		selectedButton = new ReadOnlyObjectWrapper<>();
		selectedIndex = new SimpleIntegerProperty(-1);
		spacing = new SimpleDoubleProperty();
		alignment = new SimpleObjectProperty<>(Pos.CENTER_LEFT);
		mode = new SimpleObjectProperty<>(Mode.BUTTON);
		allowClearSelection = new SimpleBooleanProperty();

		getStyleClass().addAll(STYLE_CLASS, STYLE_CLASS_BUTTON_MODE);
		setFocusTraversable(false);

		initSelectionSync();
		initButtonsListener();
		initModeStyleClass();
	}

	/**
	 * 初始化选中同步
	 */
	private void initSelectionSync() {
		toggleGroup.selectedToggleProperty().addListener((obs, oldToggle, toggle) -> {
			if (!isAllowClearSelection() && toggle == null && oldToggle instanceof ToggleButton button && buttons.contains(button)) {
				toggleGroup.selectToggle(button);
				return;
			}
			if (toggle instanceof ToggleButton button) {
				selectedButton.set(button);
				selectedIndex.set(buttons.indexOf(button));
				return;
			}
			selectedButton.set(null);
			selectedIndex.set(-1);
		});
		selectedIndex.addListener((obs, o, newIndex) -> {
			int index = newIndex == null ? -1 : newIndex.intValue();
			if (index < 0) {
				if (isAllowClearSelection()) {
					toggleGroup.selectToggle(null);
				}
				return;
			}
			if (index < buttons.size()) {
				toggleGroup.selectToggle(buttons.get(index));
			}
		});
	}

	/**
	 * 初始化按钮监听
	 */
	private void initButtonsListener() {
		buttons.addListener((ListChangeListener<ToggleButton>) change -> {
			while (change.next()) {
				if (change.wasRemoved()) {
					for (ToggleButton button : change.getRemoved()) {
						detachButton(button);
					}
				}
				if (change.wasAdded()) {
					for (ToggleButton button : change.getAddedSubList()) {
						attachButton(button);
					}
				}
			}
			syncSelectionState();
		});
	}

	/**
	 * 初始化模式样式
	 */
	private void initModeStyleClass() {
		Nodes.bindStyleClass(this, mode.isEqualTo(Mode.SLIDER), STYLE_CLASS_SLIDER_MODE, STYLE_CLASS_BUTTON_MODE);
	}

	/**
	 * 挂载按钮到当前组件
	 *
	 * @param button 按钮
	 */
	private void attachButton(ToggleButton button) {
		if (button == null) {
			throw new IllegalArgumentException("button must not be null");
		}
		button.setToggleGroup(toggleGroup);
		button.addEventFilter(MouseEvent.MOUSE_PRESSED, keepSelectionEvent);
		button.addEventFilter(KeyEvent.KEY_PRESSED, keepSelectionKeyEvent);
		button.addEventFilter(KeyEvent.KEY_RELEASED, keepSelectionKeyEvent);
	}

	/**
	 * 卸载按钮
	 *
	 * @param button 按钮
	 */
	private void detachButton(ToggleButton button) {
		button.removeEventFilter(MouseEvent.MOUSE_PRESSED, keepSelectionEvent);
		button.removeEventFilter(KeyEvent.KEY_PRESSED, keepSelectionKeyEvent);
		button.removeEventFilter(KeyEvent.KEY_RELEASED, keepSelectionKeyEvent);
		if (button.getToggleGroup() == toggleGroup) {
			button.setToggleGroup(null);
		}
	}

	/**
	 * 判断是否需要保持当前选中
	 *
	 * @param source 事件源
	 * @return true 为拦截当前重复触发
	 */
	private boolean shouldKeepSelection(Object source) {
		return !isAllowClearSelection() && source instanceof ToggleButton button && button.isSelected();
	}

	/**
	 * 同步当前选中状态
	 */
	private void syncSelectionState() {
		ToggleButton currentSelectedButton = getSelectedButton();
		if (currentSelectedButton != null && !buttons.contains(currentSelectedButton)) {
			if (isAllowClearSelection()) {
				toggleGroup.selectToggle(null);
			}
			else if (!buttons.isEmpty()) {
				toggleGroup.selectToggle(buttons.getFirst());
			}
			return;
		}
		int index = selectedIndex.get();
		if (index < 0 || buttons.size() <= index) {
			if (!buttons.isEmpty() && !isAllowClearSelection() && toggleGroup.getSelectedToggle() == null) {
				toggleGroup.selectToggle(buttons.getFirst());
			}
			return;
		}
		toggleGroup.selectToggle(buttons.get(index));
	}

	@Override
	protected Skin<?> createDefaultSkin() {
		return new RadioButtonGroupSkin(this);
	}

	/**
	 * 添加按钮
	 *
	 * @param buttons 按钮列表
	 */
	public void addButton(ToggleButton... buttons) {
		getButtons().addAll(buttons);
	}

	/**
	 * 添加文本按钮
	 *
	 * @param text 按钮文本
	 * @return 新增按钮
	 */
	public ToggleButton addButton(String text) {
		ToggleButton button = new ToggleButton(text);
		getButtons().add(button);
		return button;
	}

	/**
	 * 设置当前选中按钮
	 *
	 * @param button 当前选中按钮
	 */
	public void setSelectedButton(ToggleButton button) {
		if (button == null) {
			if (isAllowClearSelection()) {
				toggleGroup.selectToggle(null);
			}
			return;
		}
		if (!buttons.contains(button)) {
			throw new IllegalArgumentException("button must belong to current group");
		}
		toggleGroup.selectToggle(button);
	}

	/**
	 * 获取当前选中按钮
	 *
	 * @return 当前选中按钮
	 */
	public ToggleButton getSelectedButton() {
		return selectedButton.get();
	}

	/**
	 * 获取当前选中按钮属性
	 *
	 * @return 当前选中按钮属性
	 */
	public ReadOnlyObjectProperty<ToggleButton> selectedButtonProperty() {
		return selectedButton.getReadOnlyProperty();
	}

	/**
	 * 设置当前选中下标
	 *
	 * @param selectedIndex 当前选中下标
	 */
	public void setSelectedIndex(int selectedIndex) {
		this.selectedIndex.set(selectedIndex);
	}

	/**
	 * 获取当前选中下标
	 *
	 * @return 当前选中下标
	 */
	public int getSelectedIndex() {
		return selectedIndex.get();
	}

	/**
	 * 获取当前选中下标属性
	 *
	 * @return 当前选中下标属性
	 */
	public IntegerProperty selectedIndexProperty() {
		return selectedIndex;
	}

	/**
	 * 设置按钮间距
	 *
	 * @param spacing 按钮间距
	 */
	public void setSpacing(double spacing) {
		this.spacing.set(Math.max(0, spacing));
	}

	/**
	 * 获取按钮间距
	 *
	 * @return 按钮间距
	 */
	public double getSpacing() {
		return spacing.get();
	}

	/**
	 * 获取按钮间距属性
	 *
	 * @return 按钮间距属性
	 */
	public DoubleProperty spacingProperty() {
		return spacing;
	}

	/**
	 * 设置布局对齐方式
	 *
	 * @param alignment 布局对齐方式
	 */
	public void setAlignment(Pos alignment) {
		this.alignment.set(alignment);
	}

	/**
	 * 获取布局对齐方式
	 *
	 * @return 布局对齐方式
	 */
	public Pos getAlignment() {
		return alignment.get();
	}

	/**
	 * 获取布局对齐方式属性
	 *
	 * @return 布局对齐方式属性
	 */
	public ObjectProperty<Pos> alignmentProperty() {
		return alignment;
	}

	/**
	 * 设置当前模式
	 *
	 * @param mode 当前模式
	 */
	public void setMode(Mode mode) {
		this.mode.set(mode == null ? Mode.BUTTON : mode);
	}

	/**
	 * 获取当前模式
	 *
	 * @return 当前模式
	 */
	public Mode getMode() {
		return mode.get();
	}

	/**
	 * 获取当前模式属性
	 *
	 * @return 当前模式属性
	 */
	public ObjectProperty<Mode> modeProperty() {
		return mode;
	}

	/**
	 * 设置是否允许清空选中
	 *
	 * @param allowClearSelection true 为允许清空选中
	 */
	public void setAllowClearSelection(boolean allowClearSelection) {
		this.allowClearSelection.set(allowClearSelection);
		if (!allowClearSelection && toggleGroup.getSelectedToggle() == null && !buttons.isEmpty()) {
			toggleGroup.selectToggle(buttons.getFirst());
		}
	}

	/**
	 * 获取是否允许清空选中
	 *
	 * @return true 为允许清空选中
	 */
	public boolean isAllowClearSelection() {
		return allowClearSelection.get();
	}

	/**
	 * 获取是否允许清空选中属性
	 *
	 * @return 是否允许清空选中属性
	 */
	public BooleanProperty allowClearSelectionProperty() {
		return allowClearSelection;
	}
}
