#!/bin/bash
# ================================================================
# OKRFEDEF — Sprint 07: Sesión en vivo + Consola del consultor
# WebSockets, sala de retiro, control de bloques
# Ejecutar desde: /home/evolucionamos/public_html/estrategia
# Comando: bash sprint_07_live.sh
# ================================================================
set -e
echo "========================================="
echo "  OKRFEDEF — Sprint 07: Sesión en vivo"
echo "========================================="

php artisan make:controller LiveSessionController
php artisan make:event SessionBlockStarted
php artisan make:event ParticipantJoined
php artisan make:event SessionEnded

echo ">>> Archivos base creados. Escribiendo lógica..."

# ── 1. LIVE SESSION CONTROLLER ───────────────────────────────────
cat > app/Http/Controllers/LiveSessionController.php << 'PHP'
<?php
namespace App\Http\Controllers;

use App\Events\ParticipantJoined;
use App\Events\SessionBlockStarted;
use App\Events\SessionEnded;
use App\Models\LiveSession;
use App\Models\Project;
use Illuminate\Http\Request;
use Inertia\Inertia;

class LiveSessionController extends Controller
{
    // Bloques disponibles por tipo de sesión
    private array $blocks = [
        'retiro1' => [
            ['id' => 'bienvenida',    'label' => 'Bienvenida y demo plataforma', 'duration' => 20],
            ['id' => 'diagnostico',   'label' => 'Diagnóstico y contexto global',  'duration' => 60],
            ['id' => 'dofa_mefi',     'label' => 'Calificación MEFI (factores internos)', 'duration' => 20],
            ['id' => 'dofa_mefe',     'label' => 'Calificación MEFE (factores externos)', 'duration' => 20],
            ['id' => 'dofa_debate',   'label' => 'Debate resultados DOFA',          'duration' => 30],
            ['id' => 'aspiracion',    'label' => 'Aspiración Ganadora',             'duration' => 30],
            ['id' => 'capacidades',   'label' => 'Capacidades Distintivas',         'duration' => 25],
            ['id' => 'renuncias',     'label' => 'Renuncias Estratégicas',          'duration' => 30],
            ['id' => 'apuestas',      'label' => 'Apuestas Estratégicas',           'duration' => 60],
            ['id' => 'okr_builder',   'label' => 'Constructor OKRs v1.0',           'duration' => 90],
            ['id' => 'sintesis',      'label' => 'Síntesis y cierre del retiro',    'duration' => 20],
        ],
        'retiro2' => [
            ['id' => 'validacion',    'label' => 'Validación OKRs refinados',       'duration' => 30],
            ['id' => 'frentes',       'label' => 'Despliegue OKRs por frentes',     'duration' => 60],
            ['id' => 'owners',        'label' => 'Asignación de Owners',            'duration' => 30],
            ['id' => 'iniciativas',   'label' => 'Matriz de Iniciativas',           'duration' => 40],
            ['id' => 'cfr',           'label' => 'Rituales CFR — primer check-in',  'duration' => 30],
            ['id' => 'plataforma',    'label' => 'Entrenamiento plataforma',        'duration' => 60],
            ['id' => 'junta',         'label' => 'Presentación Junta Directiva',    'duration' => 60],
            ['id' => 'cierre',        'label' => 'Plan de arranque y cierre formal','duration' => 30],
        ],
        'weekly' => [
            ['id' => 'checkin',       'label' => 'Check-in de avances',            'duration' => 15],
            ['id' => 'alertas',       'label' => 'KRs en rojo — plan de acción',   'duration' => 10],
            ['id' => 'compromisos',   'label' => 'Compromisos de la semana',        'duration' => 5],
        ],
    ];

    /**
     * Consola del consultor — vista principal de facilitación
     */
    public function console(Project $project)
    {
        $activeSession = LiveSession::where('project_id', $project->id)
            ->whereIn('status', ['waiting', 'active', 'paused'])
            ->latest()->first();

        $sessions = LiveSession::where('project_id', $project->id)
            ->orderByDesc('created_at')->get();

        return Inertia::render('Live/Console', [
            'project'       => $project->load('organization'),
            'activeSession' => $activeSession,
            'sessions'      => $sessions,
            'blocksByType'  => $this->blocks,
        ]);
    }

