<!-- pages/Anjungan/AntrianLoket/index.vue -->
<template>
  <div class="antrian-display-container">
    <!-- Header -->
    <div class="display-header">
      <div class="header-left">
        <div class="logo-circle">
          <img 
            src="/RSSA logo 1.png" 
            alt="RSUD Logo" 
            class="header-logo"
            width="64"
            height="64"
            style="width: 64px; height: 64px; object-fit: contain;"
          />
        </div>
        <div class="header-text">
          <h1 class="hospital-name">ANTRIAN LOKET</h1>
          <p class="display-subtitle">RSUD dr. Saiful Anwar Provinsi Jawa Timur</p>
          <!-- <p v-if="loketData" class="klinik-info">{{ loketData.namaLoket }}</p> -->
        </div>
      </div>
      <div class="header-right">
        <div class="datetime-display">
          <div class="time-large">{{ currentTime }}</div>
          <div class="date-small">{{ currentDate }}</div>
        </div>
      </div>
    </div>

    <!-- Hero Call Section - Large with multiple calls on the side -->
    <Transition name="hero-fade">
      <div v-if="currentCalledQueue" class="hero-main-section">
        <div class="hero-call-section">
          <div class="call-label">
            <v-icon size="32" color="white" class="mr-2">mdi-bullhorn</v-icon>
            SEDANG DIPANGGIL
          </div>
          <div class="call-number">{{ currentCalledQueue.noAntrian.split(' |')[0] }}</div>
          <div class="call-klinik">{{ currentCalledQueue.klinik || 'Klinik' }}</div>
          <div v-if="currentMultipleCallsTimer" class="call-timer-main">
            <v-icon size="20" color="white">mdi-timer</v-icon>
            <span>{{ currentMultipleCallsTimer }}</span>
          </div>
        </div>
      </div>
    </Transition>

    <!-- Next 5 Tickets to be Called -->
    <div v-if="nextTicketsToCall && nextTicketsToCall.length > 0" class="next-tickets-section">
      <div class="next-tickets-header">
        <v-icon size="24" color="primary-600">mdi-clock-fast</v-icon>
        <span>5 Tiket Akan Dipanggil Tercepat</span>
      </div>
      <div class="next-tickets-grid">
        <div 
          v-for="(ticket, idx) in nextTicketsToCall" 
          :key="`next-${ticket.no}`"
          class="next-ticket-card"
          :class="{ 
            'is-multiple-call': ticket.isMultipleCall,
            'highlight-called': isCalled(ticket),
            'is-next-in-queue': idx === 0 && !isCalled(ticket)
          }"
        >
          <div class="next-ticket-number">{{ ticket.noAntrian.split(' |')[0] }}</div>
          <div class="next-ticket-klinik">{{ ticket.klinik || 'Klinik' }}</div>
          <div v-if="ticket.loket" class="next-ticket-loket">{{ ticket.loket }}</div>
          <div v-if="getTimerText(ticket)" class="next-ticket-timer">
            <v-icon size="14" color="warning">mdi-timer</v-icon>
            <span>{{ getTimerText(ticket) }}</span>
          </div>
        </div>
      </div>
    </div>

    <!-- Main Grid - Display Klinik -->
    <div class="kliniks-grid" :style="gridStyle">
      <div
        v-for="klinik in displayedClinics"
        :key="klinik.name"
        class="klinik-box"
      >
        <!-- Klinik Header -->
        <div class="klinik-header-bar">
          <span class="klinik-title">{{ klinik.name }}</span>
          <v-chip size="small" class="klinik-count">
            <v-icon size="16" color="white" class="mr-1">mdi-account-multiple</v-icon>
            <span class="count-text">{{ klinik.totalQueues }}</span>
          </v-chip>
        </div>

          <!-- Queue Content -->
          <div class="klinik-content">
            <!-- Current Serving Queue (Large) -->
            <div class="current-serving-section">
              <div class="current-label">SEDANG DILAYANI</div>
              <div 
                v-if="klinik.currentQueue"
                class="current-number-large"
                :class="{ 'highlight-called': isCalled(klinik.currentQueue) }"
              >
                {{ klinik.currentQueue.noAntrian.split(' |')[0] }}
              </div>
              <div v-else class="current-waiting-text">
                MENUNGGU PANGGILAN
              </div>
            </div>

            <!-- Queues already Checked-In -->
            <div v-if="klinik.diLoketQueues && klinik.diLoketQueues.length > 0" class="mt-4">
              <div class="current-label mb-2">SIAP DIPANGGIL</div>
              <div 
                class="all-queues-grid"
                :style="getGridStyle(klinik.diLoketQueues.length)"
              >
                <div 
                  v-for="queue in klinik.diLoketQueues" 
                  :key="`di-loket-${queue.no}`"
                  class="queue-grid-item"
                  :class="{ 
                    'is-multiple': queue.calledByAdmin,
                    'highlight-called': isCalled(queue),
                  }"
                >
                  {{ queue.noAntrian.split(' |')[0] }}
                </div>
              </div>
            </div>

            <!-- Queues PENDING (TERTUNDA) -->
            <div v-if="klinik.pendingQueues && klinik.pendingQueues.length > 0" class="mt-4">
              <div class="current-label mb-2 text-warning">TERTUNDA</div>
              <div 
                class="all-queues-grid"
                :style="getGridStyle(klinik.pendingQueues.length)"
              >
                <div 
                  v-for="queue in klinik.pendingQueues" 
                  :key="`pending-${queue.no}`"
                  class="queue-grid-item is-pending"
                  :class="{ 
                    'highlight-called': isCalled(queue),
                  }"
                >
                  {{ queue.noAntrian.split(' |')[0] }}
                </div>
              </div>
            </div>


            <!-- Empty State -->
            <div v-if="!klinik.currentQueue && (!klinik.diLoketQueues || klinik.diLoketQueues.length === 0) && (!klinik.pendingQueues || klinik.pendingQueues.length === 0)" class="empty-state">
              <v-icon size="48" color="grey-lighten-3">mdi-clock-outline</v-icon>
              <p class="empty-text">Tidak Ada Antrian</p>
            </div>
          </div>
      </div>
    </div>

    <!-- Footer Stats -->
    <div class="footer-stats-bar">
      <div class="stat-item">
        <div class="stat-icon stat-icon-total">
          <v-icon size="32" color="success-600">mdi-format-list-numbered</v-icon>
        </div>
        <div class="stat-info">
          <div class="stat-value">{{ statistics.total }}</div>
          <div class="stat-label">Total Antrian</div>
        </div>
      </div>

      <div class="stat-divider"></div>

      <div class="stat-item">
        <div class="stat-icon stat-icon-active">
          <v-icon size="32" color="success-600">mdi-account-check</v-icon>
        </div>
        <div class="stat-info">
          <div class="stat-value">{{ statistics.active }}</div>
          <div class="stat-label">Sedang Dilayani</div>
        </div>
      </div>

      <div class="footer-message">
        <v-icon size="24" color="success-600" class="mr-2">mdi-information</v-icon>
        <span>Harap perhatikan nomor antrian Anda di layar</span>
      </div>
    </div>
  </div>
</template>

<script setup>
import { ref, computed, onMounted, onUnmounted, watch } from 'vue'
import { useQueueStore } from '@/stores/queueStore'
import { useLoketStore } from '@/stores/loketStore'
import { useMasterStore } from '@/stores/masterStore'
import { useClinicStore } from '@/stores/clinicStore'
import { useRoute, useRuntimeConfig } from '#app'

definePageMeta({
  layout: false,
})

// Set viewport meta tag for proper responsive behavior
useHead({
  meta: [
    {
      name: 'viewport',
      content: 'width=device-width, initial-scale=1.0, maximum-scale=1.0, user-scalable=no, viewport-fit=cover'
    }
  ],
  script: [
    { src: 'https://code.responsivevoice.org/responsivevoice.js?key=ZeMK8Joo' }
  ]
})

const route = useRoute()
const queueStore = useQueueStore()
const loketStore = useLoketStore()
const masterStore = useMasterStore()
const clinicStore = useClinicStore()

