<template>
  <header class="topbar">
    <div class="title">RADIUS Nucleo</div>
    <div class="actions">
      <a class="icon-btn" href="https://inicio.nucleoriofrio.com">
        🏠 Inicio
      </a>
      <button class="icon-btn" @click="toggleTheme">
        <img class="icon" :src="theme === 'light' ? '/icons/moon.svg' : '/icons/sun.svg'" alt="theme">
      </button>
      <span class="chip"><span class="muted">Estado:</span> {{ statusText }}</span>
      <button class="icon-btn" @click="openAddUser">
        <img class="icon" src="/icons/user-plus.svg" alt="usuario"> Usuario
      </button>
      <button class="icon-btn" @click="openAddGuest">
        <img class="icon" src="/icons/guest.svg" alt="invitado"> Invitado
      </button>
      <div class="dropdown">
        <button class="icon-btn" @click="toggleSettingsMenu">
          <img class="icon" src="/icons/settings.svg" alt="config"> Configuración
        </button>
        <div v-if="showSettingsMenu" class="menu">
          <button @click="openRawDb">ver rawDB</button>
          <button @click="openVlanForm">crear VLAN</button>
          <hr/>
          <a class="icon-btn" href="/api/users.csv">Exportar usuarios CSV</a>
          <a class="icon-btn" href="/api/devices.csv">Exportar dispositivos CSV</a>
          <a class="icon-btn" href="/api/vlans.csv">Exportar VLANs CSV</a>
          <button class="icon-btn" @click="openImport('users')">Importar usuarios CSV</button>
          <button class="icon-btn" @click="openImport('devices')">Importar dispositivos CSV</button>
          <button class="icon-btn" @click="openImport('vlans')">Importar VLANs CSV</button>
        </div>
      </div>
    </div>
  </header>

  <section class="shell">
    <!-- Sidebar: Eventos -->
    <aside class="panel" :class="{ collapsed: sidebarCollapsed }">
      <div class="panel-header">
        <div class="panel-title">Eventos FreeRADIUS</div>
        <div class="panel-actions">
          <span class="chip">Página {{ reqPage+1 }} / {{ Math.max(1, Math.ceil(filteredRequestsAll.length / pageSize)) }}</span>
          <button class="icon-btn" @click="reqPage=Math.max(0, reqPage-1)">Anterior</button>
          <button class="icon-btn" @click="reqPage=Math.min(Math.ceil(filteredRequestsAll.length/pageSize)-1, reqPage+1)">Siguiente</button>
          <button class="icon-btn" title="Filtrar" @click="showEventFilters = true"><img class="icon" src="/icons/filter.svg" alt="filtrar"></button>
          <button class="icon-btn" title="Limpiar" @click="clearRequests"><img class="icon" src="/icons/clear.svg" alt="limpiar"></button>
          <button class="icon-btn" title="Test" @click="selfTest"><img class="icon" src="/icons/test.svg" alt="test"></button>
          <a class="icon-btn" title="Descargar" href="/api/requests.csv" target="_blank"><img class="icon" src="/icons/download.svg" alt="descargar"></a>
          <button class="icon-btn" title="Copiar" @click="copyRequests"><img class="icon" src="/icons/copy.svg" alt="copiar"></button>
          <button class="icon-btn" title="Colapsar" @click="sidebarCollapsed = !sidebarCollapsed">{{ sidebarCollapsed ? 'Expandir' : 'Colapsar' }}</button>
        </div>
      </div>
      <div class="scroll">
        <div v-if="loading.requests" class="muted">Cargando eventos…</div>
        <EventCard v-for="ev in pagedRequests" :key="ev.id" :ev="ev" />
      </div>
    </aside>

    <!-- Main: Usuarios -->
    <main class="panel" :class="{ collapsed: mainCollapsed }">
      <div class="panel-header">
        <div class="row">
          <div class="panel-title">Usuarios y Dispositivos</div>
          <span class="spacer"></span>
          <span class="chip" v-if="layoutMode==='user'">Página {{ userPage+1 }} / {{ Math.max(1, Math.ceil(filteredUsersAll.length / pageSize)) }}</span>
          <span class="chip" v-else>Página {{ devicePage+1 }} / {{ Math.max(1, Math.ceil(devicesAll.length / pageSize)) }}</span>
          <button class="icon-btn" @click="layoutMode==='user' ? (userPage=Math.max(0,userPage-1)) : (devicePage=Math.max(0,devicePage-1))">Anterior</button>
          <button class="icon-btn" @click="layoutMode==='user' ? (userPage=Math.min(Math.ceil(filteredUsersAll.length/pageSize)-1,userPage+1)) : (devicePage=Math.min(Math.ceil(devicesAll.length/pageSize)-1,devicePage+1))">Siguiente</button>
          <button class="icon-btn" title="Vista usuarios" @click="layoutMode='user'">
            <img class="icon" src="/icons/layout-users.svg" alt="usuarios"/> Usuarios
          </button>
          <button class="icon-btn" title="Vista dispositivos" @click="layoutMode='device'">
            <img class="icon" src="/icons/layout-devices.svg" alt="dispositivos"/> Dispositivos
          </button>
          <button class="icon-btn" title="Filtrar" @click="showUserFilters = true"><img class="icon" src="/icons/filter.svg" alt="filtro"/></button>
          <button class="icon-btn" title="Colapsar" @click="mainCollapsed = !mainCollapsed">{{ mainCollapsed ? 'Expandir' : 'Colapsar' }}</button>
        </div>
      </div>
      <div class="scroll">
        <div v-if="loading.users" class="muted">Cargando usuarios…</div>
        <template v-else>
          <div v-if="layoutMode==='user'" class="grid">
            <UserCard v-for="u in filteredUsers" :key="u.username" :item="u" :devicesById="devicesById"
              :expanded="!!userExpanded[u.username]"
              @toggle-expand="userExpanded[u.username] = !userExpanded[u.username]"
              @toggleDisable="toggleDisable" @remove="removeUser" @edit="openEditUser"
              @disconnect="disconnectUser" @edit-device="openDeviceForm" />
          </div>
          <div v-else class="grid">
            <DispositivoCard v-for="d in pagedDevices" :key="d.id" :device="d" :users="usersForDevice(d.id)" :devicesById="devicesById"
              :expanded="!!deviceExpanded[d.id]"
              @toggle-expand="deviceExpanded[d.id] = !deviceExpanded[d.id]"
              @edit="openDeviceForm" @disconnect="disconnectDevice" />
          </div>
        </template>
      </div>
    </main>
  </section>

  <!-- Modals -->
  <Modal :open="showEventFilters" title="Filtros de eventos" @close="showEventFilters=false">
    <div class="row" style="margin:8px 0;">
      <input v-model="eventFilters.text" placeholder="Buscar texto" class="toggle" style="flex:1;"/>
      <select v-model="eventFilters.type" class="toggle">
        <option value="">Todos</option>
        <option value="authorize">authorize</option>
        <option value="accounting">accounting</option>
        <option value="post-auth">post-auth</option>
        <option value="selftest">selftest</option>
        <option value="coa-disconnect">coa-disconnect</option>
      </select>
    </div>
  </Modal>

  <Modal :open="showUserFilters" title="Filtros de usuarios" @close="showUserFilters=false">
    <div class="row" style="margin:8px 0;">
      <input v-model="userFilters.text" placeholder="Buscar usuario" class="toggle" style="flex:1;"/>
      <select v-model="userFilters.status" class="toggle">
        <option value="">Todos</option>
        <option value="active">Activos</option>
        <option value="disabled">Deshabilitados</option>
      </select>
    </div>
  </Modal>

  <Modal :open="showUserForm" :title="userFormMode==='edit' ? 'Editar usuario' : (userFormMode==='guest' ? 'Agregar invitado' : 'Agregar usuario')"
    @close="showUserForm=false">
    <UserForm :model-value="userFormModel" :mode="userFormMode" @submit="handleUserFormSubmit" @cancel="showUserForm=false" />
  </Modal>

  <Modal :open="showRawDb" :fullscreen="rawDbFullscreen" title="Raw DB Viewer" @close="closeRawDb">
    <RawDbViewer @toggle-fullscreen="rawDbFullscreen = !rawDbFullscreen" />
  </Modal>

  <Modal :open="showVlan" title="Crear VLAN" @close="closeVlan">
    <VlanForm @success="onVlanCreated" @cancel="closeVlan" />
  </Modal>

  <Modal :open="showDevice" title="Editar dispositivo" @close="closeDevice">
    <DeviceForm :model="deviceFormModel" @success="onDeviceSaved" @cancel="closeDevice" />
  </Modal>

  <Modal :open="showImport" :title="'Importar ' + importKind + ' CSV'" @close="closeImport">
    <div class="column" style="gap:8px;">
      <div class="row" style="gap:8px; align-items:center; flex-wrap:wrap;">
        <input id="csvFile" ref="importFile" type="file" accept=".csv,text/csv" @change="onImportFile" style="display:none;" />
        <button class="icon-btn" @click="$refs.importFile.click()">Seleccionar archivo CSV</button>
        <span class="muted" v-if="importFilename">{{ importFilename }}</span>
      </div>
      <textarea v-model="importText" rows="10" placeholder="Pega CSV aquí o selecciona un archivo" style="width:100%; background:transparent; border: 1px solid rgba(255,255,255,.12); color: inherit; padding:8px;"></textarea>
      <div v-if="importError" class="muted" style="color:#ff6b6b;">{{ importError }}</div>
      <div class="modal-footer">
        <button class="icon-btn" @click="closeImport">Cancelar</button>
        <button class="icon-btn" @click="submitImport">Importar</button>
      </div>
    </div>
  </Modal>

  <!-- Toast System -->
  <Toast position="top-center" />