    /**
     * Vista del participante (celular)
     */
    public function participate(Project $project, LiveSession $session)
    {
        return Inertia::render('Live/Participate', [
            'project' => $project->load('organization'),
            'session' => $session,
            'blocks'  => $this->blocks[$session->type] ?? [],
            'user'    => auth()->user()->only('id', 'name'),
        ]);
    }

    /**
     * Vista video beam (pantalla grande)
     */
    public function beam(Project $project, LiveSession $session)
    {
        return Inertia::render('Live/Beam', [
            'project' => $project->load('organization'),
            'session' => $session,
            'blocks'  => $this->blocks[$session->type] ?? [],
        ]);
    }

    /**
     * Crear nueva sesión
     */
    public function store(Request $request, Project $project)
    {
        $request->validate([
            'type' => 'required|in:retiro1,retiro2,weekly,monthly,quarterly',
        ]);

        // Cerrar sesiones activas previas
        LiveSession::where('project_id', $project->id)
            ->whereIn('status', ['waiting', 'active', 'paused'])
            ->update(['status' => 'ended', 'ended_at' => now()]);

        $session = LiveSession::create([
            'project_id'     => $project->id,
            'type'           => $request->type,
            'status'         => 'waiting',
            'block_number'   => 0,
            'current_block'  => null,
            'connected_users'=> [],
            'session_log'    => [],
            'voice_enabled'  => true,
            'facilitated_by' => auth()->id(),
        ]);

        return response()->json(['session' => $session]);
    }

    /**
     * Iniciar sesión (cambiar de waiting a active)
     */
    public function start(Project $project, LiveSession $session)
    {
        $session->update([
            'status'     => 'active',
            'started_at' => now(),
        ]);

        broadcast(new SessionBlockStarted($session, null, 'Sesión iniciada'));

        return response()->json(['session' => $session->fresh()]);
    }

    /**
     * Avanzar al siguiente bloque
     */
    public function nextBlock(Request $request, Project $project, LiveSession $session)
    {
        $request->validate(['block_id' => 'required|string']);

        $blocks     = $this->blocks[$session->type] ?? [];
        $blockData  = collect($blocks)->firstWhere('id', $request->block_id);
        $blockNum   = $session->block_number + 1;

        // Registrar en el log
        $log = $session->session_log ?? [];
        $log[] = [
            'block'     => $request->block_id,
            'label'     => $blockData['label'] ?? $request->block_id,
            'started_at'=> now()->toISOString(),
            'block_num' => $blockNum,
        ];

        $session->update([
            'current_block' => $request->block_id,
            'block_number'  => $blockNum,
            'session_log'   => $log,
            'status'        => 'active',
        ]);

        broadcast(new SessionBlockStarted(
            $session,
            $request->block_id,
            $blockData['label'] ?? $request->block_id,
            $blockData['duration'] ?? null
        ));

        return response()->json(['session' => $session->fresh()]);
    }

    /**
     * Pausar / reanudar sesión
     */
    public function togglePause(Project $project, LiveSession $session)
    {
        $newStatus = $session->status === 'active' ? 'paused' : 'active';
        $session->update(['status' => $newStatus]);
        broadcast(new SessionBlockStarted($session, $session->current_block, $newStatus === 'paused' ? 'PAUSA' : 'REANUDADO'));
        return response()->json(['status' => $newStatus]);
    }

    /**
     * Finalizar sesión
     */
    public function end(Project $project, LiveSession $session)
    {
        $session->update([
            'status'   => 'ended',
            'ended_at' => now(),
        ]);

        broadcast(new SessionEnded($project->id, $session->id));

        return response()->json(['session' => $session->fresh()]);
    }

    /**
     * Registrar participante conectado
     */
    public function join(Project $project, LiveSession $session)
    {
        $users = $session->connected_users ?? [];
        $userId = auth()->id();
        $userName = auth()->user()->name;

        if (!collect($users)->firstWhere('id', $userId)) {
            $users[] = ['id' => $userId, 'name' => $userName, 'joined_at' => now()->toISOString()];
            $session->update(['connected_users' => $users]);
            broadcast(new ParticipantJoined($project->id, $session->id, $userId, $userName, count($users)));
        }

        return response()->json([
            'session'     => $session->fresh(),
            'user'        => ['id' => $userId, 'name' => $userName],
            'total_users' => count($users),
        ]);
    }