// Generate a unique session suffix (random ID) to fulfill "use different id" request
// This ensures that even if the same screen is opened in multiple tabs, they have distinct client IDs
const uniqueSessionSuffix = ref(process.client ? Math.random().toString(36).substring(2, 8) : '')

const currentTime = ref('')
const currentDate = ref('')
let timeInterval = null
let pollInterval = null
let broadcastChannel = null
const broadcastedPatient = ref(null)

let lastPlayedTime = 0;
let lastPlayedNo = '';

const playCallVoice = (patient, customLoketName = null) => {
  console.log("🔊 playCallVoice called for:", patient?.noAntrian);
  if (!patient || !patient.noAntrian) return;
  
  const now = Date.now();
  // Cegah double-play berbarengan (dari WS dan BroadcastChannel) dalam waktu 2 detik
  if (patient.noAntrian === lastPlayedNo && (now - lastPlayedTime < 2000)) {
    console.log("🔊 Skipped double play (debounce)");
    return;
  }
  
  lastPlayedNo = patient.noAntrian;
  lastPlayedTime = now;

  console.log("🔊 responsiveVoice object exists?", !!window.responsiveVoice);
  if (window.responsiveVoice) {
    const noAntrianRaw = patient.noAntrian.split(" |")[0];
    const formattedNo = noAntrianRaw.split('').join(' ');
    const destination = customLoketName || patient.loketName || loketData.value?.namaLoket || 'Loket';
    const textToSpeak = `Nomor antrean, ${formattedNo}, silahkan menuju ke, ${destination}`;
    
    console.log("🔊 Speaking:", textToSpeak);
    try {
      if (window.responsiveVoice.isPlaying()) {
        window.responsiveVoice.cancel();
      }
      
      window.responsiveVoice.speak(textToSpeak, "Indonesian Female", { pitch: 1, rate: 0.85, volume: 1 });
    } catch (e) {
      console.error("🔊 ResponsiveVoice Error:", e);
    }
  } else {
    console.error("🔊 ResponsiveVoice is NOT loaded yet!");
  }
};

// Get loket ID from route
const loketId = computed(() => {
  const id = route.params.id
  const idValue = Array.isArray(id) ? id[0] : id
  const parsed = parseInt(idValue)
  return isNaN(parsed) ? null : parsed
})

// Get loket data for this ID
const loketData = computed(() => {
  const id = loketId.value
  if (!id) return null
  return loketStore.getLoketById(id) || null
})

// Get current processing patient dari AdminLoket (isolated by loketId)
const currentProcessingPatient = computed(() => {
  const targetId = loketId.value;
  const key = targetId ? `loket-${targetId}` : 'loket';
  return queueStore.currentProcessingPatient?.[key] || null;
})

// Get all patients with processStage "loket" and filter status "di-loket" (sudah check-in)
// Hanya tampilkan nomor antrian yang sudah check-in dan berstatus "di-loket"
// Get all patients with processStage "loket"
const loketPatients = computed(() => {
  // Get all patients from queueStore
  const allPatients = queueStore.allPatients?.value || queueStore.allPatients || [];
  
  // Filter for this stage and relevant statuses
  // Include 'di-loket' (serving/called), 'waiting' (next in line) and 'pending' (on hold)
  return allPatients.filter(p => 
    p.processStage === 'loket' && 
    (p.status === 'di-loket' || p.status === 'anjungan' || p.status === 'pending')
  );
});

// Watch currentProcessingPatient to clear broadcastedPatient when patient changes
watch(() => {
  const targetId = loketId.value;
  const key = targetId ? `loket-${targetId}` : 'loket';
  return queueStore.currentProcessingPatient?.[key];
}, (newVal, oldVal) => {
  if (!newVal || (oldVal && newVal.no !== oldVal.no)) {
    console.log('🔄 [Anjungan] Processing patient changed, clearing broadcasted call');
    broadcastedPatient.value = null;
  }
}, { deep: true });

// Helper untuk mendapatkan waktu check-in atau waktu dipanggil
const getCheckInTime = (patient) => {
  // Jika ada checkInAt, gunakan itu
  if (patient.checkInAt) {
    return new Date(patient.checkInAt)
  }
  // Jika status di-loket dan ada createdAt, gunakan createdAt sebagai waktu check-in
  if (patient.status === 'di-loket' && patient.createdAt) {
    return new Date(patient.createdAt)
  }
  // Jika ada lastCalledAt, gunakan itu
  if (patient.lastCalledAt) {
    return new Date(patient.lastCalledAt)
  }
  // Fallback ke createdAt
  return new Date(patient.createdAt || 0)
}

// Helper untuk mendapatkan nama klinik dari pasien
// Membaca poli/klinik dari nomor antrian yang dibuat sebelumnya
// Nomor antrian dibuat dengan kode klinik tertentu, jadi kita baca dari data pasien
const getKlinikNameFromPatient = (patient) => {
  // Prioritas 1: Gunakan kodeKlinik dari data pasien (dari nomor antrian yang dibuat sebelumnya)
  // Ini adalah sumber kebenaran karena kodeKlinik disimpan saat nomor antrian dibuat
  if (patient.kodeKlinik) {
    // 1a. Coba cari di loketData._spesialisDetail (data dari API loket) terlebih dahulu
    if (loketData.value && loketData.value._spesialisDetail) {
      const spesialis = loketData.value._spesialisDetail.find(s => 
        String(s.idklinik) === String(patient.kodeKlinik)
      )
      if (spesialis && spesialis.namaklinik) {
        return spesialis.namaklinik.trim()
      }
    }
    
    // 1b. Cari di clinicStore
    const clinic = clinicStore.getClinicByKode ? clinicStore.getClinicByKode(patient.kodeKlinik) : null
    if (clinic && clinic.name) {
      return clinic.name.trim()
    }
    
    // 1c. Fallback ke masterStore
    const klinikData = masterStore.getKlinikByKode ? masterStore.getKlinikByKode(patient.kodeKlinik) : null
    if (klinikData && klinikData.nama) {
      return klinikData.nama.trim()
    }
  }
  
  // Prioritas 2: Gunakan patient.klinik jika ada dan valid
  // Ini juga dari data pasien yang dibuat sebelumnya
  if (patient.klinik && patient.klinik.trim() !== '') {
    return patient.klinik.trim()
  }
  
  // Prioritas 3: Coba parse dari noAntrian sebagai fallback
  // Catatan: Format nomor antrian adalah RA001, EA001, dll (R/E + Loket + nomor)
  // Kode klinik tidak langsung terlihat dari format ini, tapi kita coba match dengan kode yang ada
  if (patient.noAntrian) {
    const noAntrianPart = patient.noAntrian.split(' |')[0] // Ambil bagian sebelum " |"
    // Hapus prefix R/E dan Fast Track (F) jika ada
    const withoutPrefix = noAntrianPart.replace(/^[FR]?[RE]?/, '') // Hapus F, R, E di awal
    const match = withoutPrefix.match(/^([A-Z]+)/) // Extract kode loket/klinik (A, B, C, dll)
    
    if (match) {
      const kode = match[1]
      
      // Cek semua kemungkinan kode di clinicStore
      let allClinics = []
      if (clinicStore.getAllClinics) {
        allClinics = typeof clinicStore.getAllClinics === 'function' 
          ? clinicStore.getAllClinics() 
          : (clinicStore.getAllClinics.value || [])
      }
      
      // Coba match dengan kode klinik yang ada
      let matchedClinic = allClinics.find(c => c.kode === kode)
      
      if (matchedClinic && matchedClinic.name) {
        return matchedClinic.name.trim()
      }
      
      // Fallback ke masterStore
      const klinikData = masterStore.getKlinikByKode ? masterStore.getKlinikByKode(kode) : null
      if (klinikData && klinikData.nama) {
        return klinikData.nama.trim()
      }
    }
  }
  
  // Fallback: UMUM
  return 'UMUM'
}