</template>

<script setup>
import { onMounted, reactive, ref, computed, watch } from 'vue';
import EventCard from './components/EventCard.js';
import UserCard from './components/UserCard.vue';
import DispositivoCard from './components/DispositivoCard.vue';
import Modal from './components/Modal.vue';
import UserForm from './components/UserForm.vue';
import RawDbViewer from './components/RawDbViewer.vue';
import VlanForm from './components/VlanForm.vue';
import DeviceForm from './components/DeviceForm.vue';
import Toast from './components/Toast.vue';
import { createToastSystem, useToast } from './composables/useToast.js';

// Initialize toast system
createToastSystem();
const { toast } = useToast();

const users = ref([]);
const requests = ref([]);
const loading = reactive({ users: false, requests: false });
const devices = ref([]);
const userExpanded = reactive({});
const deviceExpanded = reactive({});
// formulario inline removido: se usa modal con UserForm

const showEventFilters = ref(false);
const showUserFilters = ref(false);
const eventFilters = reactive({ text: '', type: '' });
const userFilters = reactive({ text: '', status: '' });
const sidebarCollapsed = ref(false);
const mainCollapsed = ref(false);
const layoutMode = ref('user');
const theme = ref(localStorage.getItem('theme') || 'dark');
const statusText = ref('OK');
const showSettingsMenu = ref(false);