    /**
     * Estado actual de la sesión (polling fallback)
     */
    public function status(Project $project, LiveSession $session)
    {
        return response()->json([
            'session'         => $session->fresh(),
            'connected_count' => count($session->connected_users ?? []),
        ]);
    }
}
PHP

# ── 2. EVENTOS ───────────────────────────────────────────────────
cat > app/Events/SessionBlockStarted.php << 'PHP'
<?php
namespace App\Events;

use App\Models\LiveSession;
use Illuminate\Broadcasting\Channel;
use Illuminate\Broadcasting\InteractsWithSockets;
use Illuminate\Contracts\Broadcasting\ShouldBroadcast;
use Illuminate\Foundation\Events\Dispatchable;
use Illuminate\Queue\SerializesModels;

class SessionBlockStarted implements ShouldBroadcast
{
    use Dispatchable, InteractsWithSockets, SerializesModels;

    public function __construct(
        public LiveSession $session,
        public ?string     $blockId,
        public string      $blockLabel,
        public ?int        $durationMinutes = null
    ) {}

    public function broadcastOn(): Channel
    {
        return new Channel("live.{$this->session->id}");
    }

    public function broadcastAs(): string { return 'block.started'; }

    public function broadcastWith(): array
    {
        return [
            'session_id'       => $this->session->id,
            'block_id'         => $this->blockId,
            'block_label'      => $this->blockLabel,
            'block_number'     => $this->session->block_number,
            'duration_minutes' => $this->durationMinutes,
            'status'           => $this->session->status,
            'started_at'       => now()->toISOString(),
        ];
    }
}
PHP

cat > app/Events/ParticipantJoined.php << 'PHP'
<?php
namespace App\Events;

use Illuminate\Broadcasting\Channel;
use Illuminate\Broadcasting\InteractsWithSockets;
use Illuminate\Contracts\Broadcasting\ShouldBroadcast;
use Illuminate\Foundation\Events\Dispatchable;
use Illuminate\Queue\SerializesModels;

class ParticipantJoined implements ShouldBroadcast
{
    use Dispatchable, InteractsWithSockets, SerializesModels;

    public function __construct(
        public int    $projectId,
        public int    $sessionId,
        public int    $userId,
        public string $userName,
        public int    $totalConnected
    ) {}

    public function broadcastOn(): Channel
    {
        return new Channel("live.{$this->sessionId}");
    }

    public function broadcastAs(): string { return 'participant.joined'; }

    public function broadcastWith(): array
    {
        return [
            'user_id'         => $this->userId,
            'user_name'       => $this->userName,
            'total_connected' => $this->totalConnected,
        ];
    }
}
PHP

cat > app/Events/SessionEnded.php << 'PHP'
<?php
namespace App\Events;

use Illuminate\Broadcasting\Channel;
use Illuminate\Broadcasting\InteractsWithSockets;
use Illuminate\Contracts\Broadcasting\ShouldBroadcast;
use Illuminate\Foundation\Events\Dispatchable;
use Illuminate\Queue\SerializesModels;

class SessionEnded implements ShouldBroadcast
{
    use Dispatchable, InteractsWithSockets, SerializesModels;

    public function __construct(
        public int $projectId,
        public int $sessionId
    ) {}

    public function broadcastOn(): Channel
    {
        return new Channel("live.{$this->sessionId}");
    }

    public function broadcastAs(): string { return 'session.ended'; }

    public function broadcastWith(): array
    {
        return ['session_id' => $this->sessionId];
    }
}
PHP

# ── 3. RUTAS ─────────────────────────────────────────────────────
cat >> routes/web.php << 'PHP'

// ── Live Session Routes ───────────────────────────────────────────
use App\Http\Controllers\LiveSessionController;

Route::middleware(['auth', 'verified'])->prefix('projects/{project}/live')->group(function () {
    Route::get('/console',                    [LiveSessionController::class, 'console'])->name('live.console');
    Route::post('/sessions',                  [LiveSessionController::class, 'store'])->name('live.store');
    Route::get('/sessions/{session}/beam',    [LiveSessionController::class, 'beam'])->name('live.beam');
    Route::get('/sessions/{session}/join',    [LiveSessionController::class, 'participate'])->name('live.participate');
    Route::post('/sessions/{session}/start',  [LiveSessionController::class, 'start'])->name('live.start');
    Route::post('/sessions/{session}/next',   [LiveSessionController::class, 'nextBlock'])->name('live.next');
    Route::post('/sessions/{session}/pause',  [LiveSessionController::class, 'togglePause'])->name('live.pause');
    Route::post('/sessions/{session}/end',    [LiveSessionController::class, 'end'])->name('live.end');
    Route::post('/sessions/{session}/join',   [LiveSessionController::class, 'join'])->name('live.join');
    Route::get('/sessions/{session}/status',  [LiveSessionController::class, 'status'])->name('live.status');
});
PHP