// 1. Dapatkan semua pasien yang relevan untuk loket ini (berdasarkan loketId atau pelayanan)
const filteredPatientsForLoket = computed(() => {
  const targetLoketId = loketId.value
  let allowedPelayananCodes = []
  
  if (targetLoketId && loketData.value) {
    allowedPelayananCodes = loketData.value.pelayanan || []
  }
  
  const shouldFilterByPelayanan = targetLoketId && allowedPelayananCodes.length > 0
  
  // Ambil semua pasien yang berada di tahap 'loket'
  let allPatients = loketPatients.value
  
  // Include currentProcessingPatient jika ada dan belum ada di list
  const targetId = loketId.value;
  const key = targetId ? `loket-${targetId}` : 'loket';
  const currentProc = queueStore.currentProcessingPatient?.[key]
  
  if (currentProc) {
    const exists = allPatients.find(p => p.no === currentProc.no)
    if (!exists) {
      allPatients = [...allPatients, currentProc]
    }
  }

  // Filter based on Loket ID and assigned Services (Pelayanan)
  return allPatients.filter(patient => {
    // A. Filter by Loket ID (Priority)
    // Jika pasien sudah memiliki loketId eksplisit, harus match
    if (patient.loketId) {
      const isMatch = patient.loketId === targetLoketId || patient.loketId === targetLoketId.toString()
      if (isMatch) return true
    }
    
    // B. Filter by Loket Name (Priority)
    if (patient.loket && loketData.value) {
      if (patient.loket.toLowerCase().includes(loketData.value.namaLoket?.toLowerCase() || '')) return true
    }
    
    // C. Filter by assigned Services (Pelayanan)
    // Jika tidak ada loketId eksplisit (e.g. seed/new onsite), check if clinic is handled by this loket
    if (shouldFilterByPelayanan) {
      const klinikName = getKlinikNameFromPatient(patient)
      const clinic = clinicStore.getClinicByName ? clinicStore.getClinicByName(klinikName) : null
      
      let isAllowed = false
      if (clinic) {
        isAllowed = allowedPelayananCodes.includes(clinic.kode)
      } else {
        // Fallback checks for codes in noAntrian
        if (patient.noAntrian) {
          const noAntrianPart = patient.noAntrian.split(' |')[0]
          const match = noAntrianPart.match(/^([A-Z]+)/)
          if (match) {
            const kodeFromNoAntrian = match[1]
            if (allowedPelayananCodes.includes(kodeFromNoAntrian)) isAllowed = true
          }
        }
      }
      
      // Fallback: Match by clinic name if no code match found
      if (!isAllowed && klinikName && loketData.value) {
        const normalizedKlinik = klinikName.trim().toUpperCase();
        isAllowed = (loketData.value._spesialisDetail || []).some(s => 
          s.namaklinik && s.namaklinik.trim().toUpperCase() === normalizedKlinik
        );
      }
      
      return isAllowed
    }

    // D. If no explicit filters match and we shouldn't filter by pelayanan, 
    // we only show if it's default Loket 1 (fallback for untagged items)
    if (!targetLoketId || targetLoketId === 1) return true

    return false
  })
})

// 2. Tampilkan klinik dengan antrean masing-masing
const displayedClinics = computed(() => {
  const filteredPatients = filteredPatientsForLoket.value
  const clinicsMap = new Map()
  
  filteredPatients.forEach(patient => {
    const klinikName = getKlinikNameFromPatient(patient)
    if (!klinikName || klinikName.trim() === '') return
    
    if (!clinicsMap.has(klinikName)) {
      clinicsMap.set(klinikName, [])
    }
    clinicsMap.get(klinikName).push(patient)
  })
  
  // PENTING: Selalu tampilkan card untuk semua pelayanan loket, 
  // bahkan jika tidak ada antrian
  const targetLoketId = loketId.value
  const allowedPelayananCodes = (targetLoketId && loketData.value) ? (loketData.value.pelayanan || []) : []

  if (targetLoketId && loketData.value) {
    allowedPelayananCodes.forEach(kode => {
      // Prioritas 1: Cari nama klinik dari _spesialisDetail (data API)
      let klinikName = null
      
      if (loketData.value._spesialisDetail) {
        const spesialis = loketData.value._spesialisDetail.find(s => 
          String(s.idklinik) === String(kode)
        )
        if (spesialis && spesialis.namaklinik) {
          klinikName = spesialis.namaklinik
        }
      }
      
      // Prioritas 2: Coba getKlinikById untuk local data (numeric ID seperti 1000, 1001)
      // Local data EKSEKUTIF menggunakan ID numeric, bukan kode
      if (!klinikName) {
        const klinikData = masterStore.getKlinikById ? masterStore.getKlinikById(kode) : null
        if (klinikData && klinikData.nama) {
          klinikName = klinikData.nama
        }
      }
      
      // Prioritas 3: Fallback ke masterStore.getKlinikByKode untuk data yang pakai kode
      if (!klinikName) {
        const klinikData = masterStore.getKlinikByKode ? masterStore.getKlinikByKode(kode) : null
        if (klinikData && klinikData.nama) {
          klinikName = klinikData.nama
        }
      }
      
      // Prioritas 4: Fallback ke clinicStore
      if (!klinikName) {
        const clinic = clinicStore.getClinicByKode ? clinicStore.getClinicByKode(kode) : null
        if (clinic && clinic.name) {
          klinikName = clinic.name
        }
      }
      
      // Jika berhasil mendapatkan nama klinik, tambahkan ke map
      if (klinikName && !clinicsMap.has(klinikName)) {
        // Tambahkan klinik kosong (akan ditampilkan sebagai "Tidak Ada Antrian")
        clinicsMap.set(klinikName, [])
      }
    })
  }
  
  // Process each clinic
  const clinics = Array.from(clinicsMap.entries()).map(([klinikName, patients]) => {
    // Sort berdasarkan prioritas untuk multiple klinik dalam satu loket:
    // 1. Waktu generate tiket (createdAt) - yang lebih lama lebih dulu
    // 2. Urutan tiket (no) - yang lebih kecil lebih dulu
    // 3. Waktu check-in - yang lebih lama lebih dulu
    const sortedQueues = patients.sort((a, b) => {
      // Prioritas 1: Waktu generate tiket
      const createdAtA = new Date(a.createdAt || 0)
      const createdAtB = new Date(b.createdAt || 0)
      if (createdAtA.getTime() !== createdAtB.getTime()) {
        return createdAtA - createdAtB
      }
      
      // Prioritas 2: Urutan tiket
      if (a.no !== b.no) {
        return a.no - b.no
      }
      
      // Prioritas 3: Waktu check-in
      const checkInA = getCheckInTime(a)
      const checkInB = getCheckInTime(b)
      return checkInA - checkInB
    })
    
    // Deteksi multiple calls (antrian yang dipanggil bersamaan dalam 5 detik)
    const multipleCallGroups = []
    const processed = new Set()
    
    sortedQueues.forEach((queue, index) => {
      if (processed.has(queue.no)) return
      
      // Hanya proses antrian dengan status 'di-loket' (sudah check-in)
      if (queue.status === 'di-loket') {
        const callTime = queue.lastCalledAt ? new Date(queue.lastCalledAt) : (queue.createdAt ? new Date(queue.createdAt) : null)
        if (callTime) {
          const group = [queue]
          processed.add(queue.no)
          
          sortedQueues.forEach((otherQueue, otherIndex) => {
            if (otherIndex !== index && !processed.has(otherQueue.no) && otherQueue.status === 'di-loket') {
              const otherCallTime = otherQueue.lastCalledAt ? new Date(otherQueue.lastCalledAt) : (otherQueue.createdAt ? new Date(otherQueue.createdAt) : null)
              if (otherCallTime) {
                const timeDiff = Math.abs(callTime - otherCallTime)
                // Jika dipanggil dalam 5 detik, anggap sebagai multiple call
                if (timeDiff <= 5000) {
                  group.push(otherQueue)
                  processed.add(otherQueue.no)
                }
              }
            }
          })
          
          if (group.length > 1) {
            multipleCallGroups.push(group)
          }
        }
      }
    })
    
    // Flatten multiple call groups untuk mendapatkan semua multiple calls
    const allMultipleCalls = multipleCallGroups.flat()
    
    // Determine currentQueue: The patient currently being served in this clinic
    let currentQueue = null
    const normalizedKlinikName = klinikName.trim()
    
    // Get all checked-in or pending patients for this clinic
    const servingQueues = sortedQueues.filter(q => 
      (q.status === 'di-loket' || q.status === 'pending') && 
      q.processStage === 'loket' && 
      getKlinikNameFromPatient(q) === normalizedKlinikName
    )

    // Prioritas 1: If Hero section is calling someone for THIS clinic, show them as serving
    if (currentCalledQueue.value) {
      const heroPatient = currentCalledQueue.value
      const heroKlinik = getKlinikNameFromPatient(heroPatient)
      
      if (heroKlinik === normalizedKlinikName) {
        currentQueue = sortedQueues.find(q => 
          q.no === heroPatient.no || 
          (q.noAntrian && heroPatient.noAntrian && q.noAntrian === heroPatient.noAntrian)
        ) || heroPatient
      }
    }
    
    // Prioritas 2: Persistence - If no active Hero call for this clinic, 
    // find the patient who was explicitly called by admin OR has idvisit === 8 or 7 (from database)
    if (!currentQueue) {
      const calledServingQueues = servingQueues
        .filter(q => q.calledByAdmin || q.idvisit === 8 || q.idvisit === 7)
        .sort((a, b) => {
          // Priority: idvisit === 8 or 7 has higher precedence
          const isDbActiveA = a.idvisit === 8 || a.idvisit === 7;
          const isDbActiveB = b.idvisit === 8 || b.idvisit === 7;
          if (isDbActiveA && !isDbActiveB) return -1;
          if (!isDbActiveA && isDbActiveB) return 1;
          
          const timeA = new Date(a.lastCalledAt || a.createdAt || 0).getTime();
          const timeB = new Date(b.lastCalledAt || b.createdAt || 0).getTime();
          return timeB - timeA;
        })
      
      if (calledServingQueues.length > 0) {
        currentQueue = calledServingQueues[0]
      }
    }

    // Multiple calls for display
    const multipleCalls = (allMultipleCalls || []).filter(q => {
      const queueKlinik = getKlinikNameFromPatient(q)
      if (queueKlinik !== normalizedKlinikName) return false
      return q.no !== currentQueue?.no
    })
    
    // Separate di-loket and pending grids (exclude currently served patients from grid)
    const diLoketQueuesInGrid = servingQueues.filter(q => q.no !== currentQueue?.no && q.status !== 'pending' && q.idvisit !== 8 && q.idvisit !== 7);
    const pendingQueues = servingQueues.filter(q => q.status === 'pending');

    return {
      name: klinikName,
      currentQueue: currentQueue,
      multipleCalls: multipleCalls,
      diLoketQueues: diLoketQueuesInGrid,
      pendingQueues: pendingQueues, // Aded to display pending patients
      waitingQueues: [], // Removed as per request
      allQueues: diLoketQueuesInGrid,
      totalQueues: servingQueues.length
    }
  })
  
  return clinics.sort((a, b) => a.name.localeCompare(b.name))
})