async function fetchUsers() {
  loading.users = true;
  try {
    const res = await fetch('/api/users');
    const data = await res.json();
    users.value = data.items || [];
  } finally { loading.users = false; }
}

async function fetchRequests() {
  loading.requests = true;
  try {
    const res = await fetch('/api/requests');
    const data = await res.json();
    requests.value = data.items || [];
  } finally { loading.requests = false; }
}

async function fetchDevices() {
  try {
    const res = await fetch('/api/devices');
    const data = await res.json();
    devices.value = data.items || [];
  } catch {}
}

async function toggleDisable(u) {
  await fetch(`/api/users/${encodeURIComponent(u.username)}`, {
    method: 'PATCH',
    headers: { 'Content-Type': 'application/json' },
    body: JSON.stringify({ disabled: !u.disabled })
  });
  await fetchUsers();
}

async function removeUser(u) {
  if (!confirm(`Eliminar ${u.username}?`)) return;
  await fetch(`/api/users/${encodeURIComponent(u.username)}`, { method: 'DELETE' });
  await fetchUsers();
}

async function refreshRequests() { await fetchRequests(); }

async function clearRequests() {
  await fetch('/api/requests', { method: 'DELETE' });
  await fetchRequests();
}

async function selfTest() {
  await fetch('/test/radius', { method: 'POST' });
}