# ── 4. VISTAS VUE ────────────────────────────────────────────────
mkdir -p resources/js/Pages/Live

# Consola del consultor
cat > resources/js/Pages/Live/Console.vue << 'VUE'
<script setup>
import { ref, computed, onMounted, onUnmounted } from 'vue'
import { router } from '@inertiajs/vue3'
import AppLayout from '@/Layouts/AppLayout.vue'
import AiChatPanel from '@/Components/AiChatPanel.vue'
import VoiceControl from '@/Components/VoiceControl.vue'
import axios from 'axios'

const props = defineProps({
    project: Object,
    activeSession: Object,
    sessions: Array,
    blocksByType: Object,
})

const session = ref(props.activeSession)
const connectedCount = ref(session.value?.connected_users?.length || 0)
const creating = ref(false)
const newType = ref('retiro1')

const typeLabels = {
    retiro1: 'Retiro 1 — Diseño Estratégico',
    retiro2: 'Retiro 2 — Activación',
    weekly:  'Reunión semanal CFR',
    monthly: 'Revisión mensual',
    quarterly: 'Retrospectiva trimestral',
}

const blocks = computed(() => session.value ? (props.blocksByType[session.value.type] || []) : [])
const sessionContext = computed(() => ({ session_id: session.value?.id, block: session.value?.current_block }))

let channel = null
onMounted(() => {
    if (session.value && window.Echo) {
        channel = window.Echo.channel(`live.${session.value.id}`)
            .listen('.participant.joined', (data) => { connectedCount.value = data.total_connected })
    }
})
onUnmounted(() => channel?.stopListening('.participant.joined'))

const createSession = async () => {
    creating.value = true
    try {
        const res = await axios.post(route('live.store', props.project.id), { type: newType.value })
        session.value = res.data.session
        if (window.Echo) {
            channel = window.Echo.channel(`live.${session.value.id}`)
                .listen('.participant.joined', (d) => { connectedCount.value = d.total_connected })
        }
    } finally { creating.value = false }
}

const startSession = async () => {
    const res = await axios.post(route('live.start', [props.project.id, session.value.id]))
    session.value = res.data.session
}

const nextBlock = async (blockId) => {
    const res = await axios.post(route('live.next', [props.project.id, session.value.id]), { block_id: blockId })
    session.value = res.data.session
}

const togglePause = async () => {
    const res = await axios.post(route('live.pause', [props.project.id, session.value.id]))
    session.value = { ...session.value, status: res.data.status }
}

const endSession = async () => {
    if (!confirm('¿Finalizar la sesión?')) return
    await axios.post(route('live.end', [props.project.id, session.value.id]))
    session.value = null
}

const beamUrl  = computed(() => session.value ? route('live.beam', [props.project.id, session.value.id]) : '#')
const joinUrl  = computed(() => session.value ? route('live.participate', [props.project.id, session.value.id]) : '#')

const statusColors = { waiting: 'bg-yellow-400', active: 'bg-green-400', paused: 'bg-orange-400', ended: 'bg-gray-400' }
</script>

