<template>
    <Teleport to="body">
        <transition name="modal">
            <div v-if="visible" class="fixed inset-0 z-50 flex items-center justify-center">
                <div class="absolute inset-0 bg-deep/85 backdrop-blur-sm" @click="closeIfDone"></div>

                <div class="relative w-full max-w-[640px] mx-4 bg-panel border border-border rounded overflow-hidden shadow-2xl">
                    <!-- Header -->
                    <div class="flex items-center justify-between px-3 py-2 bg-section border-b border-border">
                        <div class="flex items-center gap-2">
                            <span class="w-1.5 h-1.5 rounded-full"
                                  :class="running ? 'bg-accent animate-pulse-dot' : (hasError ? 'bg-danger' : 'bg-accent')"></span>
                            <span class="text-[11px] font-semibold uppercase tracking-[0.1em] text-label">{{ title }}</span>
                        </div>
                        <button v-if="!running" @click="close"
                                class="text-muted hover:text-value text-sm leading-none cursor-pointer px-1">&times;</button>
                    </div>

                    <!-- Log output -->
                    <div ref="logArea" class="bg-deep p-3 max-h-[420px] overflow-y-auto scroll-smooth">
                        <div v-for="(line, i) in lines" :key="i"
                             class="flex gap-2.5 text-[11px] leading-[1.7]">
                            <span class="text-muted/60 shrink-0 select-none">{{ line.time }}</span>
                            <span :class="lineClass(line.type)">
                                <span v-if="line.type === 'success' || line.type === 'done'" class="text-accent">&#10003; </span>
                                <span v-else-if="line.type === 'error'" class="text-danger">&#10007; </span>
                                {{ line.message }}
                            </span>
                        </div>
                        <div v-if="running" class="text-accent/50 text-[11px] leading-[1.7] animate-pulse">&#9613;</div>
                    </div>

                    <!-- Footer -->
                    <div class="flex items-center justify-between px-3 py-2 border-t border-border bg-section">
                        <span class="text-[10px] text-muted">
                            {{ running ? 'Running...' : (hasError ? 'Completed with errors' : 'Completed') }}
                        </span>
                        <button @click="close" :disabled="running"
                                class="px-3 py-1 text-[10px] uppercase tracking-wider rounded-sm transition-all cursor-pointer disabled:opacity-30 disabled:cursor-not-allowed"
                                :class="running ? 'text-muted border border-border' : 'text-accent border border-accent/30 hover:bg-accent/10'">
                            Close
                        </button>
                    </div>
                </div>
            </div>
        </transition>
    </Teleport>
</template>

<script setup>
import { ref, nextTick, watch } from 'vue';

const visible = ref(false);
const title = ref('');
const lines = ref([]);
const running = ref(false);
const hasError = ref(false);
const logArea = ref(null);

const emit = defineEmits(['close']);

function getXsrfToken() {
    const match = document.cookie.match(/XSRF-TOKEN=([^;]+)/);
    return match ? decodeURIComponent(match[1]) : '';
}

async function start(url, modalTitle) {
    visible.value = true;
    title.value = modalTitle;
    lines.value = [];
    running.value = true;
    hasError.value = false;

    try {
        const response = await fetch(url, {
            method: 'POST',
            headers: {
                'X-XSRF-TOKEN': getXsrfToken(),
                'Accept': 'text/plain',
            },
            credentials: 'same-origin',
        });

        const reader = response.body.getReader();
        const decoder = new TextDecoder();
        let buffer = '';

        while (true) {
            const { done, value } = await reader.read();
            if (done) break;

            buffer += decoder.decode(value, { stream: true });
            const parts = buffer.split('\n');
            buffer = parts.pop();

            for (const part of parts) {
                if (!part.trim()) continue;
                try {
                    const line = JSON.parse(part);
                    lines.value.push(line);
                    if (line.type === 'error') hasError.value = true;
                } catch {
                    lines.value.push({ type: 'info', message: part, time: '' });
                }
            }
        }

        // Process remaining buffer
        if (buffer.trim()) {
            try {
                const line = JSON.parse(buffer);
                lines.value.push(line);
                if (line.type === 'error') hasError.value = true;
            } catch {
                lines.value.push({ type: 'info', message: buffer, time: '' });
            }
        }
    } catch (e) {
        lines.value.push({ type: 'error', message: `Connection failed: ${e.message}`, time: new Date().toLocaleTimeString('en-US', { hour12: false }) });
        hasError.value = true;
    } finally {
        running.value = false;
    }
}

function close() {
    if (running.value) return;
    visible.value = false;
    emit('close');
}

function closeIfDone() {
    if (!running.value) close();
}

// Auto-scroll to bottom on new lines
watch(lines, () => {
    nextTick(() => {
        if (logArea.value) {
            logArea.value.scrollTop = logArea.value.scrollHeight;
        }
    });
}, { deep: true });

function lineClass(type) {
    switch (type) {
        case 'step': return 'text-value font-medium';
        case 'success': return 'text-accent';
        case 'done': return 'text-accent font-semibold';
        case 'warn': return 'text-warn';
        case 'error': return 'text-danger';
        default: return 'text-label';
    }
}

defineExpose({ start });
</script>

<style scoped>
.modal-enter-active { transition: opacity 0.15s ease; }
.modal-leave-active { transition: opacity 0.1s ease; }
.modal-enter-from, .modal-leave-to { opacity: 0; }
</style>
