import { n as __exportAll } from "./chunk-B-1-B7_t.js";
import { n as cn } from "./utils-DXzSsU1e.js";
import { n as Badge_default } from "./badge-BbQrSqE-.js";
import { n as Button_default } from "./button-B9CnnuTu.js";
import { n as CardTitle_default, o as CardContent_default, r as CardHeader_default, s as Card_default } from "./card-X74FuL7B.js";
import { Fragment, computed, createBlock, createCommentVNode, createElementBlock, createElementVNode, createTextVNode, createVNode, defineComponent, normalizeClass, onBeforeUnmount, onMounted, openBlock, ref, renderList, resolveDynamicComponent, toDisplayString, unref, watch, withCtx } from "vue";
import { Activity, AlertTriangle, Ban, CheckCircle2, ChevronDown, ChevronRight, Clock, Loader2, XCircle } from "@lucide/vue";
//#region src/components/compositions/elements/code/BuildStatus.vue?vue&type=script&setup=true&lang.ts
var _hoisted_1 = { class: "flex items-center justify-between gap-2" };
var _hoisted_2 = { class: "flex flex-wrap items-center gap-x-3 gap-y-1 text-xs text-muted-foreground" };
var _hoisted_3 = {
	key: 0,
	class: "font-mono",
	"data-testid": "build-status-sha"
};
var _hoisted_4 = {
	key: 1,
	class: "truncate",
	"data-testid": "build-status-commit-message"
};
var _hoisted_5 = {
	key: 2,
	"data-testid": "build-status-triggered-by"
};
var _hoisted_6 = {
	key: 3,
	class: "flex items-center gap-1",
	"data-testid": "build-status-duration"
};
var _hoisted_7 = {
	key: 0,
	class: "text-xs text-destructive",
	role: "alert",
	"data-testid": "build-status-parse-error"
};
var _hoisted_8 = {
	key: 0,
	class: "max-h-80 overflow-auto rounded-md bg-muted p-3 font-mono text-xs leading-snug",
	"data-testid": "build-status-logs"
};
var BuildStatus_vue_vue_type_script_setup_true_lang_default = /* @__PURE__ */ defineComponent({
	__name: "BuildStatus",
	props: {
		initial: {},
		eventStream: {},
		socket: {},
		onUnsubscribe: {},
		showLogs: {
			type: Boolean,
			default: true
		},
		label: { default: "Build Status" },
		name: {},
		classList: {}
	},
	setup(__props, { expose: __expose }) {
		/**
		* Props-driven real-time build status composition. Subscribes to an
		* `EventSource` OR `WebSocket` (whichever is provided) and renders the
		* latest decoded `BuildStatusEvent`. The composition does NOT open or
		* close the underlying stream — the caller owns its lifecycle. The
		* optional `onUnsubscribe` callback fires on unmount for any extra
		* cleanup the caller wants.
		*
		*   <BuildStatus
		*     :initial="{ status: 'pending' }"
		*     :event-stream="es"
		*     :on-unsubscribe="() => es.close()"
		*   />
		*/
		const props = __props;
		const current = ref({
			status: props.initial?.status ?? "pending",
			startedAt: props.initial?.startedAt,
			finishedAt: props.initial?.finishedAt,
			durationMs: props.initial?.durationMs,
			commitSha: props.initial?.commitSha,
			commitMessage: props.initial?.commitMessage,
			triggeredBy: props.initial?.triggeredBy,
			logs: props.initial?.logs ? [...props.initial.logs] : []
		});
		const logsExpanded = ref(true);
		const parseError = ref(null);
		watch(() => props.initial, (next) => {
			if (!next) return;
			current.value = {
				status: next.status,
				startedAt: next.startedAt,
				finishedAt: next.finishedAt,
				durationMs: next.durationMs,
				commitSha: next.commitSha,
				commitMessage: next.commitMessage,
				triggeredBy: next.triggeredBy,
				logs: next.logs ? [...next.logs] : []
			};
		}, { deep: true });
		function applyEvent(payload) {
			if (!payload || typeof payload !== "object") return;
			const next = payload;
			current.value = {
				status: next.status ?? current.value.status,
				startedAt: next.startedAt ?? current.value.startedAt,
				finishedAt: next.finishedAt ?? current.value.finishedAt,
				durationMs: next.durationMs !== void 0 ? next.durationMs : current.value.durationMs,
				commitSha: next.commitSha ?? current.value.commitSha,
				commitMessage: next.commitMessage ?? current.value.commitMessage,
				triggeredBy: next.triggeredBy ?? current.value.triggeredBy,
				logs: Array.isArray(next.logs) ? next.logs : current.value.logs
			};
		}
		function handleMessage(e) {
			const raw = e.data;
			if (typeof raw === "string") try {
				applyEvent(JSON.parse(raw));
				parseError.value = null;
			} catch (err) {
				parseError.value = err instanceof Error ? err.message : String(err);
			}
			else if (raw && typeof raw === "object") applyEvent(raw);
		}
		let attached = null;
		onMounted(() => {
			const target = props.eventStream ?? props.socket;
			if (!target) return;
			const handler = (e) => {
				handleMessage(e);
			};
			target.addEventListener("message", handler);
			attached = {
				target,
				handler
			};
		});
		onBeforeUnmount(() => {
			if (attached) {
				attached.target.removeEventListener("message", attached.handler);
				attached = null;
			}
			if (props.onUnsubscribe) props.onUnsubscribe();
		});
		const statusBadgeVariant = computed(() => {
			switch (current.value.status) {
				case "running": return "info";
				case "success": return "success";
				case "failed": return "destructive";
				case "cancelled": return "warning";
				default: return "secondary";
			}
		});
		const statusIcon = computed(() => {
			switch (current.value.status) {
				case "running": return Loader2;
				case "success": return CheckCircle2;
				case "failed": return XCircle;
				case "cancelled": return Ban;
				default: return Clock;
			}
		});
		function formatDuration(value) {
			if (value === void 0 || Number.isNaN(value)) return "";
			const sec = Math.round(value / 1e3);
			if (sec < 60) return `${sec}s`;
			return `${Math.floor(sec / 60)}m ${sec % 60}s`;
		}
		const shortSha = computed(() => {
			const sha = current.value.commitSha;
			if (!sha) return "";
			return sha.length > 7 ? sha.slice(0, 7) : sha;
		});
		const durationLabel = computed(() => formatDuration(current.value.durationMs));
		const isRunning = computed(() => current.value.status === "running");
		__expose({ applyEvent });
		return (_ctx, _cache) => {
			return openBlock(), createBlock(unref(Card_default), {
				class: normalizeClass(unref(cn)(props.classList)),
				"data-element-name": props.name ?? void 0,
				"data-testid": "build-status"
			}, {
				default: withCtx(() => [createVNode(unref(CardHeader_default), { class: "flex flex-col gap-2 pb-3" }, {
					default: withCtx(() => [
						createElementVNode("div", _hoisted_1, [createVNode(unref(CardTitle_default), { class: "flex items-center gap-2 text-base" }, {
							default: withCtx(() => [createVNode(unref(Activity), { class: "size-5 text-muted-foreground" }), createElementVNode("span", null, toDisplayString(props.label), 1)]),
							_: 1
						}), createVNode(unref(Badge_default), {
							variant: statusBadgeVariant.value,
							size: "default",
							class: "capitalize",
							"data-testid": "build-status-badge"
						}, {
							default: withCtx(() => [(openBlock(), createBlock(resolveDynamicComponent(statusIcon.value), {
								class: normalizeClass(unref(cn)("size-3.5", isRunning.value ? "animate-spin" : "")),
								"data-testid": isRunning.value ? "build-status-spinner" : "build-status-icon"
							}, null, 8, ["class", "data-testid"])), createElementVNode("span", null, toDisplayString(current.value.status), 1)]),
							_: 1
						}, 8, ["variant"])]),
						createElementVNode("div", _hoisted_2, [
							shortSha.value ? (openBlock(), createElementBlock("span", _hoisted_3, toDisplayString(shortSha.value), 1)) : createCommentVNode("", true),
							current.value.commitMessage ? (openBlock(), createElementBlock("span", _hoisted_4, toDisplayString(current.value.commitMessage), 1)) : createCommentVNode("", true),
							current.value.triggeredBy ? (openBlock(), createElementBlock("span", _hoisted_5, " by " + toDisplayString(current.value.triggeredBy), 1)) : createCommentVNode("", true),
							durationLabel.value ? (openBlock(), createElementBlock("span", _hoisted_6, [createVNode(unref(Clock), { class: "size-3" }), createTextVNode(" " + toDisplayString(durationLabel.value), 1)])) : createCommentVNode("", true)
						]),
						parseError.value ? (openBlock(), createElementBlock("p", _hoisted_7, [createVNode(unref(AlertTriangle), { class: "inline size-3" }), createTextVNode(" " + toDisplayString(parseError.value), 1)])) : createCommentVNode("", true)
					]),
					_: 1
				}), props.showLogs && current.value.logs && current.value.logs.length > 0 ? (openBlock(), createBlock(unref(CardContent_default), {
					key: 0,
					class: "flex flex-col gap-2 pt-0"
				}, {
					default: withCtx(() => [createVNode(unref(Button_default), {
						type: "button",
						variant: "ghost",
						size: "sm",
						class: "w-fit -ml-2 text-xs",
						"data-testid": "build-status-logs-toggle",
						onClick: _cache[0] || (_cache[0] = ($event) => logsExpanded.value = !logsExpanded.value)
					}, {
						default: withCtx(() => [(openBlock(), createBlock(resolveDynamicComponent(logsExpanded.value ? unref(ChevronDown) : unref(ChevronRight)), { class: "size-4" })), createElementVNode("span", null, toDisplayString(logsExpanded.value ? "Hide logs" : "Show logs") + " (" + toDisplayString(current.value.logs.length) + ")", 1)]),
						_: 1
					}), logsExpanded.value ? (openBlock(), createElementBlock("pre", _hoisted_8, [createElementVNode("code", null, [(openBlock(true), createElementBlock(Fragment, null, renderList(current.value.logs, (line, i) => {
						return openBlock(), createElementBlock(Fragment, { key: i }, [createTextVNode(toDisplayString(line), 1), _cache[1] || (_cache[1] = createElementVNode("br", null, null, -1))], 64);
					}), 128))])])) : createCommentVNode("", true)]),
					_: 1
				})) : createCommentVNode("", true)]),
				_: 1
			}, 8, ["class", "data-element-name"]);
		};
	}
});
//#endregion
//#region src/components/compositions/elements/code/BuildStatus.vue
var BuildStatus_exports = /* @__PURE__ */ __exportAll({ default: () => BuildStatus_default });
var BuildStatus_default = BuildStatus_vue_vue_type_script_setup_true_lang_default;
//#endregion
export { BuildStatus_exports as n, BuildStatus_default as t };

//# sourceMappingURL=BuildStatus-DtS1km1m.js.map