async function disconnectUser(u) {
  try {
    await fetch(`/api/users/${encodeURIComponent(u.username)}/disconnect`, { method: 'POST' });
    await Promise.all([fetchUsers(), fetchDevices()]);
  } catch {}
}

async function disconnectDevice(d) {
  try {
    await fetch(`/api/devices/${encodeURIComponent(d.id)}/disconnect`, { method: 'POST' });
    await Promise.all([fetchUsers(), fetchDevices()]);
  } catch {}
}

function setupSse() {
  const ev = new EventSource('/api/events');
  let refreshTimer = null;
  function scheduleRefresh() {
    if (refreshTimer) clearTimeout(refreshTimer);
    refreshTimer = setTimeout(async () => {
      await Promise.all([fetchUsers(), fetchDevices()]);
      refreshTimer = null;
    }, 1000);
  }
  ev.addEventListener('message', (e) => {
    try {
      const data = JSON.parse(e.data);
      if (data && data.ts) requests.value.push(data);
      const t = data && data.type;
      if (t === 'authorize' || t === 'post-auth' || t === 'accounting' || t === 'coa-disconnect') {
        scheduleRefresh();
      }
    } catch {}
  });
  ev.addEventListener('clear', () => { requests.value = []; });
}

onMounted(async () => {
  // Load persisted expand state
  try {
    const ue = JSON.parse(localStorage.getItem('ui_userExpanded') || '{}');
    Object.assign(userExpanded, ue && typeof ue === 'object' ? ue : {});
  } catch {}
  try {
    const de = JSON.parse(localStorage.getItem('ui_deviceExpanded') || '{}');
    Object.assign(deviceExpanded, de && typeof de === 'object' ? de : {});
  } catch {}
  await fetchUsers();
  await fetchDevices();
  await fetchRequests();
  setupSse();
  applyTheme();
  checkPWAStatus();
});

// PWA Detection and Toast
function checkPWAStatus() {
  // Don't show if already dismissed
  if (localStorage.getItem('pwa_toast_dismissed')) return;

  // Check if running in standalone mode (PWA installed and active)
  const isStandalone = window.matchMedia('(display-mode: standalone)').matches ||
                       window.navigator.standalone === true;

  // If NOT in standalone mode, user is in browser
  if (!isStandalone) {
    // Check if PWA can be installed
    let deferredPrompt = null;

    window.addEventListener('beforeinstallprompt', (e) => {
      e.preventDefault();
      deferredPrompt = e;

      // Show install prompt
      setTimeout(() => {
        toast.pwa('Instala RADIUS Nucleo como aplicación para una mejor experiencia', {
          title: '📱 Instalar Aplicación',
          position: 'top-center',
          duration: 0, // Persistent
          persistent: true,
          action: {
            label: 'Instalar',
            handler: async () => {
              if (deferredPrompt) {
                deferredPrompt.prompt();
                const { outcome } = await deferredPrompt.userChoice;
                if (outcome === 'accepted') {
                  localStorage.setItem('pwa_toast_dismissed', 'true');
                }
                deferredPrompt = null;
              }
            }
          }
        });
      }, 2000);
    });

    // If already installed but not in standalone, prompt to open in app
    if ('getInstalledRelatedApps' in navigator) {
      navigator.getInstalledRelatedApps().then((apps) => {
        if (apps.length > 0) {
          setTimeout(() => {
            toast.pwa('Abre RADIUS Nucleo en la aplicación para una mejor experiencia', {
              title: '📱 Abrir en App',
              position: 'top-center',
              duration: 10000,
              action: {
                label: 'Entendido',
                handler: () => {
                  localStorage.setItem('pwa_toast_dismissed', 'true');
                }
              }
            });
          }, 2000);
        }
      });
    }
  }
}