<template>
    <AppLayout :title="`Consola — ${project.name}`">
        <div class="max-w-6xl mx-auto px-4 py-6">

            <div class="mb-6 flex justify-between items-start">
                <div>
                    <h1 class="text-2xl font-bold text-gray-900">Consola del Facilitador</h1>
                    <p class="text-gray-500">{{ project.organization?.name }}</p>
                </div>
                <div v-if="!session" class="flex gap-3 items-center">
                    <select v-model="newType" class="border border-gray-300 rounded-lg px-3 py-2 text-sm">
                        <option v-for="(label, type) in typeLabels" :key="type" :value="type">{{ label }}</option>
                    </select>
                    <button @click="createSession" :disabled="creating"
                            class="bg-blue-600 text-white px-4 py-2 rounded-xl text-sm font-medium hover:bg-blue-700 disabled:bg-gray-300">
                        {{ creating ? 'Creando...' : '+ Crear sesión' }}
                    </button>
                </div>
            </div>

            <div v-if="session" class="grid grid-cols-3 gap-6">

                <!-- Panel izquierdo: control de sesión -->
                <div class="col-span-2 space-y-4">

                    <!-- Estado de sesión -->
                    <div class="bg-white rounded-2xl shadow p-5">
                        <div class="flex justify-between items-center mb-4">
                            <div>
                                <div class="flex items-center gap-2">
                                    <div :class="['w-3 h-3 rounded-full', statusColors[session.status]]"></div>
                                    <span class="font-semibold text-gray-800">{{ typeLabels[session.type] }}</span>
                                </div>
                                <p class="text-sm text-gray-500 mt-1">
                                    Bloque {{ session.block_number }} ·
                                    {{ connectedCount }} participante(s) conectado(s)
                                </p>
                                <p v-if="session.current_block" class="text-sm font-medium text-blue-600 mt-1">
                                    Bloque activo: {{ session.current_block }}
                                </p>
                            </div>
                            <div class="flex gap-2">
                                <a :href="beamUrl" target="_blank"
                                   class="bg-gray-800 text-white px-3 py-1.5 rounded-lg text-xs font-medium">
                                    📺 Video Beam
                                </a>
                                <button v-if="session.status === 'waiting'" @click="startSession"
                                        class="bg-green-600 text-white px-3 py-1.5 rounded-lg text-xs font-medium hover:bg-green-700">
                                    ▶ Iniciar
                                </button>
                                <button v-if="['active','paused'].includes(session.status)" @click="togglePause"
                                        class="bg-orange-500 text-white px-3 py-1.5 rounded-lg text-xs font-medium">
                                    {{ session.status === 'active' ? '⏸ Pausar' : '▶ Reanudar' }}
                                </button>
                                <button @click="endSession"
                                        class="bg-red-500 text-white px-3 py-1.5 rounded-lg text-xs font-medium">
                                    ■ Finalizar
                                </button>
                            </div>
                        </div>

                        <!-- QR / Link de participantes -->
                        <div class="bg-blue-50 rounded-xl p-3 flex items-center justify-between">
                            <div>
                                <p class="text-xs font-semibold text-blue-700">Link para participantes</p>
                                <p class="text-xs text-blue-500 break-all">{{ joinUrl }}</p>
                            </div>
                            <button @click="navigator.clipboard.writeText(joinUrl)"
                                    class="bg-blue-600 text-white text-xs px-3 py-1.5 rounded-lg shrink-0 ml-3">
                                Copiar
                            </button>
                        </div>
                    </div>

                    <!-- Bloques de la sesión -->
                    <div class="bg-white rounded-2xl shadow p-5">
                        <h2 class="text-sm font-semibold text-gray-600 uppercase mb-3">Bloques de la sesión</h2>
                        <div class="space-y-2">
                            <div v-for="block in blocks" :key="block.id"
                                 :class="['flex items-center justify-between rounded-xl p-3 transition-all',
                                     session.current_block === block.id ? 'bg-blue-50 border-2 border-blue-400' : 'bg-gray-50 border border-gray-200']">
                                <div>
                                    <span v-if="session.current_block === block.id"
                                          class="text-xs bg-blue-500 text-white px-2 py-0.5 rounded-full mr-2">ACTIVO</span>
                                    <span class="text-sm font-medium text-gray-700">{{ block.label }}</span>
                                    <span class="text-xs text-gray-400 ml-2">{{ block.duration }} min</span>
                                </div>
                                <button @click="nextBlock(block.id)"
                                        :disabled="session.status === 'waiting' || session.status === 'ended'"
                                        :class="['text-xs px-3 py-1.5 rounded-lg font-medium transition-colors',
                                            session.current_block === block.id
                                                ? 'bg-blue-600 text-white'
                                                : 'bg-gray-200 text-gray-600 hover:bg-gray-300 disabled:opacity-40']">
                                    {{ session.current_block === block.id ? '↺ Re-iniciar' : '→ Activar' }}
                                </button>
                            </div>
                        </div>
                    </div>
                </div>

                <!-- Panel derecho: IA + Voz -->
                <div class="space-y-4">
                    <VoiceControl :project="project" />
                    <AiChatPanel :project="project" :session-context="sessionContext"
                                 placeholder="Pregunta a Claude en vivo..." />
                </div>
            </div>

            <!-- Sin sesión activa -->
            <div v-else class="bg-gray-50 rounded-2xl p-12 text-center text-gray-500">
                <p class="text-4xl mb-3">🎯</p>
                <p class="text-lg font-medium">Sin sesión activa</p>
                <p class="text-sm mt-1">Selecciona el tipo de sesión y crea una nueva</p>
            </div>

            <!-- Historial de sesiones -->
            <div v-if="sessions.length" class="mt-8">
                <h2 class="text-sm font-semibold text-gray-600 uppercase mb-3">Sesiones anteriores</h2>
                <div class="bg-white rounded-xl shadow overflow-hidden">
                    <div v-for="s in sessions" :key="s.id"
                         class="flex items-center justify-between px-4 py-3 border-b border-gray-100 last:border-0">
                        <div>
                            <span class="text-sm font-medium">{{ typeLabels[s.type] }}</span>
                            <span :class="['ml-2 text-xs px-2 py-0.5 rounded-full', statusColors[s.status]]">{{ s.status }}</span>
                        </div>
                        <span class="text-xs text-gray-400">{{ new Date(s.created_at).toLocaleDateString('es-CO') }}</span>
                    </div>
                </div>
            </div>
        </div>
    </AppLayout>