// Dynamic grid logic: divide total by 2 to balance across rows, cap at 5 for larger cards
const gridColumns = computed(() => {
  const total = displayedClinics.value.length
  if (total <= 1) return 1
  if (total <= 5) return total 
  return Math.min(5, Math.ceil(total / 2))
})

const gridStyle = computed(() => ({
  gridTemplateColumns: `repeat(${gridColumns.value}, 1fr)`
}))

// Helper untuk cek apakah tiket masih dalam TTS window (15 detik)
const isInTTSWindow = (queue) => {
  if (!queue) return false
  
  const _ = currentTime.value // Force reactivity
  
  const callTime = queue.lastCalledAt ? new Date(queue.lastCalledAt) : null
  if (!callTime) return false
  
  const ttsDuration = 15000 // 15 detik
  const elapsed = Date.now() - callTime.getTime()
  const remaining = ttsDuration - elapsed
  
  return remaining > 0
}

// Current called queue - tiket yang sedang diproses di admin loket
// Nomor antrian menjadi "dipanggil" jika diproses pada AdminLoket DAN sudah dipanggil oleh admin
const currentCalledQueue = computed(() => {
  const targetLoketId = String(loketId.value)
  const CALL_DISPLAY_DURATION = 60000 // 60 seconds
  const now = new Date()
  
  // Force reactivity to time passing
  const _ = currentTime.value 

  // Prioritas 0: Broadcasted patient (Real-time from BroadcastChannel)
  if (broadcastedPatient.value) {
    const rawMsg = broadcastedPatient.value._rawMessage
    const msgLoketId = rawMsg?.loketId ? String(rawMsg.loketId) : null
    
    if (msgLoketId === targetLoketId) {
      const callTime = new Date(broadcastedPatient.value.lastCalledAt || now)
      const timeDiff = now.getTime() - callTime.getTime()
      
      if (timeDiff <= CALL_DISPLAY_DURATION) {
        return broadcastedPatient.value
      } else {
        broadcastedPatient.value = null // Clear if expired
      }
    }
  }

  // Prioritas 1: Pasien yang sedang diproses di admin loket (currentProcessingPatient)
  // Hanya tampilkan jika sudah dipanggil eksplisit oleh admin (calledByAdmin === true)
  if (currentProcessingPatient.value) {
    const processingPatient = currentProcessingPatient.value
    const patientLoketId = processingPatient.loketId ? String(processingPatient.loketId) : "1"
    
    if (patientLoketId === targetLoketId && processingPatient.calledByAdmin && processingPatient.noAntrian && processingPatient.status === 'di-loket') {
      const callTime = processingPatient.lastCalledAt ? new Date(processingPatient.lastCalledAt) : now
      const timeDiff = now.getTime() - callTime.getTime()
      
      if (timeDiff <= CALL_DISPLAY_DURATION) {
        const klinikName = getKlinikNameFromPatient(processingPatient)
        return {
          ...processingPatient,
          klinik: klinikName || processingPatient.klinik || 'Klinik'
        }
      }
    }
  }

  // Prioritas 2: Database state - Find patient in the fetched list who is currently being served (idvisit === 8 or 7)
  const allPatientsList = filteredPatientsForLoket.value
  const dbActive = allPatientsList.find(p => p.idvisit === 8 || p.idvisit === 7)
  if (dbActive) {
    const callTime = dbActive.lastCalledAt ? new Date(dbActive.lastCalledAt) : now
    const timeDiff = now.getTime() - callTime.getTime()
    
    if (timeDiff <= CALL_DISPLAY_DURATION) {
      const klinikName = getKlinikNameFromPatient(dbActive)
      return {
        ...dbActive,
        klinik: klinikName || dbActive.klinik || 'Klinik'
      }
    }
  }
  
  return null
})


// Timer untuk card utama (TTS countdown)
const currentMultipleCallsTimer = computed(() => {
  if (!currentCalledQueue.value) return null
  
  const _ = currentTime.value // Force reactivity
  
  const callTime = currentCalledQueue.value.lastCalledAt ? new Date(currentCalledQueue.value.lastCalledAt) : null
  if (!callTime) return null
  
  const ttsDuration = 15000 // 15 detik
  const elapsed = Date.now() - callTime.getTime()
  const remaining = Math.max(0, ttsDuration - elapsed)
  
  if (remaining <= 0) return null
  
  const seconds = Math.ceil(remaining / 1000)
  return `${seconds}s`
})