const filteredRequests = computed(() => {
  return requests.value.filter(ev => {
    if (eventFilters.type && ev.type !== eventFilters.type) return false;
    if (eventFilters.text) {
      const t = eventFilters.text.toLowerCase();
      const blob = JSON.stringify(ev).toLowerCase();
      if (!blob.includes(t)) return false;
    }
    return true;
  });
});

const filteredUsersAll = computed(() => {
  return users.value.filter(u => {
    if (userFilters.text && !u.username.toLowerCase().includes(userFilters.text.toLowerCase())) return false;
    if (userFilters.status === 'active' && u.disabled) return false;
    if (userFilters.status === 'disabled' && !u.disabled) return false;
    return true;
  });
});
const pageSize = 20;
const userPage = ref(0);
const filteredUsers = computed(() => filteredUsersAll.value.slice(userPage.value*pageSize, userPage.value*pageSize + pageSize));
watch([filteredUsersAll, () => layoutMode.value], () => { userPage.value = 0; });

const devicesById = computed(() => {
  const m = {};
  for (const d of devices.value) m[d.id] = d;
  return m;
});

function usersForDevice(id) {
  return users.value.filter(u => Array.isArray(u.dispositivos_utilizados) && u.dispositivos_utilizados.includes(id));
}

// Devices pagination
const devicesAll = computed(() => devices.value);
const devicePage = ref(0);
const pagedDevices = computed(() => devicesAll.value.slice(devicePage.value*pageSize, devicePage.value*pageSize + pageSize));
watch([devicesAll, () => layoutMode.value], () => { devicePage.value = 0; });

// Requests pagination (sidebar)
const filteredRequestsAll = computed(() => filteredRequests.value);
const reqPage = ref(0);
const pagedRequests = computed(() => filteredRequestsAll.value.slice(reqPage.value*pageSize, reqPage.value*pageSize + pageSize));
watch(filteredRequestsAll, () => { reqPage.value = 0; });

// Persist expansion state
watch(userExpanded, (v) => {
  try { localStorage.setItem('ui_userExpanded', JSON.stringify(v)); } catch {}
}, { deep: true });
watch(deviceExpanded, (v) => {
  try { localStorage.setItem('ui_deviceExpanded', JSON.stringify(v)); } catch {}
}, { deep: true });

function copyRequests() {
  const txt = JSON.stringify(requests.value, null, 2);
  navigator.clipboard?.writeText(txt);
}

function toggleTheme() {
  theme.value = theme.value === 'light' ? 'dark' : 'light';
  localStorage.setItem('theme', theme.value);
  applyTheme();
}
function applyTheme() {
  document.documentElement.classList.toggle('light', theme.value === 'light');
}

const showUserForm = ref(false);
const userFormMode = ref('create'); // 'create' | 'edit' | 'guest'
const userFormModel = ref({ username:'', password:'', vlan:'', disabled:false });

