// stores/queueStore.js
import { defineStore } from 'pinia';
import { ref, computed, watch } from 'vue';
import { useClinicStore } from './clinicStore';
import { usePenunjangStore } from './penunjangStore';
import { useLoketStore } from './loketStore';
import { useWebSocket } from '@/composables/useWebSocket';

export const useQueueStore = defineStore('queue', () => {
  const clinicStore = useClinicStore();
  const penunjangStore = usePenunjangStore();
  const loketStore = useLoketStore();

  // ============================================
  // API INTEGRATION FOR LOKET PATIENTS
  // ============================================
  
  // State untuk API patient data per loket
  const allPatients = ref([]);
  const apiPatientsPerLoket = ref({});
  const isLoadingPatients = ref(false);
  const apiPatientsError = ref(null);
  const quotaUsed = ref(5);
  const currentProcessingPatient = ref({});
  
  const lastUpdated = ref(Date.now());
  const lastFetchTime = ref({});
  const lastGlobalFetchTime = ref(0); // Cooldown for bulk refreshes

  // Scoped Refresh Logic: track which lokets are currently being viewed
  const activeLoketInterest = ref({}); // { [loketId]: count }
  const activeClinicInterest = ref({}); // { [kodeKlinik]: count }
  const globalInterestCount = ref(0); // Tracks pages that need ALL loket data (e.g. CheckInPasien)
  
  const registerInterest = (loketId) => {
    if (!loketId) return;
    const id = String(loketId);
    activeLoketInterest.value[id] = (activeLoketInterest.value[id] || 0) + 1;
    console.log(`🔌 [queueStore] Registered interest in Loket ${id}. Active:`, activeLoketInterest.value);
  };

  const unregisterInterest = (loketId) => {
    if (!loketId) return;
    const id = String(loketId);
    if (activeLoketInterest.value[id]) {
      activeLoketInterest.value[id] = Math.max(0, activeLoketInterest.value[id] - 1);
      if (activeLoketInterest.value[id] === 0) {
        delete activeLoketInterest.value[id];
      }
    }
    console.log(`🔌 [queueStore] Unregistered interest in Loket ${id}. Active lokets:`, activeLoketInterest.value);
  };

  const registerClinicInterest = (kodeKlinik) => {
    if (!kodeKlinik) return;
    const code = String(kodeKlinik);
    activeClinicInterest.value[code] = (activeClinicInterest.value[code] || 0) + 1;
    console.log(`🔌 [queueStore] Registered interest in Clinic ${code}. Active clinics:`, activeClinicInterest.value);
  };

  const unregisterClinicInterest = (kodeKlinik) => {
    if (!kodeKlinik) return;
    const code = String(kodeKlinik);
    if (activeClinicInterest.value[code]) {
      activeClinicInterest.value[code] = Math.max(0, activeClinicInterest.value[code] - 1);
      if (activeClinicInterest.value[code] === 0) {
        delete activeClinicInterest.value[code];
      }
    }
    console.log(`🔌 [queueStore] Unregistered interest in Clinic ${code}. Active clinics:`, activeClinicInterest.value);
  };

  const registerGlobalInterest = () => {
    globalInterestCount.value++;
    console.log(`🌐 [queueStore] Global interest registered. Total: ${globalInterestCount.value}`);
  };

  const unregisterGlobalInterest = () => {
    globalInterestCount.value = Math.max(0, globalInterestCount.value - 1);
    console.log(`🌐 [queueStore] Global interest unregistered. Total: ${globalInterestCount.value}`);
  };

  const fetchPatientsForClinic = async (kodeKlinik) => {
    if (!kodeKlinik) return { success: false, message: 'Kode Klinik diperlukan' };

    isLoadingPatients.value = true;
    apiPatientsError.value = null;

    // THROTTLE: Check if we recently fetched (within last 5 seconds)
    const now = Date.now();
    const lastFetch = lastFetchTime.value[`clinic-${kodeKlinik}`] || 0;
    const timeSinceLastFetch = now - lastFetch;
    
    if (timeSinceLastFetch < 5000) {
      console.log(`⏭️ [queueStore] Skipping fetch for clinic ${kodeKlinik} (last fetched ${Math.round(timeSinceLastFetch/1000)}s ago)`);
      isLoadingPatients.value = false;
      return { success: true, message: 'Using cache' };
    }
    
    lastFetchTime.value[`clinic-${kodeKlinik}`] = now;

    try {
      // Find clinic ID from clinicStore
      const clinic = clinicStore.clinics.find(c => c.kode === kodeKlinik);
      if (!clinic || !clinic.id) {
        throw new Error(`Klinik ID tidak ditemukan untuk kode: ${kodeKlinik}`);
      }

      const url = `/visit-api/visit?klinik_id=${clinic.id}&limit=500`;
      console.log(`🔄 [queueStore] Fetching patients for clinic ${kodeKlinik} (ID: ${clinic.id})...`);
      
      const response = await fetch(url);
      if (!response.ok) throw new Error(`HTTP error! status: ${response.status}`);

      const rawResponse = await response.json();
      const data = rawResponse?.data || [];
      
      const mappedClinicPatients = [];
      data.forEach((visit, index) => {
        const healthcareServices = visit.healthcare_services || [];
        
        // Filter for KLINIK healthcare_type_name with active=true
        const services = healthcareServices.filter(s => 
          s.healthcare_type_name === 'KLINIK' && s.active === true
        );
        
        if (services.length > 0) {
          services.forEach(service => {
            const roomId = service.fk_ms_sub_healthcare_service_id;
            const visitStatuses = visit.visit_statuses || [];
            const latestStatus = visitStatuses[visitStatuses.length - 1];
            
            let patientStatus = 'di-loket';
            
            // Map status from database ID if available
            if (latestStatus?.fk_ref_visit_status_id) {
              const statusId = String(latestStatus.fk_ref_visit_status_id);
              if (PATIENT_STATUS_MAP[statusId]) {
                patientStatus = PATIENT_STATUS_MAP[statusId];
              }
            }
            
            // Fallback to description search
            if (patientStatus === 'di-loket' && (latestStatus?.visit_status_description || latestStatus?.desc)) {
              const desc = (latestStatus.visit_status_description || latestStatus.desc).toLowerCase();
              if (desc.includes('pemeriksaan') || desc.includes('sedang diproses')) {
                patientStatus = 'pemeriksaan';
              }
            }
            
            const p = {
              no: visit.id || (20000 + index),
              barcode: visit.visit_code || service.ticket || '',
              noAntrian: service.ticket || visit.visit_code || '',
              jamPanggil: service.check_in_datetime || visit.registration_datetime || '',
              klinik: service.healthcare_service_name || clinic.name,
              kodeKlinik: kodeKlinik,
              klinikId: clinic.id,
              healthcareServiceId: service.id, // Store the healthcare service ID
              ruang: service.sub_healthcare_service_name || '',
              nomorRuang: roomId ? String(roomId) : String(clinic.id),
              pembayaran: service.payment_type_name || visit.payment_type_name || '',
              status: patientStatus,
              processStage: 'klinik-ruang',
              createdAt: visit.registration_datetime || new Date().toISOString(),
              visitType: visit.visit_type_name || 'ONSITE',
              noRM: visit.norm || '',
              fastTrack: (service.ticket && String(service.ticket).startsWith('F-')) ? "YA" : "TIDAK",
              registrationType: 'api',
              visitId: visit.visit_id || visit.id,
              visitCode: visit.visit_code,
              referencePatient: healthcareServices.find(s => {
                const type = (s.healthcare_type_name || '').toUpperCase();
                const code = (s.healthcare_service_code || s.healtcare_service_code || '').toUpperCase();
                return type.includes('LOKET') || type.includes('PENDAFTARAN') || type.includes('REGISTRATION') || code === 'RN';
              })?.ticket || ''
            };
            
            if (isTodayPatient(p)) mappedClinicPatients.push(p);
          });
        }
      });

      // MERGE LOGIC
      const newPatientMap = new Map();
      mappedClinicPatients.forEach(p => {
        const key = p.visitId ? `vid-${p.visitId}` : (p.barcode ? `bc-${p.barcode}` : `no-${p.no}`);
        newPatientMap.set(key, p);
      });

      // CRITICAL FIX: Collect currently processing patient numbers to protect them
      const processingPatientNos = new Set(
        Object.values(currentProcessingPatient.value || {})
          .filter(p => p)
          .map(p => p.no)
      );

      // Update existing allPatients while preserving local UI state
      allPatients.value = allPatients.value.map(p => {
        if (p.processStage !== 'klinik-ruang' || p.kodeKlinik !== kodeKlinik) return p;
        
        // CRITICAL FIX: Protect currently processing patients from being removed
        if (processingPatientNos.has(p.no)) {
          // console.log(`🛡️ [queueStore] Protecting currently processing patient ${p.noAntrian} from clinic refresh`);
          const key = p.visitId ? `vid-${p.visitId}` : (p.barcode ? `bc-${p.barcode}` : `no-${p.no}`);
          // Remove from newPatientMap to prevent duplicate
          if (newPatientMap.has(key)) {
            newPatientMap.delete(key);
          }
          return p; // Keep the currently processing patient as-is
        }
        
        const key = p.visitId ? `vid-${p.visitId}` : (p.barcode ? `bc-${p.barcode}` : `no-${p.no}`);
        const apiP = newPatientMap.get(key);
                if (apiP) {
            newPatientMap.delete(key); // Mark as handled
            
            // Preserve fastTrack and F- prefix if existing patient has it
            const isFastTrack = (p.fastTrack === "YA" || (p.noAntrian && p.noAntrian.startsWith('F-')));
            let finalNoAntrian = apiP.noAntrian;
            if (isFastTrack && !finalNoAntrian.startsWith('F-')) {
              finalNoAntrian = `F-${finalNoAntrian}`;
            }

            const mergedP = {
              ...apiP,
              fastTrack: isFastTrack ? "YA" : apiP.fastTrack,
              noAntrian: finalNoAntrian
            };

            // If we have local updates, preserve them
            const hasLocalUpdates = (
              p.status !== 'di-loket' || 
              p.calledPemeriksaanAwal || 
              p.calledTindakan || 
              p.lastCalledAt
            );
            
            if (hasLocalUpdates) {
              return {
                ...mergedP,
                status: p.status,
                calledPemeriksaanAwal: p.calledPemeriksaanAwal,
                calledTindakan: p.calledTindakan,
                tipeLayanan: p.tipeLayanan,
                lastCalledAt: p.lastCalledAt,
                lastCalledTipeLayanan: p.lastCalledTipeLayanan
              };
            } else {
              return mergedP;
            }
          }
        
        // If it was an API patient that disappeared from the list, it's either stale or from another room
        if (p.registrationType === 'api') return null;
        
        return p;
      }).filter(Boolean);

      // Add remaining new patients
      allPatients.value.push(...newPatientMap.values());

      // console.log(`✅ [queueStore] Successfully fetched ${mappedClinicPatients.length} patients for clinic ${kodeKlinik}`);
      return { success: true, message: `${mappedClinicPatients.length} pasien dimuat` };

    } catch (error) {
      console.error(`❌ [queueStore] Error fetching clinic patients (${kodeKlinik}):`, error);
      apiPatientsError.value = error.message;
      return { success: false, message: error.message };
    } finally {
      isLoadingPatients.value = false;
    }
  };

  // ============================================
  // WEBSOCKET INTEGRATION (CENTRALIZED)
  // ============================================
  // ============================================
  // WEBSOCKET INTEGRATION (CENTRALIZED)
  // ============================================
  const isWsConnected = ref(false);
  const wsClientId = ref(`client-${Math.random().toString(36).substring(7)}`);
  
  const onWsMessage = (data) => {
    const messageData = data?.data || data;
    const targetLoketId = messageData?.loketId || messageData?.idloket;
    const targetKlinikId = messageData?.klinikId || messageData?.idklinik;
    const isTrigger = messageData?.triggerRefresh || targetLoketId || targetKlinikId;

    if (!isTrigger) {
      // console.log('📨 [queueStore] Global WS Message received (No trigger, skipping refresh)');
      return;
    }

    console.log('📨 [queueStore] Global WS Trigger Message received:', data);
    
    // TRIGGER STRATEGIC REFRESHES
    let refreshedSomething = false;

    if (targetLoketId) {
      // 1. If message specifies a loket, refresh that one specifically
      console.log(`🎯 [queueStore] WS targeting Loket ${targetLoketId}: Refreshing...`);
      fetchPatientsForLoket(targetLoketId);
      refreshedSomething = true;
    } 
    
    if (targetKlinikId) {
      // 2. If message specifies a klinik, refresh those clinics
      // Check if it matches any of our active interests or if it's a general trigger
      const interestingClinics = Object.keys(activeClinicInterest.value);
      if (interestingClinics.includes(String(targetKlinikId)) || targetKlinikId === 'broadcast') {
        console.log(`🎯 [queueStore] WS targeting Clinic ${targetKlinikId}: Refreshing...`);
        fetchPatientsForClinic(targetKlinikId === 'broadcast' ? interestingClinics[0] : targetKlinikId);
        refreshedSomething = true;
      }
    } 
    
    // 3. If we have global interest (e.g. Check-in page open), always refresh everything on ANY trigger
    if (globalInterestCount.value > 0) {
      console.log(`📡 [queueStore] WS trigger: Global Interest active, triggering staggered bulk refresh...`);
      fetchAllPatients();
      refreshedSomething = true;
    } 
    
    // 4. Fallback: If nothing specific was refreshed but we have generic interest, refresh all active things
    if (!refreshedSomething) {
      const interestingLokets = Object.keys(activeLoketInterest.value);
      const interestingClinics = Object.keys(activeClinicInterest.value);
      
      if (interestingLokets.length > 0) {
        console.log(`🌐 [queueStore] WS trigger: Refreshing ${interestingLokets.length} active lokets:`, interestingLokets);
        interestingLokets.forEach(loketId => {
          fetchPatientsForLoket(loketId);
        });
        refreshedSomething = true;
      }
      
      if (interestingClinics.length > 0) {
        console.log(`🌐 [queueStore] WS trigger: Refreshing ${interestingClinics.length} active clinics:`, interestingClinics);
        interestingClinics.forEach(kodeKlinik => {
          fetchPatientsForClinic(kodeKlinik);
        });
        refreshedSomething = true;
      }
    }

    if (!refreshedSomething) {
      console.log(`🔕 [queueStore] WS trigger received but no active interest matched. Skipping.`);
    }
  };

  const config = useRuntimeConfig();
  const wsBaseUrl = config.public?.wsBaseUrl || "ws://10.10.123.135:8084/api/v1/ws";

  const { connect, disconnect, sendViaPost, isConnected } = useWebSocket({
    url: wsBaseUrl,
    clientId: wsClientId,
    onOpen: () => {
      console.log('✅ [queueStore] WebSocket connected');
      isWsConnected.value = true;
    },
    onClose: () => {
      console.log('❌ [queueStore] WebSocket disconnected');
      isWsConnected.value = false;
    },
    onError: (err) => {
      console.error('⚠️ [queueStore] WebSocket error:', err);
      isWsConnected.value = false;
    },
    onMessage: onWsMessage
  });

  /**
   * Initialize Global WebSocket
   */
  const initWebSocket = (customClientId = null) => {
    if (isConnected.value && customClientId === wsClientId.value) {
      console.log('🔌 [queueStore] WebSocket already connected with same ID.');
      return;
    }

    if (customClientId) {
      wsClientId.value = customClientId;
      // Re-connect with new ID if changed
      disconnect();
      // useWebSocket will use the new wsClientId.value if it's reactive
    }

    console.log(`🔌 [queueStore] Connecting to WebSocket: ${wsBaseUrl} as ${wsClientId.value}`);
    connect();
  };

  /**
   * Disconnect Global WebSocket
   */
  const disconnectWebSocket = () => {
    disconnect();
    isWsConnected.value = false;
  };

  /**
   * Sync patient status to apiPatientsPerLoket for reactivity
   */
  const syncApiPatientStatus = (patient, newStatus) => {
    if (!patient) return;
    
    // Allow syncing if strictly 'api' OR if we can find a matching ID in the API store
    // This handles cases where we have an 'onsite' patient locally that corresponds to an API patient
    
    const loketId = patient.loketId;
    if (loketId && apiPatientsPerLoket.value[loketId]) {
      // Try to find patient in the API list
      const index = apiPatientsPerLoket.value[loketId].findIndex(p => {
        if (patient.registrationType === 'api' && p.no === patient.no) return true;
        
        // Fallback matching for 'onsite' patients
        if (patient.idtiket && p.idtiket && String(patient.idtiket) === String(p.idtiket)) return true;
        if (patient.barcode && p.barcode && String(patient.barcode) === String(p.barcode)) return true;
        
        return false;
      });

      if (index !== -1) {
        const updated = [...apiPatientsPerLoket.value[loketId]];
        updated[index] = { ...updated[index], status: newStatus };
        apiPatientsPerLoket.value[loketId] = updated;
      }
    }
  };

  /**
   * Reference mapping for patient status from API idvisit
   * idvisit 1, 2 = menunggu (CT ANJUNGAN, RT ANJUNGAN)
   * idvisit 3, 4 = anjungan (PG ANJUNGAN, RT CHECK-IN)
   * idvisit 5, 6 = di-loket (PS CHECK-IN, TP LOKET)
   */
  const PATIENT_STATUS_MAP = {
    // Stage: ANJUNGAN (1-5, 25-27)
    1: 'menunggu',   // CT ANJUNGAN
    2: 'menunggu',   // RT ANJUNGAN
    3: 'anjungan',   // PG ANJUNGAN
    4: 'anjungan',   // RT CHECK-IN
    5: 'di-loket',   // PS CHECK-IN
    25: 'menunggu',  // BT ANJUNGAN
    26: 'pending',   // PE ANJUNGAN
    27: 'terlambat', // TE ANJUNGAN

    // Stage: LOKET (6-9, 28-29)
    6: 'di-loket',   // RT PENDAFTARAN
    7: 'anjungan',   // PG PENDAFTARAN
    8: 'di-loket',   // PO PENDAFTARAN
    9: 'di-loket',   // PS PENDAFTARAN
    28: 'pending',   // PE PENDAFTARAN
    29: 'terlambat', // TR PENDAFTARAN

    // Stage: VERIFIKASI (10-13, 30-31)
    10: 'di-loket',
    11: 'anjungan',
    12: 'di-loket',
    13: 'di-loket',
    30: 'pending',
    31: 'terlambat',

    // Stage: PELAYANAN (14-19, 32-33)
    14: 'pemeriksaan', // CT PEMERIKSAAN
    15: 'pemeriksaan', // RT PEMERIKSAAN
    16: 'pemeriksaan', // PG PEMERIKSAAN AWAL
    17: 'pemeriksaan', // PG PEMERIKSAAN
    18: 'pemeriksaan', // PO PEMERIKSAAN
    19: 'pemeriksaan', // PS PEMERIKSAAN
    32: 'pending',     // PE PEMERIKSAAN
    33: 'terlambat',   // TR PEMERIKSAAN
    
    // String versions for robustness
    "1": 'menunggu', "2": 'menunggu', "3": 'anjungan', "4": 'anjungan', "5": 'di-loket',
    "6": 'di-loket', "14": 'pemeriksaan', "15": 'pemeriksaan', "28": 'pending', "29": 'terlambat'
  };

  /**
   * Map status from idvisit API
   */
  const mapStatusFromIdVisit = (idvisit) => {
    return PATIENT_STATUS_MAP[idvisit] || 'menunggu';
  };

  /**
   * Map status dari deskripsi API ke status internal
   * menunggu (id 1, 2): "Cetak Tiket Antrian" atau "Ruang Tunggu Anjungan"
   * anjungan (id 3, 4): "Panggilan loket ke Anjungan" atau "Tunggu Pasien Check-In"
   * di-loket (id 5, 6): "Pasien Sudah Check-In" atau "Tunggu Panggilan Loket"
   */
  const mapStatusFromDeskripsi = (deskripsi) => {
    if (!deskripsi) return 'menunggu';
    
    const desc = deskripsi.toString().trim().toUpperCase();
    
    // di-loket (id 5, 6): PS CHECK-IN (Pasien Sudah Check-In), RT PENDAFTARAN (Tunggu Panggilan Loket)
    if (desc.includes('PASIEN SUDAH CHECK-IN') || desc.includes('TUNGGU PANGGILAN LOKET') || desc.includes('PS CHECK-IN') || desc.includes('RT PENDAFTARAN')) {
      return 'di-loket';
    }
    
    // anjungan (id 3, 4): PG ANJUNGAN (Panggilan loket ke Anjungan), RT CHECK-IN (Tunggu Pasien Check-In)
    if (desc.includes('PANGGILAN LOKET KE ANJUNGAN') || desc.includes('TUNGGU PASIEN CHECK-IN') || desc.includes('PG ANJUNGAN') || desc.includes('RT CHECK-IN')) {
      return 'anjungan';
    }
    
    // menunggu (id 1, 2): CT ANJUNGAN (Cetak Tiket Antrian), RT ANJUNGAN (Ruang Tunggu Anjungan)
    if (desc.includes('CETAK TIKET') || desc.includes('RUANG TUNGGU ANJUNGAN') || desc.includes('CT ANJUNGAN') || desc.includes('RT ANJUNGAN')) {
      return 'menunggu';
    }
    
    // Default: menunggu
    return 'menunggu';
  };



  /**
   * Map patient data dari API format ke store format
   */
  const mapApiPatientToStoreFormat = (apiPatient, index) => {
    // Use idtiket as unique patient number to avoid conflicts with seed data
    // Seed data uses sequential numbers 1-20, so we use idtiket + 10000 offset
    const uniqueNo = apiPatient.idtiket ? parseInt(apiPatient.idtiket) + 10000 : 10000 + index;
    
    // Handle nested positions to find the best status
    let status = 'menunggu';
    let posisiStr = apiPatient.posisi || '';
    let idvisit = apiPatient.idvisit;
    
    if (apiPatient.posisi && Array.isArray(apiPatient.posisi) && apiPatient.posisi.length > 0) {
      // Find the "best" status among all positions
      // Priority: di-loket > anjungan > menunggu > pending > terlambat
      const statusPriority = { 'di-loket': 5, 'anjungan': 4, 'menunggu': 3, 'pending': 2, 'terlambat': 1 };
      let bestPriority = 0;
      
      apiPatient.posisi.forEach(pos => {
        const pStatus = pos.idvisit 
          ? mapStatusFromIdVisit(pos.idvisit) 
          : mapStatusFromDeskripsi(pos.deskripsi);
          
        const pPrio = statusPriority[pStatus] || 0;
        if (pPrio > bestPriority) {
          bestPriority = pPrio;
          status = pStatus;
          posisiStr = pos.posisi || pos.deskripsi || '';
          idvisit = pos.idvisit || idvisit;
        }
      });
    } else {
      // Standard top-level status
      status = apiPatient.idvisit 
        ? mapStatusFromIdVisit(apiPatient.idvisit) 
        : mapStatusFromDeskripsi(apiPatient.deskripsi);
    }

    const mapped = {
      no: uniqueNo,
      jamPanggil: apiPatient.waktu || '',
      barcode: apiPatient.barcode || '',
      noAntrian: `${apiPatient.ticket || ''} | ${posisiStr}`,
      shift: apiPatient.shift ? `Shift ${apiPatient.shift}` : '',
      klinik: apiPatient.klinik || '',
      kodeKlinik: apiPatient.klinik || '', // Will be mapped later
      fastTrack: (apiPatient.ticket && String(apiPatient.ticket).startsWith('F-')) ? "YA" : "TIDAK", 
      pembayaran: apiPatient.pembayaran || '',
      status: status,
      processStage: 'loket',
      createdAt: apiPatient.tanggal || new Date().toISOString(),
      registrationType: 'api',
      visitType: 'Onsite',
      visitDate: apiPatient.tanggal ? apiPatient.tanggal.split('T')[0] : new Date().toISOString().substring(0, 10),
      namaDokter: null,
      noRM: null,
      penanggungJawab: null,
      alasanFastTrack: null,
      idvisit: idvisit,
      idtiket: apiPatient.idtiket,
      ticket: apiPatient.ticket,
      posisi: apiPatient.posisi,
      deskripsi: apiPatient.deskripsi,
      visitCode: apiPatient.visit_code || apiPatient.ticket,
      referencePatient: (apiPatient.healthcare_services && Array.isArray(apiPatient.healthcare_services))
        ? apiPatient.healthcare_services.find(s => {
            const type = (s.healthcare_type_name || '').toUpperCase();
            const code = (s.healthcare_service_code || s.healtcare_service_code || '').toUpperCase();
            return type.includes('LOKET') || type.includes('PENDAFTARAN') || type.includes('REGISTRATION') || code === 'RN';
          })?.ticket || ''
        : ''
    };

    // Map klinik name to code using clinicStore
    const clinic = clinicStore.clinics.find(c => 
      c.name && apiPatient.klinik && 
      (c.name.toUpperCase().includes(apiPatient.klinik.toUpperCase()) || 
       apiPatient.klinik.toUpperCase().includes(c.name.toUpperCase()))
    );
    if (clinic) {
      mapped.kodeKlinik = clinic.kode;
    } else {
      // Fallback: Use raw clinic ID if it looks like a code (e.g. numeric ID from API)
      mapped.kodeKlinik = apiPatient.idklinik || apiPatient.klinik || '';
    }

    return mapped;
  };

  /**
   * Fetch patient data untuk loket tertentu dari API
   */
  const fetchPatientsForLoket = async (loketId) => {
    if (!loketId) {
      console.error('loketId required for fetchPatientsForLoket');
      return { success: false, message: 'ID Loket diperlukan' };
    }

    isLoadingPatients.value = true;
    apiPatientsError.value = null;

    // THROTTLE: Check if we recently fetched (within last 5 seconds)
    const now = Date.now();
    const lastFetch = lastFetchTime.value[loketId] || 0;
    const timeSinceLastFetch = now - lastFetch;
    
    if (timeSinceLastFetch < 5000 && apiPatientsPerLoket.value[loketId]) {
      console.log(`⏭️ [queueStore] Skipping fetch for loket ${loketId} (last fetched ${Math.round(timeSinceLastFetch/1000)}s ago)`);
      isLoadingPatients.value = false;
      return {
        success: true,
        message: 'Using cached data',
        data: apiPatientsPerLoket.value[loketId]
      };
    }
    
    // Update last fetch time
    lastFetchTime.value[loketId] = now;

    // Check for daily reset before fetching
    checkAndResetDaily();

    try {
      console.log(`🔄 [queueStore] Fetching patients for loket ${loketId}...`);
      const response = await fetch(`http://10.10.123.140:8089/api/v1/loket/${loketId}`);
      
      if (!response.ok) {
        throw new Error(`HTTP error! status: ${response.status}`);
      }

      const rawData = await response.json();
      
      // Check response structure
      if (rawData.metadata && rawData.metadata.code !== 200) {
        throw new Error(rawData.message || 'API returned error status');
      }

      const patientsRaw = rawData.data || [];
      
      // Map API data to store format
      const mappedPatients = patientsRaw.map((apiPatient, index) => 
        mapApiPatientToStoreFormat(apiPatient, index)
      ).filter(p => isTodayPatient(p));

      // Deduplicate patients by idtiket
      // API returns multiple entries if patient is at multiple positions
      const deduplicatedMap = new Map();
      mappedPatients.forEach(p => {
        const id = p.idtiket || p.barcode || p.no;
        const existing = deduplicatedMap.get(id);
        
        if (!existing) {
          deduplicatedMap.set(id, p);
        } else {
          // Priority: di-loket > anjungan > menunggu > pending > terlambat
          const statusPriority = { 'di-loket': 5, 'anjungan': 4, 'menunggu': 3, 'pending': 2, 'terlambat': 1 };
          const pPrio = statusPriority[p.status] || 0;
          const ePrio = statusPriority[existing.status] || 0;
          
          if (pPrio > ePrio) {
            deduplicatedMap.set(id, p);
          }
        }
      });
      const finalPatients = Array.from(deduplicatedMap.values());

      // Store in API-specific state
      apiPatientsPerLoket.value[loketId] = finalPatients;

      // IMPORTANT: Also merge into allPatients so callMultiplePatients can find them
      
      // Prepare list of new API patients with loketId assigned
      const patientsWithLoketId = finalPatients.map(p => ({
        ...p,
        loketId: parseInt(loketId),
        registrationType: 'api'
      }));

      // ROBUST DEDUPLICATION & STATUS MERGE
      // 1. Identify matches between existing allPatients and new API patients
      const newPatientMap = new Map();
      patientsWithLoketId.forEach(p => {
        const key = p.idtiket ? `id-${p.idtiket}` : `bc-${p.barcode}`;
        newPatientMap.set(key, p);
      });

      // 2. Iterate existing patients to find matches and preserve status
      const preservedStatuses = new Map();
      allPatients.value.forEach(p => {
        const key = p.idtiket ? `id-${p.idtiket}` : `bc-${p.barcode}`;
        if (newPatientMap.has(key)) {
           // If existing patient has more advanced status (anjungan/di-loket), preserve it!
           // This handles cases where we updated status locally ('onsite' or 'api') but API is lagging
           const statusPriority = { 'di-loket': 5, 'anjungan': 4, 'menunggu': 3, 'pending': 2, 'terlambat': 1 };
           const existingPrio = statusPriority[p.status] || 0;
           const newPatient = newPatientMap.get(key);
           const newPrio = statusPriority[newPatient.status] || 0;
           
           if (existingPrio > newPrio) {
             preservedStatuses.set(key, p.status);
             // Also preserve lastCalledAt/calledByAdmin if available
             if (p.lastCalledAt) newPatient.lastCalledAt = p.lastCalledAt;
             if (p.calledByAdmin) newPatient.calledByAdmin = p.calledByAdmin;
           }
        }
      });

      // 3. Update new patients with preserved status and fasttrack
      patientsWithLoketId.forEach(p => {
        const key = p.idtiket ? `id-${p.idtiket}` : `bc-${p.barcode}`;
        if (preservedStatuses.has(key)) {
          p.status = preservedStatuses.get(key);
        }
      });

      // 4. Remove existing patients that collide with new ones (to be replaced)
      // This ensures API data ALWAYS takes precedence over local/seed data
      // Remove ANY patient (local, seed, or api) with matching barcode/idtiket
      allPatients.value = allPatients.value.filter(p => {
        const key = p.idtiket ? `id-${p.idtiket}` : `bc-${p.barcode}`;
        
        // CRITICAL FIX: If patient has already moved to a later stage (e.g. klinik-ruang),
        // do NOT remove it or overwrite it with a 'loket' stage API patient.
        if (p.processStage && p.processStage !== 'loket') {
          // Check if this advanced patient matches the incoming batch
          if (newPatientMap.has(key)) {
            // Signal to skip adding the 'loket' version from API
            newPatientMap.delete(key); 
          }
          return true; // Keep the advanced patient
        }

        // CRITICAL FIX: Protect currently processing patients from being removed
        // Check if this patient is currently being processed in any admin interface
        const isCurrentlyProcessing = Object.values(currentProcessingPatient.value || {}).some(
          processingPatient => processingPatient && processingPatient.no === p.no
        );
        if (isCurrentlyProcessing) {
          console.log(`🛡️ [queueStore] Protecting currently processing patient ${p.noAntrian} from WebSocket refresh`);
          // Remove from newPatientMap to prevent duplicate
          if (newPatientMap.has(key)) {
            newPatientMap.delete(key);
          }
          return true; // Keep the currently processing patient
        }

        // If it's still in loket stage but marked as processed, and it's in the API batch,
        // we might be experiencing API lag. Keep it as processed.
        if (p.status === 'processed' && newPatientMap.has(key)) {
           // Skip replacing it with 'menunggu'/'anjungan' status
           newPatientMap.delete(key);
           return true;
        }
        
        // Remove if it's a stale 'api' patient for this loket
        if (p.registrationType === 'api' && String(p.loketId) === String(loketId)) {
           // But only if it's NOT in the new batch (stale)
           if (!newPatientMap.has(key)) return false;
        }
        
        return true;
      });
      
      
      // 5. Add merged patients (with deduplication check)
      // Don't add if patient already exists in allPatients (prevents duplication with LocalStorage fallback)
      patientsWithLoketId.forEach(newPatient => {
        const existingIndex = allPatients.value.findIndex(p => 
          (p.barcode && newPatient.barcode && p.barcode === newPatient.barcode) ||
          (p.idtiket && newPatient.idtiket && p.idtiket === newPatient.idtiket)
        );
        
        if (existingIndex === -1) {
          // Patient doesn't exist, add it
          allPatients.value.push(newPatient);
        } else {
          // Patient exists - only update if new status has higher priority
          const existing = allPatients.value[existingIndex];
          const statusPriority = { 'di-loket': 5, 'anjungan': 4, 'menunggu': 3, 'pending': 2, 'terlambat': 1 };
          const existingPrio = statusPriority[existing.status] || 0;
          const newPrio = statusPriority[newPatient.status] || 0;
          
          // Only update if new status has higher or equal priority
          // This prevents API data from overwriting LocalStorage terlambat/pending status
          if (newPrio >= existingPrio) {
            console.log(`🔄 [queueStore] Updating patient ${newPatient.barcode} from ${existing.status} to ${newPatient.status}`);
            
            // Preserve fastTrack and F- prefix if existing patient has it
            const isFastTrack = (existing.fastTrack === "YA" || (existing.noAntrian && existing.noAntrian.startsWith('F-')));
            let finalNoAntrian = newPatient.noAntrian;
            if (isFastTrack && !finalNoAntrian.startsWith('F-')) {
              finalNoAntrian = `F-${finalNoAntrian}`;
            }

            allPatients.value[existingIndex] = {
              ...newPatient,
              // Preserve some local state
              calledByAdmin: existing.calledByAdmin,
              lastCalledAt: existing.lastCalledAt,
              fastTrack: isFastTrack ? "YA" : newPatient.fastTrack,
              noAntrian: finalNoAntrian
            };
          } else {
            console.log(`🛡️ [queueStore] Protecting patient ${existing.barcode} with status ${existing.status} from API overwrite (${newPatient.status})`);
          }
        }
      });

      // 6. RESTORE terlambat/pending status from LocalStorage (hybrid fallback)
      // This ensures status persists even if API doesn't save it
      allPatients.value.forEach((patient, index) => {
        if (patient.barcode) {
          const storageKey = `patient-status-${patient.barcode}`;
          const savedData = localStorage.getItem(storageKey);
          
          if (savedData) {
            try {
              const { status, timestamp } = JSON.parse(savedData);
              
              // Only apply if status is terlambat or pending and not too old (24 hours)
              const isRecent = Date.now() - timestamp < 24 * 60 * 60 * 1000;
              
              if ((status === 'terlambat' || status === 'pending') && isRecent) {
                console.log(`🔄 [LocalStorage] Restoring ${status} status for patient ${patient.barcode}`);
                allPatients.value[index] = {
                  ...patient,
                  status: status
                };
              } else if (!isRecent) {
                // Clean up old data
                localStorage.removeItem(storageKey);
              }
            } catch (e) {
              console.error('Error parsing LocalStorage data:', e);
              localStorage.removeItem(storageKey);
            }
          }
        }
      });

      // console.log(`✅ [queueStore] Successfully fetched ${finalPatients.length} unique patients for loket ${loketId} (Original: ${mappedPatients.length})`);
      // console.log(`📊 [queueStore] Total patients in allPatients: ${allPatients.value.length}`);

      return {
        success: true,
        message: `${mappedPatients.length} pasien berhasil dimuat`,
        data: mappedPatients
      };

    } catch (error) {
      console.error(`❌ [queueStore] Error fetching patients for loket ${loketId}:`, error);
      apiPatientsError.value = error.message;

      // Return empty array on error
      apiPatientsPerLoket.value[loketId] = [];

      return {
        success: false,
        message: `Gagal memuat data pasien: ${error.message}`,
        data: []
      };
    } finally {
      isLoadingPatients.value = false;
    }
  };


  /**
   * Global fetcher for all patients across all available lokets
   */
  /**
   * Global fetcher for all patients across all available lokets
   * Uses staggered fetching to prevent 429 Too Many Requests errors.
   */
  const fetchAllPatients = async (force = false) => {
    // 1. Cooldown Check: Prevent global refresh spam (max once every 5 seconds)
    const now = Date.now();
    if (!force && now - lastGlobalFetchTime.value < 5000) {
      console.log(`⏭️ [queueStore] fetchAllPatients: Global refresh on cooldown (${Math.round((now - lastGlobalFetchTime.value)/1000)}s ago)`);
      return;
    }
    lastGlobalFetchTime.value = now;

    console.log('🔄 [queueStore] Fetching all patients for all lokets (Staggered)...');
    const allLokets = loketStore.lokets || [];
    if (allLokets.length === 0) {
      console.warn('⚠️ [queueStore] No lokets available for fetchAllPatients');
      return;
    }

    // 2. Staggered Fetch: Instead of Promise.all, we fetch one by one with a small delay
    // This spreads the load and stays under the 429 threshold
    for (const loket of allLokets) {
      const id = loket.id || loket.no;
      if (id) {
        // We use await to wait for the fetch to finish, then wait a bit more
        await fetchPatientsForLoket(id);
        
        // Wait 150ms between requests (not too long, but enough to breathe)
        await new Promise(resolve => setTimeout(resolve, 150));
      }
    }
    
    console.log(`✅ [queueStore] Staggered bulk fetch completed. Total: ${allPatients.value.length} patients in memory.`);
  };

  /**
   * Get patients for a specific loket (from API or seed data based on loket type)
   */
  const getPatientsForLoket = (loketId) => {
    return computed(() => {
      const loket = loketStore.getLoketById(parseInt(loketId));
      
      // If loket is EKSEKUTIF, use seed data
      const isEksekutif = loket?.tipeloket === 'EKSEKUTIF' || 
                          loket?.tipeLoket === 'EKSEKUTIF' ||
                          (loket?.namaLoket || '').toUpperCase().includes('EKSEKUTIF');
      
      if (isEksekutif) {
        // Return EKSEKUTIF patients from seed data
        return allPatients.value.filter(p => {
          const isPembayaranEksekutif = (p.pembayaran || '').toUpperCase().includes('EKSEKUTIF') || 
                                        (p.pembayaran || '').toUpperCase().includes('VIP');
          return isPembayaranEksekutif && p.processStage === 'loket';
        });
      }
      
      // For REGULER loket, return API data
      return apiPatientsPerLoket.value[loketId] || [];
    });
  };


  // Helper function untuk mendapatkan loket default (Loket A)
  const getDefaultLoket = () => {
    const allLokets = loketStore.loketData?.value || loketStore.loketData || [];
    const loket1 = allLokets.find(l => l.id === 1 || l.no === 1);
    return loket1 ? loket1.namaLoket : 'Loket A';
  };

  // Helper function untuk increment barcode counter setelah barcode digunakan
  const incrementBarcodeCounter = () => {
    if (typeof window === 'undefined' || typeof localStorage === 'undefined') return;
    
    const now = new Date();
    const year = String(now.getFullYear()).slice(-2);
    const month = String(now.getMonth() + 1).padStart(2, '0');
    const day = String(now.getDate()).padStart(2, '0');
    const datePrefix = `${year}${month}${day}`;
    const STORAGE_KEY = `barcode_counter_${datePrefix}`;
    const LAST_DATE_KEY = 'barcode_last_date';
    
    const storedCounter = localStorage.getItem(STORAGE_KEY);
    if (storedCounter) {
      const currentCounter = parseInt(storedCounter, 10) || 1;
      localStorage.setItem(STORAGE_KEY, String(currentCounter + 1));
      localStorage.setItem(LAST_DATE_KEY, datePrefix);
    }
  };

  // Helper function untuk generate barcode dengan format: YYMMDD + 5 digit sequential
  // Format: YY (tahun 2 digit terakhir) + MM (bulan 2 digit) + DD (tanggal 2 digit) + XXXXX (5 digit sequential)
  // Contoh: 26011400001, 26011400002, dst
  // Counter akan reset setiap ganti tanggal (mulai dari 00001 lagi)
  // IMPORTANT: Memastikan barcode selalu UNIQUE dan sequential per tanggal
  // NOTE: allPatientsRef adalah optional parameter untuk menghindari TDZ error saat seed initialization
  const generateBarcode = (existingBarcodes = [], allPatientsRef = null) => {
    const now = new Date();
    const year = String(now.getFullYear()).slice(-2); // 2 digit tahun terakhir
    const month = String(now.getMonth() + 1).padStart(2, '0'); // 2 digit bulan
    const day = String(now.getDate()).padStart(2, '0'); // 2 digit tanggal
    const datePrefix = `${year}${month}${day}`; // YYMMDD
    
    // Check if localStorage is available (browser environment)
    if (typeof window !== 'undefined' && typeof localStorage !== 'undefined') {
      // Key untuk localStorage berdasarkan tanggal
      const STORAGE_KEY = `barcode_counter_${datePrefix}`;
      const LAST_DATE_KEY = 'barcode_last_date';
      
      // Cek apakah tanggal sudah berubah (reset counter)
      const lastDate = localStorage.getItem(LAST_DATE_KEY);
      const currentDate = datePrefix;
      
      let counter = 1; // Default mulai dari 1
      
      if (lastDate === currentDate) {
        // Tanggal sama, lanjutkan counter dari localStorage
        const storedCounter = localStorage.getItem(STORAGE_KEY);
        if (storedCounter) {
          counter = parseInt(storedCounter, 10) || 1;
        }
      } else {
        // Tanggal berbeda, reset counter ke 1
        counter = 1;
        // Hapus counter lama untuk tanggal sebelumnya (cleanup)
        if (lastDate) {
          localStorage.removeItem(`barcode_counter_${lastDate}`);
        }
      }
      
      // Generate barcode dengan counter saat ini
      let barcode = `${datePrefix}${String(counter).padStart(5, '0')}`;
      
      // Cek apakah barcode sudah ada di existingBarcodes atau allPatientsRef
      const checkExists = (b) => {
        if (existingBarcodes.includes(b)) return true;
        // Hanya cek allPatientsRef jika diberikan (tidak null/undefined)
        if (allPatientsRef && allPatientsRef.value && allPatientsRef.value.some(p => p.barcode === b)) return true;
        return false;
      };
      
      let attempts = 0;
      const maxAttempts = 1000; // Maksimal 1000 pasien per hari
      
      // Cek uniqueness dan increment jika duplikat
      while (checkExists(barcode) && attempts < maxAttempts) {
        counter++;
        barcode = `${datePrefix}${String(counter).padStart(5, '0')}`;
        attempts++;
      }
      
      // IMPORTANT: JANGAN tulis counter ke localStorage di sini!
      // Counter hanya di-increment dan di-save setelah barcode benar-benar digunakan
      // Ini mencegah counter naik meskipun barcode tidak digunakan (misalnya saat refresh)
      // Counter akan di-increment oleh incrementBarcodeCounter() setelah pasien dibuat
      // Hanya update LAST_DATE_KEY untuk tracking tanggal
      localStorage.setItem(LAST_DATE_KEY, currentDate);
      
      return barcode;
    } else {
      // Fallback untuk SSR atau environment tanpa localStorage
      // Gunakan counter berdasarkan existing barcodes atau allPatientsRef
      const existingCount = existingBarcodes.filter(b => b && b.startsWith(datePrefix)).length;
      const allCount = allPatientsRef && allPatientsRef.value 
        ? allPatientsRef.value.filter(p => p.barcode && p.barcode.startsWith(datePrefix)).length 
        : 0;
      const counter = Math.max(existingCount, allCount) + 1;
      const counterCode = String(counter).padStart(5, '0');
      return `${datePrefix}${counterCode}`;
    }
  };
  // Seed data for easy reset during dev
  // IMPORTANT: Setiap pasien HARUS memiliki barcode UNIK untuk menghindari konflik
  // Format barcode menggunakan generateBarcode() untuk konsistensi dengan format baru
  // Generate barcode dengan memastikan uniqueness menggunakan existingBarcodes array
  // NOTE: Seed data menggunakan barcode statis untuk menghindari increment counter saat refresh
  // Counter hanya di-increment ketika pasien baru benar-benar dibuat dari Anjungan
  
  // Gunakan barcode statis untuk seed data (tidak memanggil generateBarcode)
  // Format: YYMMDD + 5 digit sequential dimulai dari 00001
  // Ini mencegah counter naik setiap kali halaman di-refresh
  const getSeedBarcode = (index) => {
    if (typeof window !== 'undefined' && typeof localStorage !== 'undefined') {
      const now = new Date();
      const year = String(now.getFullYear()).slice(-2);
      const month = String(now.getMonth() + 1).padStart(2, '0');
      const day = String(now.getDate()).padStart(2, '0');
      const datePrefix = `${year}${month}${day}`;
      
      // Gunakan barcode statis berdasarkan index (1-16)
      // Format: YYMMDD + 5 digit (contoh: 26011500001, 26011500002, dst)
      return `${datePrefix}${String(index).padStart(5, '0')}`;
    }
    // Fallback untuk SSR
    const now = new Date();
    const year = String(now.getFullYear()).slice(-2);
    const month = String(now.getMonth() + 1).padStart(2, '0');
    const day = String(now.getDate()).padStart(2, '0');
    const datePrefix = `${year}${month}${day}`;
    return `${datePrefix}${String(index).padStart(5, '0')}`;
  };
  
  const seedBarcode1 = getSeedBarcode(1);
  const seedBarcode2 = getSeedBarcode(2);
  const seedBarcode3 = getSeedBarcode(3);
  const seedBarcode4 = getSeedBarcode(4);
  const seedBarcode5 = getSeedBarcode(5);
  // Barcode untuk pasien Eksekutif only (REGULER akan dari API)
  const seedBarcodeE1 = getSeedBarcode(1);
  const seedBarcodeE2 = getSeedBarcode(2);
  const seedBarcodeE3 = getSeedBarcode(3);
  const seedBarcodeE4 = getSeedBarcode(4);
  
  // IMPORTANT: Set counter ke nilai yang sesuai dengan jumlah seed data
  // Ini mencegah counter naik tidak terkendali saat seed data di-generate
  // Counter akan di-set ke jumlah seed data (16) + 1 untuk next barcode
  // Hanya set jika counter belum ada atau lebih kecil dari jumlah seed data
  if (typeof window !== 'undefined' && typeof localStorage !== 'undefined') {
    const now = new Date();
    const year = String(now.getFullYear()).slice(-2);
    const month = String(now.getMonth() + 1).padStart(2, '0');
    const day = String(now.getDate()).padStart(2, '0');
    const datePrefix = `${year}${month}${day}`;
    const STORAGE_KEY = `barcode_counter_${datePrefix}`;
    const LAST_DATE_KEY = 'barcode_last_date';
    const storedCounter = localStorage.getItem(STORAGE_KEY);
    const seedDataCount = 4; // Jumlah seed data (EKSEKUTIF only)
    
    // Hanya set counter jika belum ada atau lebih kecil dari jumlah seed data
    // Jangan overwrite counter yang sudah lebih besar (berarti sudah ada pasien baru)
    if (!storedCounter || parseInt(storedCounter, 10) < seedDataCount) {
      localStorage.setItem(STORAGE_KEY, String(seedDataCount + 1));
      localStorage.setItem(LAST_DATE_KEY, datePrefix);
    }
  }
  
  
  // SEED DATA: ONLY EKSEKUTIF PATIENTS
  // All REGULER patients will come from API
  const seedPatients = [
    // {
    //   no: 1,
    //   jamPanggil: "11:20",
    //   barcode: seedBarcodeE1,
    //   noAntrian: `EA001 | Online - ${seedBarcodeE1}`,
    //   shift: "Shift 1",
    //   klinik: "KANDUNGAN",
    //   kodeKlinik: "KD",
    //   fastTrack: "TIDAK",
    //   pembayaran: "Eksekutif",
    //   status: "anjungan",
    //   processStage: "loket",
    //   createdAt: new Date().toISOString(),
    //   registrationType: 'online',
    //   visitType: 'SEKARANG',
    //   visitDate: new Date().toISOString().substring(0, 10),
    //   namaDokter: "Dr. Ahmad Wijaya, Sp.OG",
    //   noRM: "RM-E001",
    //   penanggungJawab: null,
    //   alasanFastTrack: null,
    // },
    // {
    //   no: 2,
    //   jamPanggil: "13:45",
    //   barcode: seedBarcodeE2,
    //   noAntrian: `EA002 | Online - ${seedBarcodeE2}`,
    //   shift: "Shift 1",
    //   klinik: "IPD",
    //   kodeKlinik: "IP",
    //   fastTrack: "TIDAK",
    //   pembayaran: "Eksekutif",
    //   status: "anjungan",
    //   processStage: "loket",
    //   createdAt: new Date().toISOString(),
    //   registrationType: 'online',
    //   visitType: 'SEKARANG',
    //   visitDate: new Date().toISOString().substring(0, 10),
    //   namaDokter: "Dr. Budi Santoso, Sp.PD",
    //   noRM: "RM-E002",
    //   penanggungJawab: null,
    //   alasanFastTrack: null,
    // },
    // {
    //   no: 3,
    //   jamPanggil: "15:10",
    //   barcode: seedBarcodeE3,
    //   noAntrian: `F-EA003 | Online - ${seedBarcodeE3}`,
    //   shift: "Shift 2",
    //   klinik: "SARAF",
    //   kodeKlinik: "SR",
    //   fastTrack: "YA",
    //   pembayaran: "Eksekutif",
    //   status: "anjungan",
    //   processStage: "loket",
    //   createdAt: new Date().toISOString(),
    //   registrationType: 'online',
    //   visitType: 'SEKARANG',
    //   visitDate: new Date().toISOString().substring(0, 10),
    //   namaDokter: "Dr. Citra Dewi, Sp.S",
    //   noRM: "RM-E003",
    //   penanggungJawab: "Dr. Citra Dewi",
    //   alasanFastTrack: "Pasien Eksekutif prioritas",
    // },
    // {
    //   no: 4,
    //   jamPanggil: "16:30",
    //   barcode: seedBarcodeE4,
    //   noAntrian: `EA004 | Online - ${seedBarcodeE4}`,
    //   shift: "Shift 2",
    //   klinik: "THT",
    //   kodeKlinik: "TH",
    //   fastTrack: "TIDAK",
    //   pembayaran: "VIP",
    //   status: "anjungan",
    //   processStage: "loket",
    //   createdAt: new Date().toISOString(),
    //   registrationType: 'online',
    //   visitType: 'SEKARANG',
    //   visitDate: new Date().toISOString().substring(0, 10),
    //   namaDokter: "Dr. Eka Putri, Sp.THT",
    //   noRM: "RM-E004",
    //   penanggungJawab: null,
    //   alasanFastTrack: null,
    // },
  ];



  const cloneSeed = () => seedPatients.map(p => ({ ...p }));

  // Initialize counters from seed data to ensure numbering continues correctly
  // Counter SHARED untuk semua payment group dan semua jenis pasien
  // Calculate max queue number from seeds or existing patients
  const syncCountersWithState = () => {
    if (typeof window === 'undefined') return;
    
    const today = new Date().toISOString().split('T')[0];
    const STORAGE_KEY_BARCODE = `barcode_counter_${today.replace(/-/g, '').substring(2)}`;
    
    // Determine the source of patients (prefer current state, fallback to seeds)
    const sourcePatients = allPatients.value.length > 0 ? allPatients.value : seedPatients;
    
    // Find max barcode counter
    let maxBarcodeCounter = 0;
    sourcePatients.forEach(patient => {
      const barcode = patient.barcode || '';
      if (barcode.length > 5) {
        const counterStr = barcode.substring(barcode.length - 5);
        const counter = parseInt(counterStr, 10);
        if (!isNaN(counter) && counter > maxBarcodeCounter) {
          maxBarcodeCounter = counter;
        }
      }
    });

    // Initialize barcode counter if not exists or smaller
    const existingBarcode = localStorage.getItem(STORAGE_KEY_BARCODE);
    if (!existingBarcode || parseInt(existingBarcode, 10) < maxBarcodeCounter) {
      localStorage.setItem(STORAGE_KEY_BARCODE, maxBarcodeCounter.toString());
    }

    // Existing queue counter logic...
    const counterKeyQueue = `queue_counter_loket_shared_${today}`;
    
    // Calculate max queue number from seeds dynamically
    const maxQueueCounter = sourcePatients.length > 0 
      ? Math.max(...sourcePatients.map(p => p.no)) 
      : 0;

    const existingQueue = localStorage.getItem(counterKeyQueue);
    // Always update if current max in memory is larger than stored
    if (!existingQueue || parseInt(existingQueue) < maxQueueCounter) {
      localStorage.setItem(counterKeyQueue, maxQueueCounter.toString()); 
    }
  };

  // Initial state - START EMPTY to avoid clashing with hydration
  // Placeholder lines for moved state references (already handled at top)

  // Daftar klinik untuk dropdown diambil 1 pintu dari clinicStore (Computed for stability)
  const kliniks = computed(() => clinicStore.clinics || []);

  // Penunjang data - reference dari penunjangStore (Computed for stability)
  const penunjangs = computed(() => penunjangStore.penunjangList || []);

  const RESET_HOUR = 2; // 2 AM reset threshold

  /**
   * Get logical reset threshold date object
   * If current time is before 2 AM, the threshold is yesterday at 2 AM.
   * If current time is after 2 AM, the threshold is today at 2 AM.
   */
  const getResetThreshold = () => {
    const now = new Date();
    const threshold = new Date(now);
    threshold.setHours(RESET_HOUR, 0, 0, 0);
    
    if (now.getHours() < RESET_HOUR) {
      threshold.setDate(threshold.getDate() - 1);
    }
    
    return threshold;
  };

  /**
   * Check if data needs to be reset (passed 2 AM of a new day)
   */
  const checkAndResetDaily = () => {
    const threshold = getResetThreshold();
    const thresholdTS = threshold.getTime();
    
    // Use localStorage to persist last reset time across sessions/tabs
    const lastResetStr = localStorage.getItem('queue_last_reset_time');
    const lastResetTS = lastResetStr ? parseInt(lastResetStr, 10) : 0;
    
    if (lastResetTS < thresholdTS) {
      console.log(`🕒 [queueStore] Daily reset triggered. Threshold: ${threshold.toLocaleString()}`);
      
      // Clear data
      allPatients.value = [];
      apiPatientsPerLoket.value = {};
      currentProcessingPatient.value = {};
      
      // Update reset timestamp
      localStorage.setItem('queue_last_reset_time', thresholdTS.toString());
      
      // Re-sync counters for the new day
      syncCountersWithState();
      
      return true;
    }
    return false;
  };

  /**
   * Filter strictly to only show today's patients (after 2 AM)
   */
  const isTodayPatient = (patient) => {
    if (!patient) return false;
    
    // Status processing overrides filter (always show if currently processing)
    const isProcessing = Object.values(currentProcessingPatient.value).some(p => p && p.no === patient.no);
    if (isProcessing) return true;

    const threshold = getResetThreshold();
    
    // Get YYYY-MM-DD for the logical "today" (based on 2 AM threshold)
    const year = threshold.getFullYear();
    const month = String(threshold.getMonth() + 1).padStart(2, '0');
    const day = String(threshold.getDate()).padStart(2, '0');
    const logicalTodayStr = `${year}-${month}-${day}`;
    
    // 1. If visitDate (YYYY-MM-DD) matches logical today's date, it's a "today" patient
    // This correctly includes API patients with midnight timestamps (00:00:00)
    if (patient.visitDate) {
      if (patient.visitDate === logicalTodayStr) return true;
      if (patient.visitDate < logicalTodayStr) return false;
    }
    
    // 2. Fallback: check createdAt timestamp (critical for seed/manual tickets)
    // Tickets created before today's 2 AM are stale
    const createdAt = patient.createdAt ? new Date(patient.createdAt) : null;
    if (createdAt && createdAt < threshold) {
      return false;
    }
    
    return true;
  };

  /**
   * Ensures initial data exists.
   * Only seeds if the store is empty (not hydrated from storage)
   */
  const ensureInitialData = () => {
    // 1. Check for daily reset first
    checkAndResetDaily();

    if (allPatients.value.length === 0) {
      console.log('🌱 Seeding queueStore with initial data...');
      allPatients.value = cloneSeed();
    }
    // ALWAYS sync counters with state (hydrated or seeded) to ensure next number is correct
    syncCountersWithState();
  };

  // Synchronization Guard: track if we are currently hydrating from another tab
  let isSyncing = false;

  // CROSS-TAB SYNC: Listen for storage events to update store across tabs
  if (typeof window !== 'undefined') {
    window.addEventListener('storage', (event) => {
      if (event.key === 'queue-store-state') {
        if (!event.newValue) return;

        try {
          const newState = JSON.parse(event.newValue);
          
          // BREAK THE LOOP: Only re-hydrate if the incoming data is strictly newer than ours
          // Defensive check: Handle cases where lastUpdated might be undefined or missing
          const incomingVersion = Number(newState?.lastUpdated || 0);
          const currentVersion = Number(lastUpdated.value || 0);
          
          if (incomingVersion === 0 || incomingVersion <= currentVersion) {
            // Already up to date, older, or corrupted data. Skip hydration.
            return; 
          }

          // console.log(`🔄 Syncing state to version: ${incomingVersion}`);
          
          if (newState) {
            isSyncing = true; // Block local watch from updating timestamp
            
            try {
              if (newState.allPatients) allPatients.value = newState.allPatients;
              if (newState.quotaUsed !== undefined) quotaUsed.value = newState.quotaUsed;
              if (newState.currentProcessingPatient) currentProcessingPatient.value = newState.currentProcessingPatient;
              if (newState.apiPatientsPerLoket) apiPatientsPerLoket.value = newState.apiPatientsPerLoket;
              
              // Set local timestamp to match the source
              lastUpdated.value = incomingVersion;
              
              syncCountersWithState();
            } finally {
              // Ensure we release the lock
              setTimeout(() => { isSyncing = false; }, 50);
            }
          }
        } catch (e) {
          console.error('Error hydrating from storage event:', e);
          isSyncing = false;
        }
      }
    });
  }

  // Automatic timestamp update for local changes
  // This ensures lastUpdated changes whenever state changes LOCALLY
  // but skips updates coming from isSyncing = true
  watch([allPatients, quotaUsed, currentProcessingPatient, apiPatientsPerLoket], () => {
    if (!isSyncing) {
      lastUpdated.value = Date.now();
      // console.log('✨ Local state updated, version:', lastUpdated.value);
    }
  }, { deep: true });

  // Computed - Filter berdasarkan process stage dan status
  const getPatientsByStage = (stage) => {
    return computed(() => {
      // Filter by stage AND date (today only)
      // optimization: filter all once, then sub-filter
      const patients = allPatients.value.filter(p => p && p.processStage === stage && isTodayPatient(p));
      
      // Debug log (limited to avoid spam)
      if (patients.length > 0) {
        // console.log(`getPatientsByStage(${stage}):`, patients.length, 'patients (Today)');
      }
      
      return {
        all: patients,
        anjungan: patients.filter(p => p.status === 'anjungan'),
        menunggu: patients.filter(p => p.status === 'menunggu'),
        diLoket: patients.filter(p => p.status === 'di-loket'),
        terlambat: patients.filter(p => p.status === 'terlambat'),
        pending: patients.filter(p => p.status === 'pending'),
      };
    });
  };

  // Total pasien per stage - strictly today
  const getTotalPasienByStage = (stage) => {
    return computed(() => 
      allPatients.value.filter(p => p.processStage === stage && isTodayPatient(p)).length
    );
  };

  const totalPasien = computed(() => allPatients.value.length);

  const resetPatients = () => {
    allPatients.value = cloneSeed();
    quotaUsed.value = 5;
    currentProcessingPatient.value = { loket: null, klinik: null, penunjang: null };
    syncCountersWithState(); // Re-initialize counters after reset
  };

  /**
   * Helper to sort patients for calling/process selection
   * Priority: Fast Track ("YA") first, then by sequence number (no)
   */
  const sortPatientsForCalling = (patients) => {
    if (!patients || !Array.isArray(patients)) return [];
    return [...patients].sort((a, b) => {
      const aFT = (a.fastTrack ?? "").toString().trim().toUpperCase() === "YA";
      const bFT = (b.fastTrack ?? "").toString().trim().toUpperCase() === "YA";
      
      if (aFT && !bFT) return -1;
      if (!aFT && bFT) return 1;
      
      const numA = parseInt(a.no) || 0;
      const numB = parseInt(b.no) || 0;
      return numA - numB;
    });
  };

  /**
   * Check if patient's payment type is compatible with loket's accepted payments
   * Maps various payment names to standardized categories
   * Handles complex names like "UMUM / JKMM / SPM / DLL"
   */
  const isPaymentCompatible = (patientPayment, loketPayments) => {
    if (!loketPayments || loketPayments.length === 0) return true; // No restriction
    if (!patientPayment) return false;
    
    const normalizedPatient = String(patientPayment).toUpperCase().trim();
    
    // Map patient payment to category
    const patientCategory = 
      (normalizedPatient.includes('JKN') || normalizedPatient.includes('BPJS')) ? 'JKN' :
      normalizedPatient.includes('EKSEKUTIF') || normalizedPatient.includes('VIP') ? 'EKSEKUTIF' :
      'UMUM'; // Default to UMUM for non-JKN, non-EKSEKUTIF
    
    // Check if loket accepts this category
    return loketPayments.some(lp => {
      const normalized = String(lp).toUpperCase().trim();
      
      // Handle exact match
      if (normalized === patientCategory) return true;
      
      // Handle partial matches for complex payment types like "UMUM / JKMM / SPM / DLL"
      if (patientCategory === 'UMUM' && normalized.includes('UMUM')) return true;
      if (patientCategory === 'JKN' && (normalized.includes('JKN') || normalized.includes('BPJS'))) return true;
      if (patientCategory === 'EKSEKUTIF' && (normalized.includes('EKSEKUTIF') || normalized.includes('VIP'))) return true;
      
      // Handle alias: BPJS = JKN (only this alias is allowed)
      if (normalized === 'BPJS' && patientCategory === 'JKN') return true;
      
      return false;
    });
  };

  // Action: Ambil Antrean Masuk dari Anjungan (Reservoir -> Waiting Room)
  // "fungsi ini juga sesuaikan dengan idloket dan klinik id"
  // "hanya memanggil tiket dari loket lain harus tiket menunggu yang sesuai id loket dan id kliniknya"
  const callNext = (adminType = 'loket', specificId = null) => {
    const stageMap = {
      'loket': 'loket',
      'klinik': 'klinik',
      'penunjang': 'penunjang'
    };
    
    const targetStage = stageMap[adminType];
    const targetId = specificId;
    
    // Filter list by stage AND relevance to this specific loket/clinic/penunjang
    const eligiblePatients = allPatients.value.filter(p => {
      // 1. Stage Check
      if (p.processStage !== targetStage) return false;
      
      // 2. Relevance Check based on adminType
      if (targetId) {
        if (adminType === 'loket') {
          // Fix: Prioritaskan loketId match. Jika loketId cocok, abaikan check pelayanan (karena bisa jadi data API beda mapping)
          const isLoketMatch = p.loketId && String(p.loketId) === String(targetId);
          if (isLoketMatch) return true;

          const thisLoket = loketStore.getLoketById(parseInt(targetId));
          // Enforce clinic mapping (pelayanan)
          if (thisLoket && thisLoket.pelayanan && Array.isArray(thisLoket.pelayanan)) {
            if (thisLoket.pelayanan.includes(p.kodeKlinik)) {
              // NEW: Check payment compatibility
              if (!isPaymentCompatible(p.pembayaran, thisLoket.pembayaran)) {
                return false;
              }
              return true;
            }
          }
          
          return false;
        } 
        else if (adminType === 'klinik') {
          // Clinic ID match
          if (String(p.kodeKlinik) !== String(targetId) && String(p.klinik) !== String(targetId)) return false;
        }
        else if (adminType === 'penunjang') {
          // Penunjang match
          if (String(p.klinik) !== String(targetId) && String(p.kodeKlinik) !== String(targetId)) return false;
        }
      }
      
      return true;
    });

    // PRIORITAS: Hanya ambil pasien dengan status 'menunggu' (Antrean Baru dari Anjungan)
    // "bukan untuk memanggil pasien tapi tiket baru dari anjungan yang statusnya menunggu"
    // SEQUENTIAL: Sort by Fast Track and Queue No before picking the first matching patient
    const waitingPatients = eligiblePatients.filter(p => p.status === 'menunggu');
    const sortedWaiting = sortPatientsForCalling(waitingPatients);
    const nextPatient = sortedWaiting[0];

    if (!nextPatient) {
      return { success: false, message: `Tidak ada antrean baru yang sesuai untuk ${adminType} ${targetId || ''}` };
    }

    // Hitung kuota yang tersedia (khusus loket ini)
    const currentLoket = loketStore.getLoketById(parseInt(targetId));
    const targetQuota = currentLoket?.kuota || 150;

    const diLoketCount = allPatients.value.filter(p => 
      p.status === 'di-loket' && 
      p.processStage === targetStage && 
      (targetId && adminType === 'loket' ? String(p.loketId) === String(targetId) : true)
    ).length;
    const availableQuota = targetQuota - diLoketCount;

    if (availableQuota <= 0) {
      return { success: false, message: "Kuota sudah penuh" };
    }

    // Update status menjadi 'anjungan' (Masuk ke antrean aktif)
    const callTimestamp = new Date().toISOString();
    const index = allPatients.value.findIndex(p => p.no === nextPatient.no);
    if (index !== -1) {
      allPatients.value[index] = { 
        ...allPatients.value[index], 
        status: "anjungan",
        // Assign to this specific loket if it was unassigned
        loketId: (adminType === 'loket' && targetId) ? parseInt(targetId) : allPatients.value[index].loketId,
        lastCalledAt: callTimestamp
      };

      // SYNC to apiPatientsPerLoket if it's an API patient
      syncApiPatientStatus(allPatients.value[index], "anjungan");

      // POST to external API when patient is called
      try {
        fetch('http://10.10.123.140:8089/api/v1/tiket/update', {
          method: 'POST',
          headers: {
            'Content-Type': 'application/json',
          },
          body: JSON.stringify({
            barcode: nextPatient.barcode || "",
            statuspasien: "3",
            statuspasien2: "4",
            idklinikstatus: "1",
            idklinikstatus2: "1"
          })
        }).then(response => {
          if (response.ok) {
            console.log(`✅ Successfully posted status update for patient ${nextPatient.barcode}`);
          } else {
            console.error(`⚠️ Failed to post status update for patient ${nextPatient.barcode}:`, response.status);
          }
        }).catch(error => {
          console.error(`❌ Error posting status update for patient ${nextPatient.barcode}:`, error);
        });
      } catch (error) {
        console.error(`❌ Error initiating status update for patient ${nextPatient.barcode}:`, error);
      }
    }

    return {
      success: true,
      message: `Berhasil mengambil antrean ${nextPatient.noAntrian.split(" |")[0]} ke daftar tunggu`,
    };
  };

  const callMultiplePatients = (count, adminType = 'loket', specificId = null) => {
    const stageMap = { 'loket': 'loket', 'klinik': 'klinik', 'penunjang': 'penunjang' };
    const targetStage = stageMap[adminType];
    const targetId = specificId;

    const eligiblePatients = allPatients.value.filter(p => {
      if (p.processStage !== targetStage) return false;
      if (targetId) {
        if (adminType === 'loket') {
          // Fix: Prioritaskan loketId match. Jika loketId cocok, abaikan check pelayanan (karena bisa jadi data API beda mapping)
          const isLoketMatch = p.loketId && String(p.loketId) === String(targetId);
          if (isLoketMatch) return true;

          const thisLoket = loketStore.getLoketById(parseInt(targetId));
          if (thisLoket && thisLoket.pelayanan && Array.isArray(thisLoket.pelayanan)) {
            if (thisLoket.pelayanan.includes(p.kodeKlinik)) {
              // NEW: Check payment compatibility
              if (!isPaymentCompatible(p.pembayaran, thisLoket.pembayaran)) {
                return false;
              }
              return true;
            }
          }
          
          return false;
        } else if (adminType === 'klinik' || adminType === 'penunjang') {
          if (String(p.kodeKlinik) !== String(targetId) && String(p.klinik) !== String(targetId)) return false;
        }
      }
      return true;
    });

    // Hanya ambil pasien status 'menunggu'
    // SEQUENTIAL: Sort by Fast Track and Queue No
    const menungguList = sortPatientsForCalling(eligiblePatients.filter(p => p.status === 'menunggu'));

    if (menungguList.length === 0) {
      return { success: false, message: `Tidak ada antrean baru yang sesuai untuk ${adminType} ${targetId || ''}` };
    }

    // Hitung kuota yang tersedia
    const currentLoket = loketStore.getLoketById(parseInt(targetId));
    const targetQuota = currentLoket?.kuota || 150;

    const diLoketCount = allPatients.value.filter(p => 
      p.status === 'di-loket' && 
      p.processStage === targetStage &&
      (targetId && adminType === 'loket' ? String(p.loketId) === String(targetId) : true)
    ).length;
    const availableQuota = targetQuota - diLoketCount;

    const maxCallable = Math.min(count, menungguList.length, availableQuota);
    
    if (maxCallable <= 0) {
      if (availableQuota <= 0) return { success: false, message: "Kuota sudah penuh" };
      return { success: false, message: "Tidak ada antrean yang bisa diambil" };
    }

    const patientsToCall = menungguList.slice(0, maxCallable);
    const callTimestamp = new Date().toISOString();

    patientsToCall.forEach(async (patient) => {
      const index = allPatients.value.findIndex(p => p.no === patient.no);
      if (index !== -1) {
        const newStatus = "anjungan";
        const newLoketId = (adminType === 'loket' && targetId) ? targetId : allPatients.value[index].loketId;
        
        allPatients.value[index] = {
          ...allPatients.value[index],
          status: newStatus,
          loketId: newLoketId,
          lastCalledAt: callTimestamp
        };

        // SYNC to apiPatientsPerLoket if it's an API patient
        syncApiPatientStatus(allPatients.value[index], newStatus);

        // POST to external API when patient is called
        try {
          const response = await fetch('http://10.10.123.140:8089/api/v1/tiket/update', {
            method: 'POST',
            headers: {
              'Content-Type': 'application/json',
            },
            body: JSON.stringify({
              barcode: patient.barcode || "",
              statuspasien: "3",
              statuspasien2: "4",
              idklinikstatus: "1",
              idklinikstatus2: "1"
            })
          });

          if (response.ok) {
            console.log(`✅ Successfully posted status update for patient ${patient.barcode}`);
          } else {
            console.error(`⚠️ Failed to post status update for patient ${patient.barcode}:`, response.status);
          }
        } catch (error) {
          console.error(`❌ Error posting status update for patient ${patient.barcode}:`, error);
        }
      }
    });

    return {
      success: true,
      message: `Berhasil mengambil ${patientsToCall.length} antrean ke daftar tunggu`,
    };
  };

  const processPatient = async (patient, action, adminType = 'loket', specificId = null) => {
    const storageKey = specificId ? `${adminType}-${specificId}` : adminType;
    const patientCode = patient.noAntrian.split(" |")[0];
    let message = "";
    
    // Find patient index for reactive update
    const patientIndex = allPatients.value.findIndex(p => p.no === patient.no);
    
    if (patientIndex === -1) {
      return { success: false, message: "Pasien tidak ditemukan" };
    }

    switch (action) {
      case "check-in":
        // Jika check-in di loket, pindahkan ke klinik dengan status di-loket
        if (adminType === 'loket') {
          allPatients.value[patientIndex] = {
            ...allPatients.value[patientIndex],
            status: "di-loket",
            processStage: "klinik",
            calledByAdmin: false // Reset flag saat pindah stage
          };
          syncApiPatientStatus(allPatients.value[patientIndex], "di-loket");
          message = `Pasien ${patientCode} berhasil check in dan masuk ke Tabel Loket Klinik`;
          
          // POST to external API when patient finishes at loket
          try {
            fetch('http://10.10.123.140:8089/api/v1/tiket/selesai', {
              method: 'POST',
              headers: {
                'Content-Type': 'application/json',
              },
              body: JSON.stringify({
                idloket: String(patient.loketId || specificId || ""),
                barcode: patient.barcode || "",
                statuspasien: "9",
                idklinikstatus: "2"
              })
            }).then(response => {
              if (response.ok) {
                console.log(`✅ [queueStore] Successfully posted selesai status for patient ${patient.barcode}`);
              } else {
                console.error(`⚠️ [queueStore] Failed to post selesai status for patient ${patient.barcode}:`, response.status);
              }
            }).catch(error => {
              console.error(`❌ [queueStore] Error posting selesai status for patient ${patient.barcode}:`, error);
            });
          } catch (error) {
            console.error(`❌ [queueStore] Error initiating selesai status update for patient ${patient.barcode}:`, error);
          }
        } 
        // Jika check-in di klinik, selesai
        else if (adminType === 'klinik') {
          allPatients.value[patientIndex] = {
            ...allPatients.value[patientIndex],
            status: "processed"
          };
          syncApiPatientStatus(allPatients.value[patientIndex], "processed");
          message = `Pasien ${patientCode} berhasil check in di Klinik`;
        }
        // Jika check-in di penunjang, selesai
        else if (adminType === 'penunjang') {
          allPatients.value[patientIndex] = {
            ...allPatients.value[patientIndex],
            status: "processed"
          };
          syncApiPatientStatus(allPatients.value[patientIndex], "processed");
          message = `Pasien ${patientCode} berhasil check in di Penunjang`;
        }
        
        // Clear current processing di admin yang melakukan check-in
        if (currentProcessingPatient.value[storageKey]?.no === patient.no) {
          currentProcessingPatient.value[storageKey] = null;
        }
        break;

      case "terlambat": {
        // DON'T update local state immediately - wait for API success then refresh from DB
        const loketId = patient.loketId;
        
        if (currentProcessingPatient.value[storageKey]?.no === patient.no) {
          currentProcessingPatient.value[storageKey] = null;
        }
        
        message = `Menandai pasien ${patientCode} sebagai terlambat...`;
        
        const payload = {
          barcode: patient.barcode || "",
          statuspasien: "29",
          statuspasien2: "29",
          idklinikstatus: "2",
          idklinikstatus2: "2"
        };
        
        console.log('📤 [TERLAMBAT] Sending API request:', payload);
        
        // POST to external API and WAIT for response
        try {
          const response = await fetch('http://10.10.123.140:8089/api/v1/tiket/update', {
            method: 'POST',
            headers: {
              'Content-Type': 'application/json',
            },
            body: JSON.stringify(payload)
          });
          
          console.log('📥 [TERLAMBAT] API response status:', response.status);
          
          // Read response body
          const responseData = await response.json().catch(() => ({}));
          console.log('📥 [TERLAMBAT] API response data:', responseData);
          
          if (response.ok) {
            console.log(`✅ [TERLAMBAT] API accepted request`);
            
            // Add delay to ensure database is updated
            console.log('⏳ [TERLAMBAT] Waiting 500ms for database update...');
            await new Promise(resolve => setTimeout(resolve, 500));
            
            // REFRESH data from database to get updated status
            console.log(`🔄 [TERLAMBAT] Refreshing data from database...`);
            await fetchPatientsForLoket(loketId);
            
            // Verify status after refresh
            const updatedPatient = allPatients.value.find(p => p.barcode === patient.barcode);
            console.log('🔍 [TERLAMBAT] Patient after refresh:', updatedPatient);
            
            if (updatedPatient?.status === 'terlambat') {
              // API successfully saved status
              console.log('✅ [TERLAMBAT] Status persisted in database!');
              message = `Pasien ${patientCode} ditandai terlambat`;
            } else {
              // API didn't save status - use LocalStorage fallback
              console.warn('⚠️ [TERLAMBAT] API did not persist status. Using LocalStorage fallback.');
              
              // Save to LocalStorage
              const storageKey = `patient-status-${patient.barcode}`;
              localStorage.setItem(storageKey, JSON.stringify({
                status: 'terlambat',
                timestamp: Date.now(),
                barcode: patient.barcode
              }));
              
              // Manually update local state since API didn't save
              const patientIndex = allPatients.value.findIndex(p => p.barcode === patient.barcode);
              if (patientIndex !== -1) {
                allPatients.value[patientIndex] = {
                  ...allPatients.value[patientIndex],
                  status: 'terlambat',
                  calledByAdmin: false
                };
                syncApiPatientStatus(allPatients.value[patientIndex], 'terlambat');
              }
              
              message = `Pasien ${patientCode} ditandai terlambat (local)`;
            }
          } else {
            console.error(`❌ [TERLAMBAT] API rejected request:`, responseData);
            message = `Gagal: ${responseData.message || 'API error'}`;
          }
        } catch (error) {
          console.error(`❌ [TERLAMBAT] Error:`, error);
          message = `Error: ${error.message}`;
        }
        
        break;
      }

      case "pending": {
        // DON'T update local state immediately - wait for API success then refresh from DB
        const loketId = patient.loketId;
        
        if (currentProcessingPatient.value[storageKey]?.no === patient.no) {
          currentProcessingPatient.value[storageKey] = null;
        }
        
        message = `Menandai pasien ${patientCode} sebagai pending...`;
        
        const payload = {
          barcode: patient.barcode || "",
          statuspasien: "28",
          statuspasien2: "28",
          idklinikstatus: "2",
          idklinikstatus2: "2"
        };
        
        console.log('📤 [PENDING] Sending API request:', payload);
        
        // POST to external API and WAIT for response
        try {
          const response = await fetch('http://10.10.123.140:8089/api/v1/tiket/update', {
            method: 'POST',
            headers: {
              'Content-Type': 'application/json',
            },
            body: JSON.stringify(payload)
          });
          
          console.log('📥 [PENDING] API response status:', response.status);
          
          // Read response body
          const responseData = await response.json().catch(() => ({}));
          console.log('📥 [PENDING] API response data:', responseData);
          
          if (response.ok) {
            console.log(`✅ [PENDING] API accepted request`);
            
            // Add delay to ensure database is updated
            console.log('⏳ [PENDING] Waiting 500ms for database update...');
            await new Promise(resolve => setTimeout(resolve, 500));
            
            // REFRESH data from database to get updated status
            console.log(`🔄 [PENDING] Refreshing data from database...`);
            await fetchPatientsForLoket(loketId);
            
            // Verify status after refresh
            const updatedPatient = allPatients.value.find(p => p.barcode === patient.barcode);
            console.log('🔍 [PENDING] Patient after refresh:', updatedPatient);
            
            if (updatedPatient?.status === 'pending') {
              // API successfully saved status
              console.log('✅ [PENDING] Status persisted in database!');
              message = `Pasien ${patientCode} di-pending`;
            } else {
              // API didn't save status - use LocalStorage fallback
              console.warn('⚠️ [PENDING] API did not persist status. Using LocalStorage fallback.');
              
              // Save to LocalStorage
              const storageKey = `patient-status-${patient.barcode}`;
              localStorage.setItem(storageKey, JSON.stringify({
                status: 'pending',
                timestamp: Date.now(),
                barcode: patient.barcode
              }));
              
              // Manually update local state since API didn't save
              const patientIndex = allPatients.value.findIndex(p => p.barcode === patient.barcode);
              if (patientIndex !== -1) {
                allPatients.value[patientIndex] = {
                  ...allPatients.value[patientIndex],
                  status: 'pending',
                  calledByAdmin: false
                };
                syncApiPatientStatus(allPatients.value[patientIndex], 'pending');
              }
              
              message = `Pasien ${patientCode} di-pending (local)`;
            }
          } else {
            console.error(`❌ [PENDING] API rejected request:`, responseData);
            message = `Gagal: ${responseData.message || 'API error'}`;
          }
        } catch (error) {
          console.error(`❌ [PENDING] Error:`, error);
          message = `Error: ${error.message}`;
        }
        
        break;
      }

      case "aktifkan": {
        const currentStatus = allPatients.value[patientIndex].status;
        if (currentStatus === "terlambat" || currentStatus === "pending") {
          // PERBAIKAN: Update dengan cara yang Vue reactive
          allPatients.value[patientIndex] = {
            ...allPatients.value[patientIndex],
            status: "di-loket"
          };
          syncApiPatientStatus(allPatients.value[patientIndex], "di-loket");
          message = `Pasien ${patientCode} diaktifkan kembali dan masuk ke tabel Di Loket`;
          
          // POST to external API to revert status to sedang diproses (status 8)
          try {
            fetch('http://10.10.123.140:8089/api/v1/tiket/update', {
              method: 'POST',
              headers: {
                'Content-Type': 'application/json',
              },
              body: JSON.stringify({
                barcode: patient.barcode || "",
                statuspasien: "8",      // Status code for "sedang diproses" when reactivating
                statuspasien2: "8",
                idklinikstatus: "2",
                idklinikstatus2: "2"
              })
            }).then(response => {
              if (response.ok) {
                console.log(`✅ [queueStore] Successfully activated patient ${patient.barcode} to sedang diproses`);
              } else {
                console.error(`⚠️ [queueStore] Failed to activate patient ${patient.barcode}:`, response.status);
              }
            }).catch(error => {
              console.error(`❌ [queueStore] Error activating patient ${patient.barcode}:`, error);
            });
          } catch (error) {
            console.error(`❌ [queueStore] Error initiating activation for patient ${patient.barcode}:`, error);
          }
        } else {
          message = `Pasien ${patientCode} tidak dapat diaktifkan (status: ${currentStatus})`;
        }
        break;
      }

      case "proses": {
        // Ambil data terbaru dari array
        const patient = allPatients.value[patientIndex];
        
        // Jika pasien memiliki status "pending" atau "terlambat", ubah menjadi "di-loket"
        // agar pasien masuk ke kategori diLoketPatients dan ditampilkan sebagai "diproses"
        const currentStatus = patient.status;
        const shouldUpdateStatus = currentStatus === "pending" || currentStatus === "terlambat";
        
        // Jika adminType adalah 'loket', pastikan ada loket assignment
        if (adminType === 'loket') {
          // Pastikan antrian yang diproses memiliki loket assignment
          const currentLoket = patient.loket || getDefaultLoket();
          const currentLoketId = patient.loketId || 1;
          
          // Update patient dengan loket assignment dan status (jika perlu)
          const updatedPatient = {
            ...patient,
            loket: patient.loket || currentLoket,
            loketId: patient.loketId || currentLoketId,
            // Ubah status menjadi "di-loket" jika sebelumnya "pending" atau "terlambat"
            ...(shouldUpdateStatus && { status: "di-loket" })
          };
          
          allPatients.value[patientIndex] = updatedPatient;
          // Set currentProcessingPatient with isolated key
          currentProcessingPatient.value[storageKey] = updatedPatient;
          
          // POST to external API when patient is being processed (sedang diproses)
          try {
            fetch('http://10.10.123.140:8089/api/v1/tiket/update', {
              method: 'POST',
              headers: {
                'Content-Type': 'application/json',
              },
              body: JSON.stringify({
                barcode: patient.barcode || "",
                statuspasien: "8",      // Status code for "sedang diproses"
                statuspasien2: "8",
                idklinikstatus: "2",
                idklinikstatus2: "2"
              })
            }).then(response => {
              if (response.ok) {
                console.log(`✅ [queueStore] Successfully updated patient ${patient.barcode} to sedang diproses`);
              } else {
                console.error(`⚠️ [queueStore] Failed to update patient ${patient.barcode} to sedang diproses:`, response.status);
              }
            }).catch(error => {
              console.error(`❌ [queueStore] Error updating patient ${patient.barcode} to sedang diproses:`, error);
            });
          } catch (error) {
            console.error(`❌ [queueStore] Error initiating status update for patient ${patient.barcode}:`, error);
          }
        } else {
          // Untuk adminType selain loket, update status jika perlu
          if (shouldUpdateStatus) {
            const updatedPatient = {
              ...patient,
              status: "di-loket"
            };
            allPatients.value[patientIndex] = updatedPatient;
            currentProcessingPatient.value[storageKey] = updatedPatient;
          } else {
            currentProcessingPatient.value[storageKey] = patient;
          }
        }
        message = `Memproses pasien ${patientCode}`;
        break;
      }
    }

    return { success: true, message };
  };



  const callProcessingPatient = (adminType = 'loket', specificId = null) => {
    // Panggil pasien yang sedang diproses untuk ditampilkan di layar anjungan
    const storageKey = specificId ? `${adminType}-${specificId}` : adminType;
    const processingPatient = currentProcessingPatient.value[storageKey];
    
    if (!processingPatient) {
      return { success: false, message: "Tidak ada pasien yang sedang diproses" };
    }

    // Update flag calledByAdmin
    const patientIndex = allPatients.value.findIndex(p => p.no === processingPatient.no);
    if (patientIndex !== -1) {
      allPatients.value[patientIndex] = {
        ...allPatients.value[patientIndex],
        calledByAdmin: true,
        lastCalledAt: new Date().toISOString()
      };
      
      // Update currentProcessingPatient with the new data
      currentProcessingPatient.value[storageKey] = allPatients.value[patientIndex];
      
      return {
        success: true,
        message: `Memanggil pasien ${processingPatient.noAntrian.split(" |")[0]}`
      };
    }

    return { success: false, message: "Gagal memanggil pasien" };
  };

  const createAntreanKlinik = (klinik, patient = null, adminType = 'loket') => {
    const newNo = allPatients.value.length + 1;
    const timestamp = new Date();
    const barcode = patient ? patient.barcode : generateBarcode([], allPatients);
    
    const newPatient = {
      no: newNo,
      jamPanggil: `${String(timestamp.getHours()).padStart(2, "0")}:${String(
        timestamp.getMinutes()
      ).padStart(2, "0")}`,
      barcode: barcode,
      noAntrian: `KL${String(newNo).padStart(4, "0")} | Klinik - ${barcode}`,
      shift: "Shift 1",
      klinik: klinik.name,
      fastTrack: "TIDAK",
      pembayaran: patient ? patient.pembayaran : "UMUM",
      noRM: patient ? (patient.noRM || `RM-SCAN-${barcode.slice(-6)}`) : `RM-SCAN-${barcode.slice(-6)}`,
      kodeKlinik: klinik.kode || klinik.id, // Ensure kodeKlinik is saved
      status: "di-loket",
      processStage: "klinik",
      createdAt: timestamp.toISOString(),
      referencePatient: patient ? patient.noAntrian : null,
      loketId: null, // Initial check
    };

    // Auto-assign Loket ID based on Clinic Mapping
    // "menyesuaikan loketnya berdasar loket id tergantung dari create tiket itu di klinik"
    if (newPatient.kodeKlinik) {
       const allLokets = loketStore.lokets || [];
       const targetLoket = allLokets.find(l => 
         l.pelayanan && Array.isArray(l.pelayanan) && l.pelayanan.includes(newPatient.kodeKlinik)
       );
       
       if (targetLoket) {
         newPatient.loketId = targetLoket.id;
         newPatient.loket = targetLoket.namaLoket;
         console.log(`✅ Auto-assigned Ticket ${newPatient.noAntrian} to Loket ${targetLoket.id} (${targetLoket.namaLoket})`);
       } else {
         console.log(`⚠️ No specific Loket found for Clinic ${newPatient.kodeKlinik}, defaulting to general pool.`);
       }
    }

    allPatients.value.push(newPatient);
    
    // Increment counter setelah barcode digunakan
    if (!patient) {
      incrementBarcodeCounter();
    }

    return {
      success: true,
      message: `Antrean klinik ${klinik.name} berhasil dibuat dan masuk ke Tabel Loket Klinik`,
      patient: newPatient,
    };
  };

  const createAntreanPenunjang = (penunjang, patient = null, adminType = 'loket') => {
    const newNo = allPatients.value.length + 1;
    const timestamp = new Date();
    const barcode = patient ? patient.barcode : generateBarcode([], allPatients);

    const newPatient = {
      no: newNo,
      jamPanggil: `${String(timestamp.getHours()).padStart(2, "0")}:${String(
        timestamp.getMinutes()
      ).padStart(2, "0")}`,
      barcode: barcode,
      noAntrian: `PN${String(newNo).padStart(4, "0")} | Penunjang - ${barcode}`,
      shift: "Shift 1",
      klinik: penunjang.name,
      fastTrack: "TIDAK",
      pembayaran: patient ? patient.pembayaran : "UMUM",
      noRM: patient ? patient.noRM : `RM-SCAN-${barcode.slice(-6)}`,
      status: "di-loket",
      processStage: "penunjang",
      createdAt: timestamp.toISOString(),
      referencePatient: patient ? patient.noAntrian : null,
    };

    allPatients.value.push(newPatient);
    
    // Increment counter setelah barcode digunakan
    if (!patient) {
      incrementBarcodeCounter();
    }

    return {
      success: true,
      message: `Antrean penunjang ${penunjang.name} berhasil dibuat dan masuk ke Tabel Loket Penunjang`,
      patient: newPatient,
    };
  };

  const createAntreanKlinikRuang = (klinikRuang, ruang, patient = null, adminType = 'klinik', apiTicketNumber = null) => {
    const newNo = allPatients.value.length + 1;
    const timestamp = new Date();
    const barcode = patient ? patient.barcode : generateBarcode([], allPatients);

    // Generate nomor antrian baru dengan format: [huruf pertama poli + urutan abjad ruang + nomor antrian ruang]
    // Contoh: "Anak" ruang 1 = "AA001" (A dari Anak, A dari ruang 1, 001 nomor antrian)
    
    let newNoAntrian;
    
    // Use API ticket number if available, otherwise generate locally
    if (apiTicketNumber) {
      newNoAntrian = apiTicketNumber; // Use ticket from API (e.g., "PK001")
      console.log('🎫 Using API ticket number:', newNoAntrian);
    } else {
      // 1. Ambil huruf pertama dari nama klinik/poli
      const firstLetter = klinikRuang.namaKlinik.charAt(0).toUpperCase();
      
      // 2. Konversi nomor ruang ke abjad (1 = A, 2 = B, 3 = C, dst)
      const ruangNumber = parseInt(ruang.nomorRuang) || 1;
      const ruangLetter = String.fromCharCode(64 + ruangNumber); // 64 = '@', 65 = 'A', 66 = 'B', dst
      
      // 3. Hitung nomor antrian ruang (dimulai dari 1, maksimal 3 digit)
      const roomQueues = allPatients.value.filter(p => 
        p.kodeKlinik === klinikRuang.kodeKlinik &&
        p.nomorRuang === ruang.nomorRuang &&
        p.processStage === 'klinik-ruang'
      );
      const queueNumber = roomQueues.length + 1;
      const queueNumberStr = String(queueNumber).padStart(3, "0");
      
      // 4. Format nomor antrian: AA001, AB002, dst
      newNoAntrian = `${firstLetter}${ruangLetter}${queueNumberStr}`;
    }

    const newPatient = {
      no: newNo,
      jamPanggil: `${String(timestamp.getHours()).padStart(2, "0")}:${String(
        timestamp.getMinutes()
      ).padStart(2, "0")}`,
      barcode: barcode,
      noAntrian: `${newNoAntrian} | ${klinikRuang.namaKlinik} - ${ruang.namaRuang}`,
      noAntrianRuang: `${klinikRuang.namaKlinik} - ${ruang.namaRuang} | ${newNoAntrian}`,
      shift: patient ? (patient.shift || "Shift 1") : "Shift 1",
      klinik: klinikRuang.namaKlinik,
      ruang: ruang.namaRuang,
      kodeKlinik: klinikRuang.kodeKlinik,
      nomorRuang: ruang.nomorRuang,
      nomorScreen: ruang.nomorScreen,
      fastTrack: patient ? (patient.fastTrack || "TIDAK") : "TIDAK",
      pembayaran: patient ? patient.pembayaran : "UMUM",
      noRM: patient ? (patient.noRM || patient.norm) : null,
      status: "pemeriksaan", // Patients from loket to klinik ruang start as "pemeriksaan"
      processStage: "klinik-ruang", // Set ke klinik-ruang langsung
      createdAt: timestamp.toISOString(),
      referencePatient: patient ? patient.noAntrian : null,
      sourcePatientNo: patient ? patient.no : null,
      visitId: patient ? (patient.visitId || patient.id) : null,
      visitCode: patient ? (patient.visitCode || patient.visit_code) : null,
      registrationType: patient ? (patient.registrationType || 'onsite') : 'onsite',
      // Tracking panggilan
      calledPemeriksaanAwal: false,
      calledTindakan: false,
      lastCalledAt: null,
      lastCalledTipeLayanan: null,
      calledByAdmin: false, // Flag untuk tracking apakah sudah dipanggil oleh admin
    };

    allPatients.value.push(newPatient);
    
    // Increment counter setelah barcode digunakan
    if (!patient) {
      incrementBarcodeCounter();
    }

    return {
      success: true,
      message: `Antrean ${klinikRuang.namaKlinik} Ruang ${ruang.nomorRuang} berhasil dibuat: ${newNoAntrian}`,
      patient: newPatient,
    };
  };

  // Pindah pasien ke klinik ruang lain dengan nomor antrian tetap
  const pindahKlinikRuang = (patient, targetKlinikRuang, targetRuang) => {
    
    if (patientIndex === -1) {
      return { success: false, message: "Pasien tidak ditemukan" };
    }

    const currentPatient = allPatients.value[patientIndex];
    
    // Jika pasien sudah punya antrean klinik ruang, update antreannya
    if (currentPatient.processStage === 'klinik-ruang' && currentPatient.tipeLayanan) {
      const baseNoAntrian = currentPatient.noAntrian?.split(" |")[0] || currentPatient.barcode || '';
      
      // Update dengan klinik dan ruang baru, tapi nomor antrian tetap sama
      allPatients.value[patientIndex] = {
        ...currentPatient,
        klinik: targetKlinikRuang.namaKlinik,
        ruang: targetRuang.namaRuang,
        kodeKlinik: targetKlinikRuang.kodeKlinik,
        nomorRuang: targetRuang.nomorRuang,
        nomorScreen: targetRuang.nomorScreen,
        // Nomor antrian tetap sama
        noAntrian: `${baseNoAntrian} | ${targetKlinikRuang.namaKlinik} - ${targetRuang.namaRuang} - ${currentPatient.tipeLayanan}`,
        noAntrianRuang: `${targetKlinikRuang.namaKlinik} - ${targetRuang.namaRuang} | ${baseNoAntrian}`
      };
      
      // Clear current processing dari ruang lama jika ada
      const oldKey = `klinik-ruang-${currentPatient.kodeKlinik}-${currentPatient.nomorRuang}`;
      if (currentProcessingPatient.value[oldKey]?.no === currentPatient.no) {
        currentProcessingPatient.value[oldKey] = null;
      }
    } else {
      // Pasien belum digenerate tiket, hanya update ruang
      allPatients.value[patientIndex] = {
        ...currentPatient,
        klinik: targetKlinikRuang.namaKlinik,
        ruang: targetRuang.namaRuang,
        kodeKlinik: targetKlinikRuang.kodeKlinik,
        nomorRuang: targetRuang.nomorRuang,
        nomorScreen: targetRuang.nomorScreen
      };
    }

    return {
      success: true,
      message: `Pasien berhasil dipindah ke ${targetKlinikRuang.namaKlinik} Ruang ${targetRuang.nomorRuang}`,
      patient: allPatients.value[patientIndex]
    };
  };

  // Konsultasi: pindah pasien ke klinik ruang lain dengan nomor antrian baru (prefix K- atau KF-)
  const konsultasiKlinikRuang = (patient, targetKlinikRuang, targetRuang, tipeLayanan = 'Pemeriksaan Awal') => {
    const patientIndex = allPatients.value.findIndex(p => p.no === patient.no);
    
    if (patientIndex === -1) {
      return { success: false, message: "Pasien tidak ditemukan" };
    }

    const sourcePatient = allPatients.value[patientIndex];
    
    // Cek apakah sudah ada antrean konsultasi di ruang tujuan
    const existingKonsultasi = allPatients.value.find(p => 
      p.referencePatient === sourcePatient.noAntrian &&
      p.kodeKlinik === targetKlinikRuang.kodeKlinik &&
      p.nomorRuang === targetRuang.nomorRuang &&
      p.processStage === 'klinik-ruang' &&
      (p.noAntrian?.startsWith('K-') || p.noAntrian?.startsWith('KF-'))
    );

    if (existingKonsultasi) {
      return { 
        success: false, 
        message: `Pasien sudah memiliki antrean konsultasi di ${targetKlinikRuang.namaKlinik} Ruang ${targetRuang.nomorRuang}` 
      };
    }

    // Generate queue number untuk konsultasi
    const roomQueues = allPatients.value.filter(p => 
      p.kodeKlinik === targetKlinikRuang.kodeKlinik &&
      p.nomorRuang === targetRuang.nomorRuang &&
      p.tipeLayanan === tipeLayanan &&
      p.processStage === 'klinik-ruang' &&
      (p.noAntrian?.startsWith('K-') || p.noAntrian?.startsWith('KF-'))
    );
    
    const queueNumber = roomQueues.length + 1;
    const prefix = tipeLayanan === 'Pemeriksaan Awal' ? 'PA' : 'TD';
    
    // Tentukan prefix konsultasi: KF- untuk Fast Track, K- untuk normal
    const isFastTrack = sourcePatient.fastTrack === "YA";
    const konsultasiPrefix = isFastTrack ? 'KF-' : 'K-';
    const queueNumberStr = String(queueNumber).padStart(3, "0");
    const newNoAntrian = `${konsultasiPrefix}${prefix}${queueNumberStr}`;
    
    const newNo = allPatients.value.length + 1;
    const timestamp = new Date();

    const newPatient = {
      no: newNo,
      jamPanggil: `${String(timestamp.getHours()).padStart(2, "0")}:${String(
        timestamp.getMinutes()
      ).padStart(2, "0")}`,
      barcode: sourcePatient.barcode,
      noAntrian: `${newNoAntrian} | ${targetKlinikRuang.namaKlinik} - ${targetRuang.namaRuang} - ${tipeLayanan}`,
      noAntrianRuang: `${targetKlinikRuang.namaKlinik} - ${targetRuang.namaRuang} | ${newNoAntrian}`,
      shift: sourcePatient.shift || "Shift 1",
      klinik: targetKlinikRuang.namaKlinik,
      ruang: targetRuang.namaRuang,
      kodeKlinik: targetKlinikRuang.kodeKlinik,
      nomorRuang: targetRuang.nomorRuang,
      nomorScreen: targetRuang.nomorScreen,
      tipeLayanan: tipeLayanan,
      fastTrack: sourcePatient.fastTrack || "TIDAK",
      pembayaran: sourcePatient.pembayaran || "UMUM",
      status: "anjungan",
      processStage: "klinik-ruang",
      createdAt: sourcePatient.createdAt || timestamp.toISOString(), // Gunakan createdAt dari pasien awal
      referencePatient: sourcePatient.noAntrian,
      sourcePatientNo: sourcePatient.no,
      isKonsultasi: true, // Flag untuk konsultasi
    };

    allPatients.value.push(newPatient);

    return {
      success: true,
      message: `Antrean konsultasi berhasil dibuat: ${newNoAntrian} untuk ${targetKlinikRuang.namaKlinik} Ruang ${targetRuang.nomorRuang}`,
      patient: newPatient,
    };
  };

  // Scan barcode dan generate antrean klinik ruang baru
  const scanAndCreateAntreanKlinikRuang = (barcodeInput, klinikRuang, ruang, tipeLayanan = 'Pemeriksaan Awal') => {
    // Clean input - remove whitespace and handle prefix letters
    const cleanInput = String(barcodeInput).trim().toUpperCase();
    // Remove leading letters if any (e.g., "J200730100005" -> "200730100005")
    const numericInput = cleanInput.replace(/^[A-Z]+/, '');
    
    // Find patient by barcode or noAntrian
    const sourcePatient = allPatients.value.find(p => {
      // Exact barcode match
      if (p.barcode === cleanInput || p.barcode === numericInput) return true;
      
      // Check if noAntrian includes the input
      const noAntrianUpper = (p.noAntrian || '').toUpperCase();
      if (noAntrianUpper.includes(cleanInput) || noAntrianUpper.includes(numericInput)) return true;
      
      // Try extracting number from noAntrian
      const noAntrianNumber = noAntrianUpper.match(/([A-Z]+)(\d+)/);
      if (noAntrianNumber) {
        const extractedNumber = noAntrianNumber[2];
        if (extractedNumber.includes(numericInput) || numericInput.includes(extractedNumber)) return true;
      }
      
      return false;
    });

    if (!sourcePatient) {
      return { success: false, message: "Pasien tidak ditemukan. Pastikan barcode/nomor antrian benar." };
    }

    // Check if patient already has antrean klinik ruang for this room
    const existingAntrean = allPatients.value.find(p => 
      p.referencePatient === sourcePatient.noAntrian &&
      p.kodeKlinik === klinikRuang.kodeKlinik &&
      p.nomorRuang === ruang.nomorRuang &&
      p.processStage === 'klinik-ruang'
    );

    if (existingAntrean) {
      return { 
        success: false, 
        message: `Pasien sudah memiliki antrean di ${klinikRuang.namaKlinik} Ruang ${ruang.nomorRuang}` 
      };
    }

    // Generate queue number for this specific room and tipeLayanan
    const roomQueues = allPatients.value.filter(p => 
      p.kodeKlinik === klinikRuang.kodeKlinik &&
      p.nomorRuang === ruang.nomorRuang &&
      p.tipeLayanan === tipeLayanan &&
      p.processStage === 'klinik-ruang' &&
      !p.noAntrian?.startsWith('K-') && // Exclude konsultasi
      !p.noAntrian?.startsWith('KF-') // Exclude konsultasi Fast Track
    );
    
    const queueNumber = roomQueues.length + 1;
    const prefix = tipeLayanan === 'Pemeriksaan Awal' ? 'PA' : 'TD';
    
    // Tentukan prefix untuk Fast Track: F- untuk Fast Track
    const isFastTrack = sourcePatient.fastTrack === "YA";
    const fastTrackPrefix = isFastTrack ? 'F-' : '';
    const queueNumberStr = String(queueNumber).padStart(3, "0");
    const newNoAntrian = `${fastTrackPrefix}${prefix}${queueNumberStr}`;
    
    const newNo = allPatients.value.length + 1;
    const timestamp = new Date();

    const newPatient = {
      no: newNo,
      jamPanggil: `${String(timestamp.getHours()).padStart(2, "0")}:${String(
        timestamp.getMinutes()
      ).padStart(2, "0")}`,
      barcode: sourcePatient.barcode,
      noAntrian: `${newNoAntrian} | ${klinikRuang.namaKlinik} - ${ruang.namaRuang} - ${tipeLayanan}`,
      noAntrianRuang: `${klinikRuang.namaKlinik} - ${ruang.namaRuang} | ${newNoAntrian}`,
      shift: sourcePatient.shift || "Shift 1",
      klinik: klinikRuang.namaKlinik,
      ruang: ruang.namaRuang,
      kodeKlinik: klinikRuang.kodeKlinik,
      nomorRuang: ruang.nomorRuang,
      nomorScreen: ruang.nomorScreen,
      tipeLayanan: tipeLayanan,
      fastTrack: sourcePatient.fastTrack || "TIDAK",
      pembayaran: sourcePatient.pembayaran || "UMUM",
      status: "anjungan",
      processStage: "klinik-ruang",
      createdAt: sourcePatient.createdAt || timestamp.toISOString(), // Gunakan createdAt dari pasien awal
      referencePatient: sourcePatient.noAntrian,
      sourcePatientNo: sourcePatient.no,
    };

    allPatients.value.push(newPatient);

    return {
      success: true,
      message: `Antrean ${tipeLayanan} berhasil dibuat: ${newPatient.noAntrian.split(" |")[0]} untuk ${klinikRuang.namaKlinik} Ruang ${ruang.nomorRuang}`,
      patient: newPatient,
    };
  };

  // Get patients by klinik and ruang
  const getPatientsByKlinikRuang = (kodeKlinik, nomorRuang, tipeLayanan = null) => {
    return computed(() => {
      let patients = allPatients.value.filter(p => 
        p.kodeKlinik === kodeKlinik &&
        p.nomorRuang === nomorRuang &&
        p.processStage === 'klinik-ruang'
      );

      if (tipeLayanan) {
        patients = patients.filter(p => p.tipeLayanan === tipeLayanan);
      }

      return {
        all: patients,
        anjungan: patients.filter(p => p.status === 'anjungan'),
        diLoket: patients.filter(p => p.status === 'di-loket'),
        terlambat: patients.filter(p => p.status === 'terlambat'),
        pending: patients.filter(p => p.status === 'pending'),
      };
    });
  };

  // Call next patient for a specific room and tipeLayanan
  const callNextKlinikRuang = (kodeKlinik, nomorRuang, tipeLayanan, allowMultiple = false) => {
    const patients = allPatients.value.filter(p => 
      p.kodeKlinik === kodeKlinik &&
      p.nomorRuang === nomorRuang &&
      p.tipeLayanan === tipeLayanan &&
      p.processStage === 'klinik-ruang' &&
      (p.status === 'anjungan' || (allowMultiple && p.status === 'di-loket'))
    ).sort((a, b) => {
      // Sort by status priority first: anjungan=1, di-loket=2
      const statusPriority = { 'anjungan': 1, 'di-loket': 2 };
      const priorityDiff = (statusPriority[a.status] || 99) - (statusPriority[b.status] || 99);
      if (priorityDiff !== 0) return priorityDiff;
      
      const numA = parseInt(a.noAntrian?.match(/\d+/)?.[0] || '999');
      const numB = parseInt(b.noAntrian?.match(/\d+/)?.[0] || '999');
      return numA - numB;
    });

    if (patients.length === 0) {
      return { success: false, message: `Tidak ada antrian ${tipeLayanan} yang menunggu` };
    }

    const nextPatient = patients[0];
    const index = allPatients.value.findIndex(p => p.no === nextPatient.no);
    if (index !== -1) {
      allPatients.value[index] = {
        ...allPatients.value[index],
        status: "di-loket",
        lastCalledAt: new Date().toISOString()
      };
    }
    return { 
      success: true, 
      message: `Memanggil ${nextPatient.noAntrian.split(" |")[0]}`, 
      patient: allPatients.value[index] 
    };
  };

  // Process patient in klinik ruang (set as current processing)
  const processPatientKlinikRuang = async (patient, action, kodeKlinik, nomorRuang) => {
    const patientIndex = allPatients.value.findIndex(p => p.no === patient.no);
    if (patientIndex === -1) return { success: false, message: "Pasien tidak ditemukan" };

    const specificId = `${kodeKlinik}-${nomorRuang}`;
    const storageKey = `klinik-ruang-${specificId}`;
    const pCode = patient.noAntrian.split(" |")[0];
    let message = "";

    switch (action) {
      case "proses":
        currentProcessingPatient.value[storageKey] = allPatients.value[patientIndex];
        message = `Memproses ${pCode}`;
        break;
      case "selesai":
        // Send API call to finish endpoint
        try {
          const apiUrl = 'http://10.10.123.135:8084/api/v1/visit/status/finish';
          const requestBody = {
            patient_visit_healthcare_service_id: patient.healthcareServiceId,
            visit_code: patient.barcode || patient.visitCode,
            visit_status_id: [19]
          };

          console.log('📤 [queueStore] Sending finish request to API:', requestBody);
          
          const response = await fetch(apiUrl, {
            method: 'POST',
            headers: {
              'Content-Type': 'application/json',
            },
            body: JSON.stringify(requestBody)
          });

          if (!response.ok) {
            console.error('❌ [queueStore] Failed to finish patient via API:', response.status, response.statusText);
            // Continue with local status update even if API fails
          } else {
            console.log('✅ [queueStore] Successfully finished patient via API');
          }
        } catch (error) {
          console.error('❌ [queueStore] Error calling finish API:', error);
          // Continue with local status update even if API fails
        }

        // Update local status
        allPatients.value[patientIndex] = { ...allPatients.value[patientIndex], status: "processed" };
        currentProcessingPatient.value[storageKey] = null;
        message = `Pasien ${pCode} selesai diproses`;
        break;
      case "terlambat":
        allPatients.value[patientIndex] = { ...allPatients.value[patientIndex], status: "terlambat" };
        currentProcessingPatient.value[storageKey] = null;
        message = `Pasien ${pCode} ditandai terlambat`;
        break;
      case "pending":
        allPatients.value[patientIndex] = { ...allPatients.value[patientIndex], status: "pending" };
        currentProcessingPatient.value[storageKey] = null;
        message = `Pasien ${pCode} di-pending`;
        break;
    }
    return { success: true, message };
  };

  const changeKlinik = (patient, newKlinik, adminType = 'loket', specificId = null) => {
    const patientIndex = allPatients.value.findIndex(p => p.no === patient.no);
    if (patientIndex === -1) return { success: false, message: "Pasien tidak ditemukan" };

    const oldPatient = allPatients.value[patientIndex];
    const oldLoketId = oldPatient.loketId;
    
    // Find appropriate loket for the new clinic from loketStore
    let targetLoketId = oldLoketId;
    let targetLoketName = oldPatient.loket;
    
    if (newKlinik.kode) {
      const allLokets = loketStore.lokets || [];
      const foundLoket = allLokets.find(l => 
        l.pelayanan && Array.isArray(l.pelayanan) && l.pelayanan.includes(newKlinik.kode)
      );
      
      if (foundLoket) {
        targetLoketId = foundLoket.id;
        targetLoketName = foundLoket.namaLoket;
        console.log(`📡 [queueStore] changeKlinik: Re-assigning to Loket ${targetLoketId} (${targetLoketName})`);
      }
    }

    // Determine if we need to reset status from 'diproses' to 'di-loket' if moved to different loket
    const isMovedToDifferentLoket = String(oldLoketId) !== String(targetLoketId);
    let newStatus = oldPatient.status;
    
    // Check if patient is currently being processed at the old loket
    const oldKey = specificId ? `${adminType}-${specificId}` : adminType;
    const isCurrentlyProcessingAtOld = currentProcessingPatient.value[oldKey]?.no === patient.no;

    if (isMovedToDifferentLoket && isCurrentlyProcessingAtOld) {
      // If moved, they should be back to waiting list ('di-loket') at the new target
      newStatus = 'di-loket';
      // Clear from old processing slot
      currentProcessingPatient.value[oldKey] = null;
      console.log(`📡 [queueStore] changeKlinik: Cleared processing at ${oldKey} and set status to 'di-loket'`);
    }

    // Update the patient in allPatients
    const updatedPatient = { 
      ...oldPatient, 
      klinik: newKlinik.name, 
      kodeKlinik: newKlinik.kode,
      loketId: targetLoketId,
      loket: targetLoketName,
      status: newStatus
    };
    
    allPatients.value[patientIndex] = updatedPatient;
    
    // If they AREN'T moved but were processing, update their clinic info in the current slot
    if (!isMovedToDifferentLoket && isCurrentlyProcessingAtOld) {
      currentProcessingPatient.value[oldKey] = updatedPatient;
    }

    let successMessage = `Klinik berhasil diubah ke ${newKlinik.name}`;
    if (isMovedToDifferentLoket) {
      successMessage += `. Antrean dipindah ke ${targetLoketName}.`;
    }

    return { 
      success: true, 
      message: successMessage,
      patient: updatedPatient,
      moved: isMovedToDifferentLoket
    };
  };


  const getCurrentProcessing = (adminType, id = null) => {
    const key = id ? `${adminType}-${id}` : adminType;
    return computed(() => currentProcessingPatient.value[key] || null);
  };

  const setCurrentProcessing = (patient, adminType, id = null) => {
    const key = id ? `${adminType}-${id}` : adminType;
    currentProcessingPatient.value[key] = patient;
  };

  const generateQueueNumber = (clinic, paymentType, isEksekutif = false) => {
    const serviceType = isEksekutif ? 'E' : 'R';
    const today = new Date().toISOString().split('T')[0];
    // FIXED: Use the same key as initializeCountersFromSeed
    const counterKey = `queue_counter_loket_shared_${today}`;
    
    let counter = 0;
    if (typeof window !== 'undefined') {
      const stored = localStorage.getItem(counterKey);
      counter = stored ? parseInt(stored, 10) : 0;
    }
    
    counter = counter + 1;
    const loketIndex = Math.floor((counter - 1) / 999);
    
    if (loketIndex > 13) {
      counter = 1;
      if (typeof window !== 'undefined') localStorage.setItem(counterKey, '1');
      return `${serviceType}A001`;
    }
    
    const loketLetter = String.fromCharCode(65 + loketIndex);
    const numberInLoket = ((counter - 1) % 999) + 1;
    const numberPart = String(numberInLoket).padStart(3, '0');
    
    if (typeof window !== 'undefined') localStorage.setItem(counterKey, counter.toString());
    
    return `${serviceType}${loketLetter}${numberPart}`;
  };

  // Register patient from Anjungan (onsite registration)
  const registerPatientFromAnjungan = (clinic, paymentType, visitType = 'SEKARANG', visitDate = null, shift = 'Shift 1', namaDokter = null, isFastTrack = false, fastTrackData = null, loketId = null, loket = null) => {
    // 1. Validasi keberadaan (prevent duplicates)
    // Gunakan date today untuk check-in sync
    const timestamp = new Date();
    
    // Generate barcode FIRST to check for existence
    const barcode = generateBarcode([], allPatients);
    
    // Check EXACT barcode match prevent duplicate submission
    const duplicate = allPatients.value.find(p => p.barcode === barcode);
    if (duplicate) {
        return {
            success: false,
            message: "Pasien dengan barcode ini sudah terdaftar untuk hari ini.",
            patient: duplicate
        };
    }

    const newNo = allPatients.value.length > 0 
      ? Math.max(...allPatients.value.map(p => p.no)) + 1 
      : 1;
    
    const visitDateTime = visitDate ? new Date(visitDate) : timestamp;
    const jamPanggil = visitDate 
      ? `${String(visitDateTime.getHours()).padStart(2, "0")}:${String(visitDateTime.getMinutes()).padStart(2, "0")}`
      : `${String(timestamp.getHours()).padStart(2, "0")}:${String(timestamp.getMinutes()).padStart(2, "0")}`;
    
    // Tentukan apakah pasien Eksekutif/Grand Pavilion (jika ada namaDokter, berarti Eksekutif)
    const isEksekutif = namaDokter !== null && namaDokter !== undefined && namaDokter !== '';
    
    // Generate nomor antrean dengan format baru: R/E + Loket (A-N) + 3 digit
    const queueNumber = generateQueueNumber(clinic, paymentType, isEksekutif);
    const finalQueueNumber = isFastTrack ? `F-${queueNumber}` : queueNumber;
    const noAntrian = `${finalQueueNumber} | Onsite - ${barcode}`;
    
    const status = 'menunggu';
    
    const newPatient = {
      no: newNo,
      jamPanggil: jamPanggil,
      barcode: barcode,
      noAntrian: noAntrian,
      shift: shift,
      klinik: clinic.name || clinic,
      kodeKlinik: clinic.kode || null, // Essential for filtering
      fastTrack: isFastTrack ? "YA" : "TIDAK",
      pembayaran: paymentType,
      noRM: `RM-${barcode.slice(-6)}`,
      status: status,
      processStage: "loket",
      createdAt: timestamp.toISOString(),
      registrationType: 'onsite',
      visitType: visitType,
      visitDate: visitDate || timestamp.toISOString().substring(0, 10),
      namaDokter: namaDokter || null,
      loketId: loketId, // Optional loketId
      loket: loket,     // Optional loket name
      calledByAdmin: false,
      penanggungJawab: (isFastTrack && fastTrackData) ? fastTrackData.penanggungJawab : null,
      alasanFastTrack: (isFastTrack && fastTrackData) ? fastTrackData.alasanFastTrack : null,
    };

    // Auto-assign Loket ID if not provided, based on Clinic Mapping
    // fulfill requirement: "adjust based on loket id depending on creation"
    if (!newPatient.loketId && newPatient.kodeKlinik) {
       const allLokets = loketStore.lokets || [];
       const targetLoket = allLokets.find(l => 
         l.pelayanan && Array.isArray(l.pelayanan) && l.pelayanan.includes(newPatient.kodeKlinik)
       );
       
       if (targetLoket) {
         newPatient.loketId = targetLoket.id;
         if (!newPatient.loket) newPatient.loket = targetLoket.namaLoket;
         console.log(`✅ Auto-assigned Onsite Ticket ${newPatient.noAntrian} to Loket ${targetLoket.id} (${targetLoket.namaLoket})`);
       }
    }

    allPatients.value.push(newPatient);
    incrementBarcodeCounter();

    return {
      success: true,
      message: `Pendaftaran ${clinic.name || clinic} berhasil diproses.`,
      patient: newPatient,
    };
  };

  /**
   * Register REGULER patient via API
   * POST to http://10.10.123.140:8089/api/v1/tiket/generate
   */
  const registerRegulerPatientViaApi = async (clinic, paymentType, visitType = 'SEKARANG', isFastTrack = false, fastTrackData = null) => {
    try {
      console.log('🔄 [queueStore] Generating ticket via API for REGULER patient...');
    console.log('📋 [queueStore] Payment Type:', paymentType, '| Clinic:', clinic.name, '| Clinic Code:', clinic.kode);
    
    // 1. Find appropriate idloket based on BOTH clinic code AND payment type
    const allLokets = loketStore.lokets || [];
    
    // Determine payment type for matching (normalize BPJS to JKN for API compatibility)
    const paymentTypeForMatching = paymentType === "BPJS" ? "JKN" : paymentType;
    
    console.log('🔍 [queueStore] Searching lokets...');
    console.log('  Total lokets:', allLokets.length);
    console.log('  API lokets (id < 1000):', allLokets.filter(l => l.source === 'api' && l.id < 1000).length);
    console.log('  Looking for clinic:', clinic.kode, 'payment:', paymentTypeForMatching);
    
    // Find loket that handles BOTH the clinic AND the payment type
    const targetLoket = allLokets.find(l => {
      // Check source: only use API lokets (id < 1000), not local eksekutif
      if (l.source !== 'api' || l.id >= 1000) return false;
      
      // Check if loket handles the clinic
      const handlesClinic = l.pelayanan && Array.isArray(l.pelayanan) && l.pelayanan.includes(clinic.kode);
      
      if (handlesClinic) {
        // Debug: This loket handles the clinic, now check payment
        console.log(`  ✓ Loket ${l.id} (${l.namaLoket}) handles clinic ${clinic.kode}`);
        console.log(`    Pembayaran array:`, l.pembayaran);
        
        // NEW: Use isPaymentCompatible helper for robust payment matching
        const acceptsPayment = isPaymentCompatible(paymentTypeForMatching, l.pembayaran);
        console.log(`    Accepts payment ${paymentTypeForMatching}:`, acceptsPayment);
        
        if (acceptsPayment) {
          console.log(`  ✅ MATCH FOUND: Loket ${l.id}`);
          return true;
        }
      }
      
      return false;
    });
    
    // BLOCK REGISTRATION: No fallback to loket 2
    if (!targetLoket) {
      console.error(`❌ [queueStore] No loket found for clinic ${clinic.kode} (${clinic.name}) with payment ${paymentTypeForMatching}`);
      console.warn(`⚠️ [queueStore] Available lokets:`, allLokets.filter(l => l.source === 'api').map(l => ({
        id: l.id, 
        nama: l.namaLoket, 
        pelayanan: l.pelayanan, 
        pembayaran: l.pembayaran
      })));
      
      return { 
        success: false, 
        message: `Maaf, klinik "${clinic.name}" dengan pembayaran "${paymentTypeForMatching}" belum tersedia. Silakan hubungi petugas pendaftaran.`
      };
    }
    
    console.log('🎯 [queueStore] Target Loket:', `${targetLoket.namaLoket} (ID: ${targetLoket.id})`, '| Accepts Payment:', targetLoket.pembayaran);
    
    // 2. Determine idpembayaran based on paymentType
    const idloket = String(targetLoket.id);
    // BPJS (JKN) = "2", UMUM and others = "1"
    const idpembayaran = paymentType === "BPJS" ? "2" : "1";
      
      // 3. Prepare API Body
      const body = {
        idloket: idloket,
        dokter: "",
        idklinik: String(clinic.id),
        namaklinik: clinic.name,
        idpembayaran: idpembayaran,
        statuspasien: "1",
        statuspasien2: "2",
        idklinikstatus: "1"
      };

      console.log('📤 [queueStore] API Body:', body);

      // 3. Call API
      const response = await fetch('http://10.10.123.140:8089/api/v1/tiket/generate', {
        method: 'POST',
        headers: {
          'Content-Type': 'application/json'
        },
        body: JSON.stringify(body)
      });

      if (!response.ok) {
        throw new Error(`API error! status: ${response.status}`);
      }

      const result = await response.json();
      console.log('📥 [queueStore] API Response:', result);

      if (result.metadata && result.metadata.code !== 200) {
        return { success: false, message: result.message || 'Gagal generate barcode via API' };
      }

      const apiData = result.data;
      if (!apiData || !apiData.barcode) {
        return { success: false, message: 'Data barcode tidak valid dari API' };
      }

      // 4. Map API response to Store Format
      const timestamp = new Date();
      const newNo = allPatients.value.length > 0 
        ? Math.max(...allPatients.value.map(p => p.no)) + 1 
        : 1;

      // Gunakan waktu dari API jika tersedia
      const jamPanggil = apiData.waktutiket || 
        `${String(timestamp.getHours()).padStart(2, "0")}:${String(timestamp.getMinutes()).padStart(2, "0")}`;

      const barcode = apiData.barcode;
      // Penomoran tiket menggunakan "ticket" dari API
      const queueNumber = apiData.ticket || apiData.barcode;
      const finalQueueNumber = isFastTrack ? `F-${queueNumber}` : queueNumber;
      const noAntrian = `${finalQueueNumber} | Onsite - ${barcode}`;

      const newPatient = {
        no: newNo,
        jamPanggil: jamPanggil,
        barcode: barcode,
        noAntrian: noAntrian,
        shift: apiData.shift || 'Shift 1',
        klinik: clinic.name,
        kodeKlinik: clinic.kode,
        fastTrack: isFastTrack ? "YA" : "TIDAK",
        pembayaran: paymentType,
        noRM: `RM-${barcode.slice(-6)}`,
        status: 'menunggu',
        processStage: "loket",
        createdAt: apiData.tangaltiket ? `${apiData.tangaltiket}T${apiData.waktutiket || '00:00:00'}` : timestamp.toISOString(),
        registrationType: 'onsite',
        visitType: visitType,
        visitDate: apiData.tangaltiket || timestamp.toISOString().substring(0, 10),
        namaDokter: null,
        loketId: parseInt(idloket),
        loket: targetLoket ? targetLoket.namaLoket : null,
        calledByAdmin: false,
        penanggungJawab: (isFastTrack && fastTrackData) ? fastTrackData.penanggungJawab : null,
        alasanFastTrack: (isFastTrack && fastTrackData) ? fastTrackData.alasanFastTrack : null,
        // Additional API fields
        idvisit: 1, // Default menunggu
        idtiket: apiData.id
      };

      allPatients.value.push(newPatient);
      // NOTE: Kita tidak perlu incrementBarcodeCounter() di sini karena barcode berasal dari API
      
      console.log(`✅ [queueStore] Successfully registered REGULER patient via API: ${noAntrian}`);

      return {
        success: true,
        message: `Pendaftaran ${clinic.name} berhasil via API.`,
        patient: newPatient
      };

    } catch (error) {
      console.error('❌ [queueStore] Error generating ticket via API:', error);
      return {
        success: false,
        message: `Gagal pendaftaran API: ${error.message}`
      };
    }
  };


  /**
   * Check-in patient via API
   * POST to http://10.10.123.140:8089/api/v1/tiket/checkin
   */
  const checkInPatientViaApi = async (barcode) => {
    try {
      console.log(`🔄 [queueStore] Syncing check-in via API for barcode: ${barcode}...`);
      
      const body = {
        barcode: barcode,
        statuspasien: "5",
        statuspasien2: "6",
        idklinikstatus: "1",
        idklinikstatus2: "2"
      };

      console.log('📤 [queueStore] Check-in API Body:', body);

      const response = await fetch('http://10.10.123.140:8089/api/v1/tiket/checkin', {
        method: 'POST',
        headers: {
          'Content-Type': 'application/json'
        },
        body: JSON.stringify(body)
      });

      if (!response.ok) {
        throw new Error(`API error! status: ${response.status}`);
      }

      const result = await response.json();
      console.log('📥 [queueStore] Check-in API Response:', result);

      return {
        success: true,
        data: result
      };

    } catch (error) {
      console.error('❌ [queueStore] Error syncing check-in via API:', error);
      return {
        success: false,
        message: `Gagal sync check-in API: ${error.message}`
      };
    }
  };


  // Check-in patient (update status from anjungan to di-loket)
  // IMPORTANT: Hanya menggunakan EXACT barcode match untuk menghindari false positive
  // Format barcode: YYMMDD + 5 digit (contoh: 26011500001)
  // Jangan gunakan fallback ke noAntrian atau no karena bisa menyebabkan false positive
  const checkInPatient = async (patientIdOrBarcode) => {
    console.log('🔍 checkInPatient called with:', patientIdOrBarcode);
    console.log('📊 Total patients in store:', allPatients.value.length);
    
    // Clean input - remove whitespace and normalize
    const cleanInput = String(patientIdOrBarcode).trim();
    
    // PRIORITAS: Hanya cari dengan EXACT barcode match (case-insensitive, whitespace-insensitive)
    // Format barcode: YYMMDD + 5 digit (contoh: 26011500001)
    // Ini adalah satu-satunya cara yang aman untuk match pasien
    const patientIndex = allPatients.value.findIndex(p => {
      // Normalize barcode untuk comparison
      const patientBarcode = String(p.barcode || '').trim();
      
      // EXACT barcode match (case-insensitive, whitespace-insensitive)
      // Ini adalah satu-satunya cara yang aman untuk match pasien
      if (patientBarcode === cleanInput || 
          patientBarcode.toLowerCase() === cleanInput.toLowerCase()) {
        console.log('✅ Found by exact barcode match:', patientBarcode, '===', cleanInput);
        return true;
      }
      
      return false;
    });
    
    if (patientIndex === -1) {
      console.log('❌ Patient not found. Searched for barcode:', cleanInput);
      console.log('📋 Available barcodes (first 10):', allPatients.value.slice(0, 10).map(p => ({
        no: p.no,
        barcode: p.barcode,
        noAntrian: p.noAntrian?.split(' |')[0]
      })));
      return { success: false, message: `Pasien dengan barcode ${cleanInput} tidak ditemukan. Pastikan barcode benar (format: YYMMDD + 5 digit, contoh: 26011500001).` };
    }

    // IMPORTANT: Get fresh patient data from array to avoid stale data
    // Use the patientIndex we found earlier, but get fresh data from array
    const patient = allPatients.value[patientIndex];
    console.log('✅ Patient found (fresh):', {
      no: patient.no,
      barcode: patient.barcode,
      noAntrian: patient.noAntrian,
      status: patient.status,
      processStage: patient.processStage
    });
    
    // Only allow check-in if status is anjungan (sudah dipanggil) or pending
    // Pasien dengan status "menunggu" belum bisa check-in (belum dipanggil)
    if (patient.status === 'menunggu') {
      console.log('⚠️ Patient status is menunggu (belum dipanggil):', patient.status);
      return { 
        success: false, 
        message: `Pasien belum dipanggil oleh admin loket. Mohon menunggu hingga nomor antrean Anda dipanggil.` 
      };
    }
    
    // Check if already checked in
    if (patient.status === 'di-loket') {
      console.log('⚠️ Patient already checked in:', patient.status);
      return { 
        success: false, 
        message: `Pasien sudah melakukan check-in sebelumnya. Status saat ini: ${patient.status}` 
      };
    }
    
    if (patient.status !== 'anjungan' && patient.status !== 'pending') {
      console.log('⚠️ Patient status is not anjungan/pending:', patient.status);
      return { 
        success: false, 
        message: `Pasien tidak dapat check-in. Status saat ini: ${patient.status}` 
      };
    }

    // Update status to di-loket using the patientIndex
    allPatients.value[patientIndex] = {
      ...allPatients.value[patientIndex],
      status: "di-loket",
    };
    syncApiPatientStatus(allPatients.value[patientIndex], "di-loket");
    
    console.log('✅ Check-in successful locally, patient status updated to di-loket');

    // Sync to API
    const apiSyncResult = await checkInPatientViaApi(allPatients.value[patientIndex].barcode);
    if (!apiSyncResult.success) {
      console.warn('⚠️ Check-in API sync failed:', apiSyncResult.message);
      // We still return true because local state is updated, but we could also return failure if critical
    }

    return {
      success: true,
      message: `Check-in berhasil. Pasien ${patient.noAntrian.split(" |")[0]} siap diproses di loket.`,
      patient: allPatients.value[patientIndex],
    };
  };

  // FIX: Explicit implementation of processNextQueue to target ONLY 'di-loket' patients
  const processNextQueueCorrected = (adminType = 'loket', specificId = null) => {
    // 1. Determine target stage and key
    const stageMap = { 'loket': 'loket', 'klinik': 'klinik', 'penunjang': 'penunjang' };
    const targetStage = stageMap[adminType];
    const key = specificId ? `${adminType}-${specificId}` : adminType;

    console.log(`🚀 [processNextQueue] Processing for ${key} (Stage: ${targetStage})`);

    // 2. Find next patient ONLY from 'di-loket' status
    // These are patients who have already checked in or been moved to 'di-loket'
    const eligiblePatients = allPatients.value.filter(p => 
      p.status === 'di-loket' && 
      p.processStage === targetStage &&
      (adminType !== 'loket' || String(p.loketId) === String(specificId))
    );

    // Exclude the current processing patient to find the TRULY next one
    const currentNum = currentProcessingPatient.value[key]?.no;
    const waitingDiLoket = eligiblePatients.filter(p => p.no !== currentNum);

    // 3. Sort by priority (Fast Track, then No)
    const sorted = sortPatientsForCalling(waitingDiLoket);
    const nextPatient = sorted[0];

    if (!nextPatient) {
      return { success: false, message: "Tidak ada antrean 'Di Loket' yang menunggu untuk diproses." };
    }

    // 4. Update Current Processing ISOLATED by key
    // We don't change status because it's already 'di-loket'
    // But we update the lastCalledAt and set it as current
    const index = allPatients.value.findIndex(p => p.no === nextPatient.no);
    if (index !== -1) {
      const updatedPatient = {
        ...allPatients.value[index],
        lastCalledAt: new Date().toISOString()
      };
      
      allPatients.value[index] = updatedPatient;
      currentProcessingPatient.value[key] = updatedPatient;
      
      return { 
        success: true, 
        message: `Memproses ${nextPatient.noAntrian.split(" |")[0]}`,
        patient: updatedPatient
      };
    }
    
    return { success: false, message: "Gagal memproses antrean." };
  };

  return {
    // State
    allPatients,
    resetPatients,
    ensureInitialData,
    quotaUsed,
    currentProcessingPatient,
    lastUpdated,
    lastFetchTime,
    lastGlobalFetchTime,
    kliniks,
    penunjangs,

    // API Patient State
    apiPatientsPerLoket,
    isLoadingPatients,
    apiPatientsError,

    // Computed
    totalPasien,

    // Actions
    callNext,
    callMultiplePatients,
    processPatient,
    callProcessingPatient,
    createAntreanKlinik,
    createAntreanPenunjang,
    createAntreanKlinikRuang,
    scanAndCreateAntreanKlinikRuang,
    getPatientsByKlinikRuang,
    callNextKlinikRuang,
    processPatientKlinikRuang,
    changeKlinik,
    pindahKlinikRuang,
    konsultasiKlinikRuang,
    processNextQueue: processNextQueueCorrected,
    getPatientsByStage,
    getTotalPasienByStage,
    getCurrentProcessing,
    setCurrentProcessing,
    registerPatientFromAnjungan,
    checkInPatient,
    generateQueueNumber,
    generateBarcode,
    incrementBarcodeCounter,
    syncCountersWithState,

    // API Patient Actions
    fetchPatientsForLoket,
    fetchPatientsForClinic,
    fetchAllPatients,
    getPatientsForLoket,
    mapStatusFromDeskripsi,
    mapApiPatientToStoreFormat,
    registerRegulerPatientViaApi,
    checkInPatientViaApi,
    checkAndResetDaily,
    isTodayPatient,
    getResetThreshold,
    initWebSocket,
    disconnectWebSocket,
    isWsConnected,
    sendViaPost,
    registerInterest,
    unregisterInterest,
    activeLoketInterest,
    registerClinicInterest,
    unregisterClinicInterest,
    activeClinicInterest,
    registerGlobalInterest,
    unregisterGlobalInterest,
  };

}, {
  persist: {
    key: 'queue-store-state',
    storage: typeof window !== 'undefined' ? localStorage : undefined,
    paths: ['allPatients', 'quotaUsed', 'currentProcessingPatient', 'apiPatientsPerLoket', 'lastUpdated'],
    serializer: {
      deserialize: JSON.parse,
      serialize: JSON.stringify,
    },
    restore: (value) => {
      // Ensure allPatients is always an array
      if (value && value.allPatients && !Array.isArray(value.allPatients)) {
        value.allPatients = [];
      }
      return value;
    },
  },
});