// Get timer text for multiple calls
const getTimerText = (queue) => {
  if (!queue) return ''
  
  const _ = currentTime.value // Force reactivity
  
  // Gunakan lastCalledAt untuk waktu panggilan
  const callTime = queue.lastCalledAt ? new Date(queue.lastCalledAt) : 
                   (queue.createdAt ? new Date(queue.createdAt) : null)
  if (!callTime) return ''
  
  // Hitung waktu text-to-speech (15 detik)
  const ttsDuration = 15000 // 15 detik
  const elapsed = Date.now() - callTime.getTime()
  const remaining = Math.max(0, ttsDuration - elapsed)
  
  if (remaining <= 0) return ''
  
  const seconds = Math.ceil(remaining / 1000)
  return `${seconds}s`
}

// Next 5 tickets to be called - tiket yang sudah check-in (status 'di-loket') dan belum diproses
const nextTicketsToCall = computed(() => {
  // Gunakan pasien yang sudah ter-filter untuk loket ini
  const filteredQueues = filteredPatientsForLoket.value.filter(p => 
    p.status === 'di-loket' && p.processStage === 'loket'
  )
  
  // Urutkan berdasarkan waktu check-in (yang check-in duluan lebih dulu)
  const sortedByCheckIn = filteredQueues.sort((a, b) => {
    const checkInA = getCheckInTime(a)
    const checkInB = getCheckInTime(b)
    return checkInA - checkInB
  })
  
  // Exclude current called queue (yang sedang diproses)
  const currentCalledNo = currentCalledQueue.value?.no
  const waitingForProcess = sortedByCheckIn.filter(q => q.no !== currentCalledNo)
  
  // Ambil 5 tiket berikutnya yang sudah check-in tapi belum diproses
  return waitingForProcess.slice(0, 5).map(queue => ({
    ...queue,
    klinik: getKlinikNameFromPatient(queue) || queue.klinik || 'Klinik',
    loket: queue.loket || 'Loket'
  }))
})

// Get grid style based on queue count - semakin sedikit antrian, semakin besar ukurannya
const getGridStyle = (queueCount) => {
  let minWidth = 85
  let fontSize = 16
  let padding = '10px 8px'
  
  if (queueCount <= 3) {
    // Jika antrian sangat sedikit (1-3), perbesar maksimal
    minWidth = 120
    fontSize = 20
    padding = '14px 12px'
  } else if (queueCount <= 6) {
    // Jika antrian sedikit (4-6), perbesar sedang
    minWidth = 100
    fontSize = 18
    padding = '12px 10px'
  } else if (queueCount <= 10) {
    // Jika antrian sedang (7-10), ukuran normal
    minWidth = 85
    fontSize = 16
    padding = '10px 8px'
  } else {
    // Jika antrian banyak (>10), ukuran lebih kecil
    minWidth = 75
    fontSize = 14
    padding = '8px 6px'
  }
  
  return {
    gridTemplateColumns: `repeat(auto-fill, minmax(${minWidth}px, 1fr))`,
    '--grid-item-font-size': `${fontSize}px`,
    '--grid-item-padding': padding
  }
}


// Check if a queue is currently called (highlighted)
const isCalled = (queue) => {
  if (!currentCalledQueue.value || !queue) return false
  return currentCalledQueue.value.no === queue.no
}

// Statistics
const statistics = computed(() => {
  const all = filteredPatientsForLoket.value
  const active = currentCalledQueue.value ? 1 : 0
  return {
    total: all.length,
    active: active
  }
})

const updateTime = () => {
  const now = new Date()
  // Format jam dengan titik dua (HH:MM:SS)
  const timeString = now.toLocaleTimeString('id-ID', { 
    hour: '2-digit', 
    minute: '2-digit',
    second: '2-digit'
  })
  currentTime.value = timeString.replace(/\./g, ':')
  currentDate.value = now.toLocaleDateString('id-ID', {
    weekday: 'long',
    day: 'numeric',
    month: 'long',
    year: 'numeric'
  })
}

// WebSocket configuration (Placeholder)
const config = useRuntimeConfig()
const wsBaseUrl = config.public?.wsBaseUrl || 'ws://10.10.123.135:8084/api/v1/ws'
const anjunganClientId = computed(() => {
  if (!loketId.value) return ''
  return `anjungan-loket-${loketId.value}`
})

const fetchAllData = async () => {
  if (!loketId.value) return;
  console.log('🔄 Anjungan refresh: Fetching data for current loket...');
  try {
    // 1. Fetch data for this loket
    await queueStore.fetchPatientsForLoket(loketId.value);
    
    // 2. Ensure initial data
    queueStore.ensureInitialData();
    
    console.log('✅ Anjungan refresh: Success');
  } catch (err) {
    console.error('❌ Anjungan refresh error:', err);
  }
};

const isConnected = computed(() => queueStore.isWsConnected);

// Watch for clientId changes and reconnect if needed
watch(anjunganClientId, (newClientId, oldClientId) => {
  if (newClientId && newClientId !== oldClientId) {
    console.log('🔄 Client ID changed, reconnecting centralized WebSocket...')
    queueStore.initWebSocket(newClientId)
  }
})

// Watch for Global Calls (Cross-Device Sync)
watch(() => queueStore.lastGlobalCall, (newCall) => {
  if (!newCall) return;
  
  const targetId = String(loketId.value);
  const senderId = String(newCall.loketId || '');
  
  // Only react if the call is for THIS loket
  if (senderId === targetId) {
    console.log(`🔔 [Anjungan] Global Call received for Loket ${targetId}:`, newCall.noAntrian);
    
    // Update local state to trigger Hero animation
    broadcastedPatient.value = {
      ...newCall,
      _source: 'websocket'
    };

    // Mainkan suara panggilan
    playCallVoice(newCall, newCall.loketName);
    
    // Sync with store using isolated key
    if (queueStore.currentProcessingPatient) {
      const key = targetId ? `loket-${targetId}` : 'loket';
      queueStore.currentProcessingPatient[key] = newCall;
    }
  } else {
    // Optional: Log ignored calls
    // console.log(`🔕 [Anjungan] Ignoring call for Loket ${senderId} (Target: ${targetId})`);
  }
}, { deep: true });

// let wsInstance = null
// const isConnected = ref(false)

// WebSocket configuration (Centralized in queueStore)
// Old implementation preserved for rollback
/*
const initWebSocketLocal = () => {
  if (!loketId.value) return null
  
  console.log('🔌 WebSocket: Connecting to', wsBaseUrl)
  console.log('🆔 Client ID:', anjunganClientId.value)
  
  wsInstance = useWebSocket({
    url: wsBaseUrl,
    clientId: anjunganClientId.value,
    fallbackPostUrl: `${config.public.externalApiBaseUrl}/ws`,
    onMessage: (data) => {
      console.log('📨 WebSocket message received:', data)
      const messageData = data?.data || data
      if (messageData?.noantrian || messageData?.type === 'CALL_PATIENT') {
        queueStore.fetchPatientsForLoket(loketId.value);
        if (messageData.patient) {
          broadcastedPatient.value = {
            ...messageData.patient,
            _rawMessage: messageData
          };
          const targetId = String(loketId.value);
          const key = targetId ? `loket-${targetId}` : 'loket';
          if (queueStore.currentProcessingPatient) {
            queueStore.currentProcessingPatient[key] = messageData.patient;
          }
        }
      } else {
        queueStore.fetchPatientsForLoket(loketId.value);
      }
    },
    onOpen: () => {
      console.log('✅ WebSocket connected')
      isConnected.value = true
    },
    onClose: () => {
      isConnected.value = false
    },
    onError: () => {
      isConnected.value = false
    },
    reconnectInterval: 3000,
    maxReconnectAttempts: 10
  })
  
  return wsInstance
}
*/