function openAddUser() {
  userFormMode.value = 'create';
  userFormModel.value = { username:'', password:'', vlan:'', disabled:false };
  showUserForm.value = true;
}
function openAddGuest() {
  userFormMode.value = 'guest';
  userFormModel.value = { username:'', password:'', vlan:'5', disabled:false, etiquetas: ['invitado'] };
  showUserForm.value = true;
}
function toggleSettingsMenu() { showSettingsMenu.value = !showSettingsMenu.value; }
const showRawDb = ref(false);
const rawDbFullscreen = ref(false);
function openRawDb() { showSettingsMenu.value = false; showRawDb.value = true; }
function closeRawDb() { showRawDb.value = false; }
const showVlan = ref(false);
function openVlanForm() { showSettingsMenu.value = false; showVlan.value = true; }
function closeVlan() { showVlan.value = false; }
function onVlanCreated() { showVlan.value = false; }
const showDevice = ref(false);
const deviceFormModel = ref({});
function openDeviceForm(d) { deviceFormModel.value = d; showDevice.value = true; }
function closeDevice() { showDevice.value = false; }
async function onDeviceSaved() { showDevice.value = false; await fetchDevices(); }

// Import CSV modal
const showImport = ref(false);
const importKind = ref('users');
const importText = ref('');
const importFilename = ref('');
const importError = ref('');
function openImport(kind){ importKind.value = kind; importText.value=''; showImport.value = true; }
function closeImport(){ showImport.value = false; }
async function submitImport(){
  importError.value = '';
  const txt = (importText.value || '').trim();
  if (!txt) { importError.value = 'El CSV está vacío.'; return; }
  // Basic header validation
  const headers = getCsvHeaders(txt);
  if (!headers.length) { importError.value = 'No se encontraron columnas en el CSV.'; return; }
  const need = (k)=>headers.includes(k);
  if (importKind.value === 'users') {
    const missing = ['username','password'].filter(c=>!need(c));
    if (missing.length) { importError.value = `CSV de usuarios requiere columnas: username,password. Faltantes: ${missing.join(', ')}`; return; }
  } else if (importKind.value === 'devices') {
    const missing = ['mac'].filter(c=>!need(c));
    if (missing.length) { importError.value = `CSV de dispositivos requiere columna: mac.`; return; }
  } else if (importKind.value === 'vlans') {
    const missing = ['id'].filter(c=>!need(c));
    if (missing.length) { importError.value = `CSV de VLANs requiere columna: id.`; return; }
  }
  try {
    const ep = importKind.value === 'users' ? '/api/users/import' : importKind.value === 'devices' ? '/api/devices/import' : '/api/vlans/import';
    await fetch(ep, { method:'POST', headers:{'Content-Type':'application/json'}, body: JSON.stringify({ csv: importText.value }) });
    showImport.value = false;
    await Promise.all([fetchUsers(), fetchDevices()]);
  } catch {}
}

function onImportFile(e){
  const f = e.target.files && e.target.files[0];
  if (!f) return;
  importFilename.value = f.name;
  const reader = new FileReader();
  reader.onload = () => { importText.value = String(reader.result || ''); };
  reader.readAsText(f, 'utf-8');
  e.target.value = '';
}

function getCsvHeaders(text){
  const lines = text.split(/\r?\n/).filter(l=>l.trim().length>0);
  if (!lines.length) return [];
  const line = lines[0];
  const out=[]; let cur=''; let q=false;
  for (let i=0;i<line.length;i++){
    const ch=line[i];
    if (ch==='"'){
      if (q && line[i+1]==='"'){ cur+='"'; i++; }
      else { q=!q; }
    } else if (ch===',' && !q){ out.push(cur.trim().toLowerCase()); cur=''; }
    else { cur+=ch; }
  }
  out.push(cur.trim().toLowerCase());
  return out;
}
function openSettings() { showSettingsMenu.value = !showSettingsMenu.value; }
function openEditUser(u) {
  userFormMode.value = 'edit';
  userFormModel.value = { username: u.username, password: u.password || '', vlan: u.vlan || '', disabled: !!u.disabled };
  showUserForm.value = true;
}

async function handleUserFormSubmit(data) {
  if (userFormMode.value === 'edit') {
    await fetch(`/api/users/${encodeURIComponent(userFormModel.value.username)}`, {
      method: 'PATCH', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify(data)
    });
  } else {
    await fetch('/api/users', { method: 'POST', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify(data) });
  }
  await fetchUsers();
  showUserForm.value = false;
}
</script>