</template>
VUE

# Vista participante (celular)
cat > resources/js/Pages/Live/Participate.vue << 'VUE'
<script setup>
import { ref, onMounted, onUnmounted, computed } from 'vue'
import { Link } from '@inertiajs/vue3'
import axios from 'axios'

const props = defineProps({
    project: Object,
    session: Object,
    blocks: Array,
    user: Object,
})

const currentBlock = ref(props.session.current_block)
const sessionStatus = ref(props.session.status)
const blockLabel = ref('')
const countdown = ref(null)
const joined = ref(false)

const blockMap = Object.fromEntries((props.blocks || []).map(b => [b.id, b]))
const currentBlockData = computed(() => blockMap[currentBlock.value] || null)

// Auto-join al cargar
onMounted(async () => {
    try {
        await axios.post(route('live.join', [props.project.id, props.session.id]))
        joined.value = true
    } catch (e) { console.error(e) }

    if (window.Echo) {
        window.Echo.channel(`live.${props.session.id}`)
            .listen('.block.started', (data) => {
                currentBlock.value  = data.block_id
                blockLabel.value    = data.block_label
                sessionStatus.value = data.status
                if (data.duration_minutes) {
                    countdown.value = data.duration_minutes * 60
                    startCountdown()
                }
            })
            .listen('.session.ended', () => {
                sessionStatus.value = 'ended'
            })
    }
})

let countdownInterval = null
const startCountdown = () => {
    clearInterval(countdownInterval)
    countdownInterval = setInterval(() => {
        if (countdown.value > 0) countdown.value--
        else clearInterval(countdownInterval)
    }, 1000)
}
onUnmounted(() => clearInterval(countdownInterval))

const formatTime = (secs) => {
    const m = Math.floor(secs / 60)
    const s = secs % 60
    return `${m}:${String(s).padStart(2,'0')}`
}

const blockRoutes = {
    dofa_mefi:  (p, s) => route('dofa.rate', p.id),
    dofa_mefe:  (p, s) => route('dofa.rate', p.id),
    aspiracion: (p, s) => route('strategy.aspiration', p.id),
    capacidades:(p, s) => route('strategy.capabilities', p.id),
    renuncias:  (p, s) => route('strategy.renunciations', p.id),
    apuestas:   (p, s) => route('strategy.bets', p.id),
}

const actionUrl = computed(() => {
    const fn = blockRoutes[currentBlock.value]
    return fn ? fn(props.project) : null
})
</script>