onMounted(async () => {
  console.log("🚀 AntrianLoket Component mounted");

  // Wait for stores to be hydrated and ready
  await nextTick();

  // Fetch loket data from API to ensure fresh mapping
  loketStore.fetchLoketFromAPI(true).catch(console.error);
  
  updateTime();
  timeInterval = setInterval(updateTime, 1000);

  // Initialize BroadcastChannel
  try {
    broadcastChannel = new BroadcastChannel('antrian-loket-channel');
    broadcastChannel.onmessage = (event) => {
      console.log('📡 Anjungan received broadcast:', event.data);
      if (event.data?.type === 'CALL_PATIENT') {
        const { patient, loketId: senderId } = event.data;
        const targetId = String(loketId.value);
        
        console.log(`📞 Call from Loket ${senderId} to Anjungan ${targetId}`);
        
        // Match loket ID strictly to ensure call isolation
        if (targetId && String(senderId) !== targetId) {
          console.warn(`🚫 Ignoring call: Loket ID mismatch (${senderId} vs ${targetId})`);
          return;
        }
        
        // Update local state for Hero display
        broadcastedPatient.value = {
          ...patient, // Spread patient data
          _rawMessage: event.data // Attach metadata for filtering
        };

        // Mainkan suara panggilan
        playCallVoice(patient, event.data.loketName);
        
        // Sync with store using isolated key
        if (queueStore.currentProcessingPatient) {
          const key = targetId ? `loket-${targetId}` : 'loket';
          queueStore.currentProcessingPatient[key] = patient;
        }
      }
    };
  } catch (e) {
    console.error('❌ BroadcastChannel error:', e);
  }
  
  // Initial fetch
  await fetchAllData();

  // Initialize and connect WebSocket (Centralized)
  queueStore.initWebSocket(anjunganClientId.value);

  // Register interest in this specific loket for scoped WebSocket refreshes
  queueStore.registerInterest(loketId.value);

  // Polling fallback (safety net for cross-browser / cross-device sync)
  let lastPollTime = Date.now();
  pollInterval = setInterval(async () => {
    const wsConnected = queueStore.isWsConnected;
    const now = Date.now();
    const timeSinceLastPoll = now - lastPollTime;
    
    // Poll if WebSocket is disconnected (every 4s) OR as a safety net (every 15s)
    if (!wsConnected || timeSinceLastPoll >= 15000) {
      lastPollTime = now;
      await fetchAllData();
    }
  }, 4000);
});

onUnmounted(() => {
  if (timeInterval) clearInterval(timeInterval)
  if (pollInterval) clearInterval(pollInterval)
  if (broadcastChannel) broadcastChannel.close()
  queueStore.unregisterInterest(loketId.value);
})
</script>

<style scoped lang="scss">
.antrian-display-container {
  background: #FFFFFF;
  width: 100%;
  height: 100dvh;
  padding: 16px;
  font-family: 'Inter', 'Roboto', sans-serif;
  overflow: hidden;
  display: flex;
  flex-direction: column;
  margin: 0;
  box-sizing: border-box;
  position: fixed;
  top: 0;
  left: 0;
  right: 0;
  bottom: 0;
}

/* Ensure body/html don't cause layout issues */
:deep(body),
:deep(html) {
  margin: 0;
  padding: 0;
  overflow: hidden;
  width: 100%;
  height: 100%;
  position: fixed;
  overscroll-behavior: none;
}

:deep(#__nuxt) {
  margin: 0;
  padding: 0;
  width: 100%;
  height: 100%;
  position: fixed;
  overflow: hidden;
}

/* ========== HEADER ========== */
.display-header {
  display: flex;
  justify-content: space-between;
  align-items: center;
  background: linear-gradient(135deg, var(--color-primary-600) 0%, var(--color-primary-700) 100%);
  border-radius: 12px;
  padding: clamp(12px, 1.2vw, 18px) clamp(16px, 2vw, 24px);
  margin-bottom: 12px;
  box-shadow: 0 8px 24px rgba(33, 150, 243, 0.3);
  flex-shrink: 0;
}

.header-left {
  display: flex;
  align-items: center;
  gap: 24px;
}

.logo-circle {
  background: rgba(255, 255, 255, 0.95);
  width: 80px;
  height: 80px;
  border-radius: 50%;
  display: flex;
  align-items: center;
  justify-content: center;
  backdrop-filter: blur(10px);
  box-shadow: 0 2px 8px rgba(0, 0, 0, 0.1);
}

.header-logo {
  width: 64px;
  height: 64px;
  object-fit: contain;
  padding: 4px;
}

.hospital-name {
  font-size: clamp(28px, 3vw, 42px);
  font-weight: 800;
  color: var(--color-neutral-100);
  margin: 0;
  letter-spacing: 1px;
  text-shadow: 0 2px 8px rgba(0, 0, 0, 0.2);
  line-height: 1.1;
}

.display-subtitle {
  font-size: 20px;
  font-weight: 600;
  color: var(--color-neutral-100);
  margin: 6px 0 0 0;
  opacity: 0.95;
}

.klinik-info {
  font-size: 16px;
  font-weight: 500;
  color: var(--color-neutral-100);
  margin: 4px 0 0 0;
  opacity: 0.85;
}

.datetime-display {
  text-align: right;
}

.time-large {
  font-size: clamp(32px, 3.5vw, 48px);
  font-weight: 900;
  color: var(--color-neutral-100);
  letter-spacing: 1.5px;
  line-height: 1;
  text-shadow: 0 2px 8px rgba(0, 0, 0, 0.2);
  font-variant-numeric: tabular-nums; /* Prevent digit shifting */
}

.date-small {
  font-size: 18px;
  color: var(--color-neutral-100);
  margin-top: 8px;
  font-weight: 500;
  opacity: 0.95;
}

/* ========== HERO MAIN SECTION ========== */
.hero-main-section {
  display: flex;
  gap: 16px;
  margin: clamp(8px, 1.2vh, 16px) 0;
  align-items: flex-start;
  flex-shrink: 0;
  overflow: hidden;
  max-height: 40vh; /* Limit hero size to prevent pushing grid too far */
  transition: all 0.5s cubic-bezier(0.4, 0, 0.2, 1);
}

/* ========== HERO CALL SECTION (LARGE) ========== */
.hero-call-section {
  background: linear-gradient(135deg, var(--color-primary-600) 0%, var(--color-primary-700) 100%);
  border-radius: 20px;
  padding: clamp(15px, 2vh, 32px);
  flex: 1;
  text-align: center;
  box-shadow: 0 12px 32px rgba(33, 150, 243, 0.35);
  animation: pulse-highlight 2s ease-in-out infinite;
}

.call-label {
  font-size: 28px;
  font-weight: 700;
  color: var(--color-neutral-100);
  letter-spacing: 2px;
  margin-bottom: 12px;
  display: flex;
  align-items: center;
  justify-content: center;
  text-transform: uppercase;
}

.call-number {
  font-size: clamp(60px, 14vh, 140px);
  font-weight: 900;
  color: var(--color-neutral-100);
  margin: clamp(8px, 1vh, 16px) 0;
  letter-spacing: clamp(4px, 1vw, 8px);
  line-height: 1;
  text-shadow: 0 4px 16px rgba(0, 0, 0, 0.3);
  animation: number-pulse 1.5s ease-in-out infinite;
}

.call-klinik {
  font-size: clamp(24px, 2.5vh, 36px);
  font-weight: 700;
  color: var(--color-neutral-100);
  letter-spacing: 1px;
}

.call-timer-main {
  display: flex;
  align-items: center;
  justify-content: center;
  gap: 8px;
  font-size: 24px;
  font-weight: 700;
  color: var(--color-neutral-100);
  margin-top: 16px;
  padding: 8px 16px;
  background: rgba(255, 255, 255, 0.15);
  border-radius: 12px;
  backdrop-filter: blur(10px);
  font-variant-numeric: tabular-nums; /* Stable timer digits */
}

/* ========== NEXT TICKETS SECTION ========== */
.next-tickets-section {
  background: var(--color-neutral-100);
  border: 2px solid var(--color-primary-200);
  border-radius: 16px;
  padding: clamp(10px, 1.2vh, 20px);
  margin-bottom: clamp(10px, 1.5vh, 20px);
  box-shadow: 0 4px 12px rgba(33, 150, 243, 0.12);
  flex-shrink: 0;
}

.next-tickets-header {
  display: flex;
  align-items: center;
  gap: 10px;
  font-size: 20px;
  font-weight: 700;
  color: var(--color-primary-600);
  margin-bottom: 16px;
  padding-bottom: 12px;
  border-bottom: 2px solid var(--color-primary-200);
}

