pragma ComponentBehavior: Bound

import QtQuick
import QtQuick.Layouts
import Quickshell
import ZShell.Config
import ZShell.Llm
import qs.Components
import qs.Services

MouseArea {
	id: root

	property ChatGeneration current: modelData.activeGeneration
	required property int index
	readonly property bool isUser: modelData.role === ChatMessage.Role.User
	required property ChatMessage modelData
	property bool reasoningExpanded: false
	readonly property var blocks: blockify(current.segments)
	property real savedOffset: -1
	property bool stickActive: false

	function handleReasoningToggle(expanded: bool): void {
		const view = root.ListView.view;
		if (!view)
			return;

		if (expanded) {
			root.savedOffset = view.contentY - root.y;
			root.reasoningExpanded = true;
		} else {
			root.reasoningExpanded = false;
			root.stickActive = false;
			settleTimer.stop();
			if (!isNaN(root.savedOffset)) {
				restoreAnim.to = root.y + root.savedOffset;
				restoreAnim.restart();
				root.savedOffset = NaN;
			}
		}
	}

	function blockify(segs) {
		const blocks = [];
		for (let i = 0; i < segs.length; i++) {
			const seg = segs[i];
			if (seg.type === LlmSegment.Type.Content) {
				blocks.push({
					kind: "content",
					id: i,
					segments: [seg]
				});
			} else {
				const last = blocks.length ? blocks[blocks.length - 1] : null;
				if (last && last.kind === "process") {
					last.segments.push(seg);
				} else {
					blocks.push({
						kind: "process",
						id: i,
						segments: [seg]
					});
				}
			}
		}

		return blocks;
	}

	implicitWidth: ListView.view.width
	hoverEnabled: true
	preventStealing: false
	implicitHeight: layout.implicitHeight + Tokens.padding.medium

	Timer {
		id: settleTimer
		interval: 16

		onTriggered: {
			root.stickActive = false;
		}
	}

	onImplicitHeightChanged: {
		if (root.reasoningExpanded) {
			root.stickActive = true;
			settleTimer.restart();

			const view = root.ListView.view;
			if (!view)
				return;
			const bottomEdge = root.y + root.height + Tokens.padding.large * 2;
			const viewportBottom = view.contentY + view.height;
			if (bottomEdge > viewportBottom)
				view.contentY = bottomEdge - view.height;
		}
	}

	Behavior on current {
		id: changeAnim

		property var object: targetProperty
		readonly property bool next: {
			const cond = root.modelData.generations.indexOf(current) < root.modelData.generations.indexOf(changeAnim.targetValue);
			return cond;
		}

		animation: SequentialAnimation {
			ParallelAnimation {
				Anim {
					target: root
					property: "x"
					to: changeAnim.next ? root.width / 4 : -root.width / 4
					from: 0
					type: Anim.FastEffects
				}

				Anim {
					target: root
					property: "opacity"
					from: 1
					to: 0
					type: Anim.FastEffects
				}
			}

			PropertyAction {}

			ParallelAnimation {
				Anim {
					target: root
					property: "x"
					from: changeAnim.next ? -root.width / 4 : root.width / 4
					type: Anim.FastEffects
					to: 0
				}

				Anim {
					target: root
					property: "opacity"
					from: 0
					type: Anim.FastEffects
					to: 1
				}
			}
		}
	}

	Anim {
		id: restoreAnim

		property: "contentY"
		target: root.ListView.view
		type: Anim.DefaultEffects
	}

	Column {
		id: layout

		anchors.top: parent.top
		anchors.left: parent.left
		anchors.right: parent.right
		spacing: Tokens.spacing.medium

		Repeater {
			id: segmentRep

			model: ScriptModel {
				values: root.blocks
				objectProp: "id"
			}
			delegate: DelegateChooser {
				role: "kind"

				DelegateChoice {
					roleValue: "process"

					delegate: ProcessBlock {
						width: root.width
						blocks: root.blocks
						current: root.current

						onExpandedChanged: root.handleReasoningToggle(expanded)
					}
				}

				DelegateChoice {
					roleValue: "content"

					delegate: ContentBubble {
						required property var modelData

						isUser: root.isUser
						segment: modelData.segments[0]
						width: root.width
						repeater: segmentRep
						message: root.modelData
						current: root.current
						hovered: root.containsMouse

						onEdit: text => {
							root.modelData.edit(text);

							if (root.isUser)
								root.modelData.generate();
						}
					}
				}
			}
		}
	}
}