<template>
    <div class="min-h-screen bg-gray-900 text-white flex flex-col">

        <!-- Header -->
        <div class="bg-gray-800 px-4 py-4 flex items-center justify-between">
            <div>
                <p class="text-xs text-gray-400">{{ project.organization?.name }}</p>
                <p class="text-sm font-medium">{{ user.name }}</p>
            </div>
            <div :class="['w-2 h-2 rounded-full', sessionStatus === 'active' ? 'bg-green-400 animate-pulse' : 'bg-gray-500']"></div>
        </div>

        <!-- Contenido principal -->
        <div class="flex-1 flex flex-col items-center justify-center px-6 py-8">

            <!-- Sin bloque activo -->
            <div v-if="!currentBlock || sessionStatus === 'waiting'" class="text-center">
                <div class="text-6xl mb-6">⏳</div>
                <h2 class="text-xl font-semibold mb-2">Esperando al facilitador</h2>
                <p class="text-gray-400 text-sm">La sesión comenzará en breve</p>
                <div v-if="joined" class="mt-4 flex items-center gap-2 text-green-400 text-sm">
                    <div class="w-2 h-2 rounded-full bg-green-400"></div>
                    Conectado a la sesión
                </div>
            </div>

            <!-- Sesión finalizada -->
            <div v-else-if="sessionStatus === 'ended'" class="text-center">
                <div class="text-6xl mb-6">✅</div>
                <h2 class="text-xl font-semibold mb-2">Sesión finalizada</h2>
                <p class="text-gray-400 text-sm">Gracias por participar</p>
            </div>

            <!-- Pausa -->
            <div v-else-if="sessionStatus === 'paused'" class="text-center">
                <div class="text-6xl mb-6">⏸</div>
                <h2 class="text-xl font-semibold">En pausa</h2>
            </div>

            <!-- Bloque activo -->
            <div v-else class="w-full max-w-sm">
                <div class="text-center mb-8">
                    <div class="text-sm text-gray-400 mb-2">Bloque activo</div>
                    <h2 class="text-2xl font-bold text-white mb-1">{{ currentBlockData?.label || currentBlock }}</h2>
                    <div v-if="countdown" class="text-3xl font-black text-yellow-400 mt-2">
                        {{ formatTime(countdown) }}
                    </div>
                </div>

                <!-- Botón de acción según el bloque -->
                <a v-if="actionUrl" :href="actionUrl"
                   class="block w-full bg-blue-600 hover:bg-blue-700 text-white text-center font-bold py-4 rounded-2xl text-lg transition-colors">
                    Ir a este módulo →
                </a>

                <div v-else class="bg-gray-800 rounded-2xl p-6 text-center text-gray-400 text-sm">
                    Sigue las instrucciones del facilitador
                </div>
            </div>
        </div>
    </div>
</template>
VUE

# Vista Video Beam
cat > resources/js/Pages/Live/Beam.vue << 'VUE'
<script setup>
import { ref, onMounted, onUnmounted, computed } from 'vue'

const props = defineProps({
    project: Object,
    session: Object,
    blocks: Array,
})

const currentBlock = ref(props.session.current_block)
const sessionStatus = ref(props.session.status)
const blockLabel = ref('')
const connectedCount = ref(props.session.connected_users?.length || 0)
const countdown = ref(null)
const aiMessage = ref(null)
const aiModule = ref(null)

const blockMap = Object.fromEntries((props.blocks || []).map(b => [b.id, b]))
const currentBlockData = computed(() => blockMap[currentBlock.value] || null)

let countdownInterval = null

onMounted(() => {
    if (!window.Echo) return

    window.Echo.channel(`live.${props.session.id}`)
        .listen('.block.started', (data) => {
            currentBlock.value  = data.block_id
            blockLabel.value    = data.block_label
            sessionStatus.value = data.status
            if (data.duration_minutes) {
                countdown.value = data.duration_minutes * 60
                clearInterval(countdownInterval)
                countdownInterval = setInterval(() => {
                    if (countdown.value > 0) countdown.value--
                    else clearInterval(countdownInterval)
                }, 1000)
            }
        })
        .listen('.participant.joined', (data) => {
            connectedCount.value = data.total_connected
        })
        .listen('.session.ended', () => {
            sessionStatus.value = 'ended'
        })

    window.Echo.channel(`project.${props.project.id}.ai`)
        .listen('.response.ready', (data) => {
            aiMessage.value = data.response
            aiModule.value  = data.module
        })

    window.Echo.channel(`project.${props.project.id}.voice`)
        .listen('.voice.play', (data) => {
            const audio = new Audio(data.audio_url)
            audio.play().catch(console.error)
        })
})

onUnmounted(() => clearInterval(countdownInterval))

const formatTime = (secs) => {
    if (!secs) return ''
    return `${Math.floor(secs/60)}:${String(secs%60).padStart(2,'0')}`
}

const moduleLabels = {
    dofa: 'Análisis DOFA',
    bet: 'Validación de apuesta',
    synthesis: 'Síntesis de aspiraciones',
    monthly: 'Análisis mensual',
    quarterly: 'Retrospectiva',
    chat: 'Respuesta de Claude',
}
</script>