.next-tickets-grid {
  display: grid;
  grid-template-columns: repeat(5, 1fr);
  gap: 12px;
  max-width: 100%;
}

@media (max-width: 1366px) {
  .next-tickets-grid {
    grid-template-columns: repeat(3, 1fr);
  }
}

@media (max-width: 768px) {
  .next-tickets-grid {
    grid-template-columns: repeat(2, 1fr);
  }
  
  .next-ticket-number {
    font-size: 24px;
  }
  
  .next-ticket-klinik {
    font-size: 12px;
  }
}

.next-ticket-card {
  background: var(--color-neutral-100);
  border: 2px solid var(--color-primary-300);
  border-radius: 12px;
  padding: 16px;
  text-align: center;
  transition: all 0.3s ease;
  box-shadow: 0 2px 4px rgba(0, 0, 0, 0.1);

  &.is-multiple-call {
    border-color: var(--color-warning-500);
    background: var(--color-warning-50);
    box-shadow: 0 2px 8px rgba(255, 152, 0, 0.3);
  }

  &.highlight-called {
    border-color: var(--color-danger-500);
    background: var(--color-danger-50);
    animation: highlight-flash 1s ease-in-out infinite;
    box-shadow: 0 2px 8px rgba(244, 67, 54, 0.4);
  }

  /* Next in queue - akan naik ke card utama setelah TTS selesai */
  &.is-next-in-queue {
    border-color: var(--color-primary-500);
    background: var(--color-primary-50);
    box-shadow: 0 2px 8px rgba(33, 150, 243, 0.3);
    border-width: 3px;
  }
}

.next-ticket-number {
  font-size: 32px;
  font-weight: 900;
  color: var(--color-neutral-900);
  letter-spacing: 2px;
  margin-bottom: 8px;
  line-height: 1;
  font-variant-numeric: tabular-nums; /* Stable ticket numbers */
}

.next-ticket-klinik {
  font-size: 14px;
  font-weight: 600;
  color: var(--color-neutral-600);
  margin-bottom: 4px;
}

.next-ticket-loket {
  font-size: 12px;
  font-weight: 500;
  color: var(--color-primary-600);
  margin-bottom: 6px;
}

.next-ticket-timer {
  display: flex;
  align-items: center;
  justify-content: center;
  gap: 4px;
  font-size: 12px;
  font-weight: 700;
  color: var(--color-warning-700);
  margin-top: 6px;
  font-variant-numeric: tabular-nums; /* Stable timer digits */
}


@keyframes pulse-highlight {
  0%, 100% {
    box-shadow: 0 12px 32px rgba(33, 150, 243, 0.35);
  }
  50% {
    box-shadow: 0 12px 48px rgba(33, 150, 243, 0.55);
  }
}

.call-label {
  font-size: 28px;
  font-weight: 700;
  color: var(--color-neutral-100);
  letter-spacing: 2px;
  margin-bottom: 12px;
  display: flex;
  align-items: center;
  justify-content: center;
  text-transform: uppercase;
}

.call-number {
  font-size: clamp(60px, 14vh, 140px);
  font-weight: 900;
  color: var(--color-neutral-100);
  margin: clamp(8px, 1vh, 16px) 0;
  letter-spacing: clamp(4px, 1vw, 8px);
  line-height: 1;
  text-shadow: 0 4px 16px rgba(0, 0, 0, 0.3);
  animation: number-pulse 1.5s ease-in-out infinite;
}

@keyframes number-pulse {
  0%, 100% {
    transform: scale(1);
  }
  50% {
    transform: scale(1.05);
  }
}

.call-loket {
  font-size: clamp(24px, 2.5vh, 36px);
  font-weight: 700;
  color: var(--color-neutral-100);
  letter-spacing: 1px;
}

/* Transition for Hero Section */
.hero-fade-enter-active,
.hero-fade-leave-active {
  transition: all 0.5s cubic-bezier(0.4, 0, 0.2, 1);
  max-height: 40vh;
  opacity: 1;
  margin: clamp(8px, 1.2vh, 16px) 0;
}

.hero-fade-enter-from,
.hero-fade-leave-to {
  max-height: 0;
  opacity: 0;
  margin-top: 0;
  margin-bottom: 0;
  transform: translateY(-20px);
}

/* ========== KLINIKS GRID ========== */
.kliniks-grid {
  display: grid;
  /* grid-template-columns is now handled by :style="gridStyle" */
  gap: clamp(8px, 1vw, 16px);
  margin-bottom: 12px;
  flex: 1;
  min-height: 0;
  overflow-y: auto;
}

.klinik-box {
  background: var(--color-neutral-100);
  border: 2px solid var(--color-primary-200);
  border-radius: 16px;
  overflow: hidden;
  box-shadow: 0 4px 12px rgba(33, 150, 243, 0.12);
  min-height: clamp(200px, 25vh, 320px);
  display: flex;
  flex-direction: column;
}

.klinik-header-bar {
  background: linear-gradient(135deg, var(--color-primary-600) 0%, var(--color-primary-700) 100%);
  padding: 10px 14px;
  display: flex;
  justify-content: space-between;
  align-items: flex-start;
  gap: 8px;
}

.klinik-title {
  font-size: clamp(18px, 1.2vw, 26px);
  font-weight: 800;
  color: var(--color-neutral-100);
  letter-spacing: 0.5px;
  text-transform: uppercase;
  line-height: 1.1;
  flex: 1;
  min-width: 0;
  margin-top: 2px;
  word-break: break-word;
}

.klinik-count {
  background: rgba(255, 255, 255, 0.2) !important;
  color: var(--color-neutral-100) !important;
  font-weight: 700;
  backdrop-filter: blur(10px);
  display: flex;
  align-items: center;
  gap: 6px;
  padding: 6px 12px !important;
  flex-shrink: 0;
}

.count-text {
  font-size: 18px;
  font-weight: 800;
}

.klinik-content {
  flex: 1;
  display: flex;
  flex-direction: column;
  padding: clamp(12px, 1.5vh, 24px);
  background: var(--color-primary-50);
  gap: clamp(8px, 1vh, 16px);
  min-height: 0;
}

/* Current Serving Section */
.current-serving-section {
  text-align: center;
  background: var(--color-neutral-100);
  border-radius: 12px;
  padding: clamp(10px, 1.2vw, 18px);
  margin-bottom: 12px;
}

.current-label {
  font-size: 18px;
  font-weight: 700;
  color: var(--color-primary-600);
  margin-bottom: 12px;
  letter-spacing: 0.5px;
  text-transform: uppercase;
}

.current-number-large {
  font-size: clamp(48px, 5vw, 84px);
  font-weight: 900;
  color: var(--color-neutral-900);
  letter-spacing: 2px;
  line-height: 1;
  text-shadow: 0 2px 4px rgba(0, 0, 0, 0.1);
  transition: all 0.3s ease;
  font-variant-numeric: tabular-nums; /* Stable digits */

  &.highlight-called {
    color: var(--color-danger-600);
    animation: highlight-flash 1s ease-in-out infinite;
    transform: scale(1.05);
  }
}

.current-waiting-text {
  font-size: clamp(16px, 1.2vw, 28px);
  font-weight: 700;
  color: var(--color-neutral-500);
  letter-spacing: 0.5px;
  line-height: 1.1;
  margin-top: 10px;
  opacity: 0.7;
  word-break: break-word;
  overflow-wrap: break-word;
}

/* Multiple Calls Row (Small chips) */
.multiple-calls-row {
  display: flex;
  gap: 8px;
  flex-wrap: wrap;
  margin-bottom: 12px;
}

.multiple-call-chip {
  background: var(--color-warning-100);
  border: 1px solid var(--color-warning-400);
  border-radius: 8px;
  padding: 8px 12px;
  text-align: center;
  font-size: 15px;
  font-weight: 700;
  color: var(--color-warning-800);
  transition: all 0.3s ease;
  white-space: nowrap;
  font-variant-numeric: tabular-nums; /* Stable digits */

  &.highlight-called {
    border-color: var(--color-danger-500);
    background: var(--color-danger-100);
    color: var(--color-danger-700);
    animation: highlight-flash 1s ease-in-out infinite;
  }
}