<template>
    <div class="min-h-screen bg-gray-950 text-white flex flex-col p-8">

        <!-- Header -->
        <div class="flex justify-between items-start mb-8">
            <div>
                <div class="text-green-400 text-sm font-semibold uppercase tracking-wider mb-1">
                    {{ project.organization?.name }}
                </div>
                <h1 class="text-4xl font-black text-white">OKRFEDEF</h1>
                <p class="text-gray-400 mt-1">Plan Estratégico 2026</p>
            </div>
            <div class="text-right">
                <div class="flex items-center gap-3 justify-end">
                    <div class="w-3 h-3 rounded-full bg-green-400 animate-pulse"></div>
                    <span class="text-green-400 font-medium">{{ connectedCount }} conectado(s)</span>
                </div>
                <div v-if="countdown" class="text-5xl font-black text-yellow-400 mt-2">
                    {{ formatTime(countdown) }}
                </div>
            </div>
        </div>

        <!-- Bloque activo -->
        <div class="flex-1 flex flex-col justify-center">

            <div v-if="!currentBlock" class="text-center">
                <div class="text-8xl mb-6">🎯</div>
                <h2 class="text-5xl font-black text-gray-300">Esperando inicio</h2>
                <p class="text-gray-500 text-xl mt-4">El facilitador activará el primer bloque</p>
            </div>

            <div v-else-if="sessionStatus === 'ended'" class="text-center">
                <div class="text-8xl mb-6">🏆</div>
                <h2 class="text-5xl font-black text-green-400">Sesión completada</h2>
            </div>

            <div v-else-if="sessionStatus === 'paused'" class="text-center">
                <div class="text-8xl mb-4">⏸</div>
                <h2 class="text-4xl font-black text-yellow-400">PAUSA</h2>
            </div>

            <div v-else class="space-y-8">
                <!-- Bloque activo -->
                <div class="bg-gray-900 rounded-3xl p-8 border border-gray-700">
                    <div class="text-gray-400 text-lg uppercase tracking-widest mb-3">Bloque activo</div>
                    <h2 class="text-5xl font-black text-white">{{ currentBlockData?.label || currentBlock }}</h2>
                </div>

                <!-- Análisis IA (cuando aparece) -->
                <div v-if="aiMessage" class="bg-gradient-to-br from-purple-900 to-gray-900 rounded-3xl p-8 border border-purple-700">
                    <div class="flex items-center gap-3 mb-4">
                        <div class="w-10 h-10 bg-purple-600 rounded-full flex items-center justify-center text-white font-black text-lg">C</div>
                        <span class="text-purple-300 font-semibold text-xl">{{ moduleLabels[aiModule] || 'Claude AI' }}</span>
                    </div>
                    <div class="text-gray-200 text-xl leading-relaxed whitespace-pre-line">{{ aiMessage }}</div>
                </div>
            </div>
        </div>

        <!-- Footer -->
        <div class="flex justify-between items-center mt-8 pt-6 border-t border-gray-800">
            <span class="text-gray-600 text-sm">estrategia.evolucionamos.com</span>
            <span class="text-gray-600 text-sm">Metodología OKR + Estrategia Emergente</span>
        </div>
    </div>
</template>
VUE

# ── 5. COMPILAR Y CACHEAR ────────────────────────────────────────
echo ">>> Compilando assets Vue..."
npm run build

php artisan config:clear
php artisan route:clear
php artisan config:cache
php artisan route:cache
php artisan view:cache

echo ""
echo "========================================="
echo "  Sprint 07 completado exitosamente"
echo ""
echo "  Módulos creados:"
echo "  ✅ LiveSessionController (create,"
echo "     start, nextBlock, pause, end, join)"
echo "  ✅ Eventos: SessionBlockStarted,"
echo "     ParticipantJoined, SessionEnded"
echo "  ✅ 3 vistas Vue: Console, Participate,"
echo "     Beam (video beam)"
echo "  ✅ AiChatPanel integrado en consola"
echo "  ✅ VoiceControl integrado en consola"
echo ""
echo "  Rutas disponibles:"
echo "  /projects/{id}/live/console"
echo "  /projects/{id}/live/sessions/{s}/beam"
echo "  /projects/{id}/live/sessions/{s}/join"
echo "========================================="