/* All Queues Grid */
.all-queues-grid {
  display: grid;
  gap: 10px;
  margin-top: 12px;
  grid-template-rows: auto;
  width: 100%;
}

.queue-grid-item {
  background: var(--color-neutral-100);
  border: 2px solid var(--color-neutral-500);
  border-radius: 8px;
  padding: var(--grid-item-padding, 10px 8px);
  text-align: center;
  font-size: var(--grid-item-font-size, 16px);
  font-weight: 700;
  color: var(--color-neutral-700);
  transition: all 0.3s ease;
  box-shadow: 0 1px 3px rgba(0, 0, 0, 0.1);

  /* Current queue - highlighted in orange */
  &.is-current {
    background: var(--color-warning-100);
    border: 3px solid var(--color-warning-600);
    color: var(--color-warning-800);
    font-weight: 800;
    box-shadow: 0 2px 6px rgba(255, 152, 0, 0.3);
  }

  /* Waiting queue - green or neutral highlight */
  &.is-waiting {
    background: var(--color-success-50);
    border: 2px solid var(--color-success-400);
    color: var(--color-success-800);
    font-weight: 600;
  }

  /* Multiple calls - highlighted in red */
  &.is-multiple {
    background: var(--color-danger-100);
    border: 3px solid var(--color-danger-600);
    color: var(--color-danger-700);
    font-weight: 800;
    box-shadow: 0 2px 6px rgba(244, 67, 54, 0.3);
  }

  /* Currently called - red with animation */
  &.highlight-called {
    background: var(--color-danger-100);
    border: 3px solid var(--color-danger-600);
    color: var(--color-danger-700);
    animation: highlight-flash 1s ease-in-out infinite;
    font-weight: 800;
    box-shadow: 0 2px 8px rgba(244, 67, 54, 0.4);
  }

  /* If both current and multiple, prioritize multiple (red) */
  &.is-current.is-multiple {
    background: var(--color-danger-100);
    border: 3px solid var(--color-danger-600);
    color: var(--color-danger-700);
    box-shadow: 0 2px 8px rgba(244, 67, 54, 0.4);
  }

  /* Terlambat - highlighted in orange */
  &.is-terlambat {
    background: var(--color-warning-50);
    border: 3px solid var(--color-warning-500);
    color: var(--color-warning-800);
    font-weight: 800;
    box-shadow: 0 2px 6px rgba(255, 152, 0, 0.3);
  }

  /* Pending - highlighted in grey */
  &.is-pending {
    background: var(--color-neutral-100);
    border: 3px solid var(--color-neutral-400);
    color: var(--color-neutral-600);
    font-weight: 800;
    box-shadow: 0 2px 6px rgba(0, 0, 0, 0.2);
    opacity: 0.8;
  }
}


.current-section {
  text-align: center;
  margin-bottom: 12px;
}

.current-label {
  font-size: 16px;
  font-weight: 700;
  color: var(--color-primary-600);
  margin-bottom: 8px;
  letter-spacing: 1px;
  text-transform: uppercase;
}

.current-number {
  font-size: 80px;
  font-weight: 900;
  color: var(--color-neutral-900);
  letter-spacing: 4px;
  line-height: 1;
  text-shadow: 0 2px 4px rgba(0, 0, 0, 0.1);
  transition: all 0.3s ease;
  font-variant-numeric: tabular-nums; /* Stable digits */

  &.highlight-called {
    color: var(--color-danger-600);
    animation: highlight-flash 1s ease-in-out infinite;
    transform: scale(1.1);
  }
}

@keyframes highlight-flash {
  0%, 100% {
    opacity: 1;
  }
  50% {
    opacity: 0.7;
  }
}


.empty-state {
  text-align: center;
  padding: clamp(10px, 2vh, 32px) 0;
  flex: 1;
  display: flex;
  flex-direction: column;
  justify-content: center;
  align-items: center;
}

.empty-text {
  font-size: 16px;
  color: var(--color-neutral-600);
  margin-top: 12px;
  font-weight: 600;
}

/* ========== FOOTER STATS ========== */
.footer-stats-bar {
  background: #FFFFFF;
  border: 2px solid var(--color-primary-200);
  border-radius: 14px;
  padding: 20px 40px;
  display: flex !important;
  align-items: center;
  justify-content: space-between;
  gap: 24px;
  box-shadow: 0 4px 16px rgba(25, 118, 210, 0.08);
  flex-shrink: 0;
  width: 100%;
  box-sizing: border-box;
  margin-top: auto;
}

.stat-item {
  display: flex;
  align-items: center;
  gap: 16px;
}

.stat-icon {
  width: 64px;
  height: 64px;
  border-radius: 16px;
  display: flex;
  align-items: center;
  justify-content: center;
}

.stat-icon-total,
.stat-icon-active {
  background: var(--color-primary-200);
}

.stat-info {
  text-align: left;
}

.stat-value {
  font-size: 40px;
  font-weight: 900;
  color: var(--color-neutral-900);
  line-height: 1;
  text-shadow: 0 2px 8px rgba(0, 0, 0, 0.1);
  font-variant-numeric: tabular-nums; /* Stable digits */
}

.stat-label {
  font-size: 14px;
  color: var(--color-neutral-700);
  font-weight: 600;
  margin-top: 4px;
  opacity: 0.95;
}

.stat-divider {
  width: 2px;
  height: 60px;
  background: var(--color-primary-200);
}

.footer-message {
  flex: 1;
  text-align: right;
  font-size: 20px;
  font-weight: 600;
  color: var(--color-neutral-800);
  display: flex;
  align-items: center;
  justify-content: flex-end;
}

/* ========== RESPONSIVE ========== */
@media (min-width: 1920px) {
  .antrian-display-container {
    padding: clamp(20px, 1.5vw, 32px);
  }
}

@media (max-width: 1366px) {
  .antrian-display-container {
    padding: 16px;
  }

  .display-header {
    padding: 16px 24px;
  }

  .logo-circle {
    width: 64px;
    height: 64px;
  }

  .logo-circle .v-icon {
    font-size: 40px !important;
  }

  .hero-call-section {
    padding: 24px;
  }

  .hero-main-section {
    flex-direction: column;
  }

  .hero-multiple-calls-side {
    flex-direction: row;
    flex-wrap: wrap;
    flex: 1;
  }

  .kliniks-grid {
    grid-template-columns: repeat(2, 1fr);
  }

  .lokets-grid {
    gap: 12px;
  }

  .loket-box {
    min-height: 220px;
    padding: 0;
  }

  .loket-header-bar {
    padding: 14px 16px;
  }

  .loket-content {
    padding: 16px;
  }

  .footer-stats-bar {
    padding: 16px 32px;
  }

  .stat-icon {
    width: 56px;
    height: 56px;
  }

  .stat-icon .v-icon {
    font-size: 28px !important;
  }

  .stat-divider {
    height: 50px;
  }
}

@media (max-width: 1024px) {
  .display-header {
    flex-direction: column;
    gap: 16px;
    text-align: center;
  }

  .header-left {
    flex-direction: column;
    gap: 12px;
  }

  .datetime-display {
    text-align: center;
  }

  .lokets-grid {
    grid-template-columns: repeat(2, 1fr);
  }

  .footer-stats-bar {
    flex-direction: column;
    gap: 16px;
  }

  .stat-divider {
    display: none;
  }

  .footer-message {
    text-align: center;
    justify-content: center;
  }
}

@media (max-width: 768px) {
  .lokets-grid {
    grid-template-columns: 1fr;
  }

  .kliniks-grid {
    grid-template-columns: 1fr;
  }

  .hero-call-card-small {
    min-width: 100px;
  }
}


/* Prevent text selection */
* {
  user-select: none;
  -webkit-font-smoothing: antialiased;
  -moz-osx-font-smoothing: grayscale;
}
</style>

