<?php





session_start();

if (isset($_SESSION['iduser'])) {



} else {

    // Redirige a index.php si la sesión no está iniciada

    header('Location: index.php');

    return;



    include('php/config/conexion.php');

  }

?>









<!DOCTYPE html>

<html lang="es">

<head>

  <meta charset="UTF-8">

  <meta name="viewport" content="width=device-width, initial-scale=1.0">

  <title>Autenticador 2FA</title>

  <link href="https://cdn.jsdelivr.net/npm/bootstrap@5.3.3/dist/css/bootstrap.min.css" rel="stylesheet">

  <link href="https://cdn.jsdelivr.net/npm/bootstrap-icons/font/bootstrap-icons.css" rel="stylesheet">

  <style>

    body {

      background-color: #121212;

      color: white;

      font-family: 'Segoe UI', sans-serif;

    }

    .bfa_account-list {

      display: grid;

      grid-template-columns: repeat(auto-fill, minmax(250px, 1fr));

      gap: 15px;

    }

    .bfa_account {

      padding: 15px;

      border-radius: 8px;

      box-shadow: 0 4px 6px rgba(0, 0, 0, 0.1);

      display: flex;

      flex-direction: column;

      gap: 10px;

      color: black !important;

    }

    .bfa_timer {

      width: 30px;

      height: 30px;

      border-radius: 50%;

      position: relative;

    }

    .bfa_timer::before {

      content: '';

      width: 100%;

      height: 100%;

      border-radius: 50%;

      position: absolute;

      top: 0;

      left: 0;

      background: conic-gradient(#007bff calc(var(--time) * 1%), #ddd calc(var(--time) * 1%));

      transition: background 0.5s;

    }

    .bfa_otp-code {

      font-size: 24px;

      color: #007bff;

      cursor: pointer;

    }

    .header-info {

      background-color: #181818;

      padding: 20px;

      border-radius: 10px;

      margin-bottom: 25px;

      box-shadow: 0 0 10px rgba(0,0,0,0.3);

    }

    .header-info h2 {

      font-size: 1.5rem;

      margin-bottom: 5px;

    }

    .header-info p {

      font-size: 0.95rem;

    }

    .header-info i {

      margin-right: 5px;

    }



    .toast-body {

    padding: var(--bs-toast-padding-x);

    word-wrap: break-word;

    background: #198754;

    border-radius: 5px;

}



.d-flex-sms {

    display: flex !important;

    margin-top: 24px;

    width: 194px;

    background: #198754;

    border-radius: 5px;

}







@keyframes pulse-gubernamental {

  0% {

    background-color: #0d6efd;

  }

  50% {

    background-color: #0a58ca;

  }

  100% {

    background-color: #0d6efd;

  }

}



.btn-gubernamental {

  animation: pulse-gubernamental 2s infinite ease-in-out;

  color: white;

  pointer-events: none;

}







@keyframes blink-gubernamental {

  0%, 100% {

    background-color: #0d6efd;

  }

  50% {

    background-color: #084298;

  }

}



.btn-gubernamental {

  animation: blink-gubernamental 1.5s infinite ease-in-out;

  color: white;

  pointer-events: none;

}



  </style>

</head>

<body>

<div class="container py-4">

  <div class="header-info text-white" id="headerInfo">

    <h2 class="clas2fa">Autenticador 2FA | Cuentas disponibles: 0</h2>

    <p class="mb-0">

      <i class="bi bi-check-circle-fill text-success"></i> Usadas: 0 |

      <i class="bi bi-shield-fill text-info"></i> Disponibles: 0 |

      <i class="bi bi-person-fill text-secondary"></i> Naturales (01): 0 |

      <i class="bi bi-building text-warning"></i> Jurídicas (04): 0 |

      <i class="bi bi-bank text-danger"></i> Gubernamentales (10): 0

    </p>

  </div>



  <div class="row text-center text-white mb-4" id="summaryCards">

    <div class="col-md-3">

      <div class="bg-success rounded p-2 shadow"><i class="bi bi-check-circle"></i> Usadas: <span id="sum_usadas">0</span></div>

    </div>

    <div class="col-md-3">

      <div class="bg-info rounded p-2 shadow"><i class="bi bi-shield-fill"></i> Disponibles: <span id="sum_disponibles">0</span></div>

    </div>

    <div class="col-md-2">

      <div class="bg-secondary rounded p-2 shadow"><i class="bi bi-person-fill"></i> Naturales: <span id="sum_naturales">0</span></div>

    </div>

    <div class="col-md-2">

      <div class="bg-warning rounded p-2 shadow"><i class="bi bi-building"></i> Jurídicas: <span id="sum_juridicas">0</span></div>

    </div>

    <div class="col-md-2">

      <div class="bg-danger rounded p-2 shadow"><i class="bi bi-bank"></i> Gubernamentales: <span id="sum_gubernamentales">0</span></div>

    </div>

  </div>



  <form id="addAccountForm" class="row g-3 mb-4">

    <div class="col-md-3">

      <label class="form-label text-white" for="bfa_name">Nombre</label>

      <input type="password" class="form-control bg-dark text-white" id="bfa_name" disabled="true" autocomplete="off" value="<?php echo $_SESSION["nombre"]; ?>">

    </div>

    <div class="col-md-3">

      <label class="form-label text-white" for="bfa_cuenta">Datos Cuenta</label>

      <input type="text" class="form-control bg-dark text-white" id="bfa_cuenta" required autocomplete="off">

    </div>

    <div class="col-md-3">

      <label class="form-label text-white" for="bfa_imagen">Imagen Seguridad</label>

      <input type="text" class="form-control bg-dark text-white" id="bfa_imagen" required autocomplete="off">

    </div>

    <div class="col-md-3">

      <label class="form-label text-white" for="bfa_secret">Código Secreto</label>

      <input type="text" class="form-control bg-dark text-white" id="bfa_secret" required autocomplete="off">

    </div>

    <div class="col-12">

      <button type="button" class="btn btn-success" onclick="addAccount()">Guardar Cuenta</button>

    </div>

  </form>



  <div id="bfa_accountList" class="bfa_account-list"></div>

</div>



<!-- Toast centrado superior -->

<div class="position-fixed top-0 start-50 translate-middle-x mt-3" style="z-index: 1100">

  <div id="toast-container" class="toast-container"></div>

</div>



<script>

  document.addEventListener("DOMContentLoaded", () => {

    loadAccounts();

  });



  function showToast(message, bg = 'bg-primary') {

    const container = document.getElementById('toast-container');

    const toast = document.createElement('div');

    toast.className = `toast align-items-center text-white ${bg} border-0 mb-2`;

    toast.role = 'alert';

    toast.innerHTML = `

      <div class="d-flex-sms">

        <div class="toast-body">${message}</div>

        <button type="button" class="btn-close btn-close-white me-2 m-auto" data-bs-dismiss="toast"></button>

      </div>

    `;

    container.appendChild(toast);

    const bsToast = new bootstrap.Toast(toast, { delay: 1000 });

    bsToast.show();

    toast.addEventListener('hidden.bs.toast', () => toast.remove());

  }



  function updateHeaderInfo(accounts) {

    const total = accounts.length;

    const usadas = accounts.filter(a => a.usada).length;

    const disponibles = total - usadas;

    const naturales = accounts.filter(a => a.cuenta.startsWith("01^")).length;

    const juridicas = accounts.filter(a => a.cuenta.startsWith("04^")).length;

    const gubernamentales = accounts.filter(a => a.cuenta.startsWith("10^")).length;



    const header = document.getElementById('headerInfo');

    header.innerHTML = `

      <h2 class="clas2fa">Autenticador 2FA | Cuentas disponibles: ${disponibles}</h2>

      <p class="mb-0">

        <i class="bi bi-check-circle-fill text-success"></i> Usadas: ${usadas} |

        <i class="bi bi-shield-fill text-info"></i> Disponibles: ${disponibles} |

        <i class="bi bi-person-fill text-secondary"></i> Naturales (01): ${naturales} |

        <i class="bi bi-building text-warning"></i> Jurídicas (04): ${juridicas} |

        <i class="bi bi-bank text-danger"></i> Gubernamentales (10): ${gubernamentales}

      </p>

    `;



    document.getElementById('sum_usadas').textContent = usadas;

    document.getElementById('sum_disponibles').textContent = disponibles;

    document.getElementById('sum_naturales').textContent = naturales;

    document.getElementById('sum_juridicas').textContent = juridicas;

    document.getElementById('sum_gubernamentales').textContent = gubernamentales;

  }



  function loadAccounts() {

    fetch('php/2fa/load_accounts.php')

      .then(res => res.json())

      .then(accounts => displayAccounts(accounts));

  }



  function displayAccounts(accounts) {



    updateHeaderInfo(accounts);

    const container = document.getElementById('bfa_accountList');

    container.innerHTML = '';

    accounts.forEach(account => {

      const otpCodeId = `bfa_otp-${account.id}`;

      const timerId = `bfa_timer-${account.id}`;

      const imgVisibility = account.cuenta.startsWith("04^") || account.cuenta.startsWith("10^") ? 'visible' : 'hidden';



      const div = document.createElement('div');

      div.className = 'bfa_account';

      div.style.backgroundColor = account.usada ? '#ffdddd' : 'white';

      div.innerHTML = `

        <div><strong><img class="img_jurid_tok" src="img/fire.gif" height="20" style="margin-top:-7px; visibility: ${imgVisibility};"> Cuenta: <b style="background:#5a07bc;border-radius:5px;padding:5px;color:white;">${account.id}</b> ${account.name}</strong></div>

        <div class="d-flex align-items-center">

          <div class="bfa_timer" id="${timerId}" style="--time: 100;"></div>

          <div class="bfa_otp-code ms-2" id="${otpCodeId}" onclick="copyToClipboard('${otpCodeId}')">--</div>

         ${account.cuenta.startsWith('10^') 

          ? '<button class="btn btn-sm btn-gubernamental ms-2">GUBERNAMENTAL</button>' 

          : `<button class="btn btn-sm btn-outline-primary ms-2" onclick="updateTotpManual('${account.secret}', '${otpCodeId}', '${timerId}')">Generar Código</button>`}





        </div>

        <div><small><strong>IMG:</strong> ${account.imagen}</small></div>

        <div class="d-flex gap-2">

          <button class="btn btn-sm btn-success" onclick="copyCuenta('${account.cuenta}')">ACC</button>

          <button class="btn btn-sm btn-secondary" onclick="usedAccount(${account.id})">USED</button>

          <button class="btn btn-sm btn-danger" onclick="deleteAccount(${account.id})">X</button>

          <button class="btn btn-sm btn-warning" onclick="openImageUpdate(${account.id})">Actualizar Imagen</button>

        </div>`;

      container.appendChild(div);

      startTotp(account.secret, otpCodeId, timerId);

      updateTotp(account.secret, otpCodeId, timerId); // <-- Esta línea agrega la generación automática del código

    });

  }



  function addAccount() {

    const name = document.getElementById('bfa_name').value.trim();

    const cuenta = document.getElementById('bfa_cuenta').value.trim();

    const imagen = document.getElementById('bfa_imagen').value.trim();

    const secret = document.getElementById('bfa_secret').value.trim();

    if (!name || !cuenta || !imagen || !secret) return showToast('Todos los campos son obligatorios', 'bg-danger');

    fetch('php/2fa/save_account.php', {

      method: 'POST',

      headers: { 'Content-Type': 'application/x-www-form-urlencoded' },

      body: `name=${encodeURIComponent(name)}&cuenta=${encodeURIComponent(cuenta)}&imagen=${encodeURIComponent(imagen)}&secret=${encodeURIComponent(secret)}`

    }).then(res => res.text()).then(text => showToast(text, 'bg-success')).then(() => loadAccounts());

  }



  function copyCuenta(cuenta) {

    navigator.clipboard.writeText(cuenta).then(() => showToast('Cuenta copiada', 'bg-success'));

  }



  function copyToClipboard(id) {

    const text = document.getElementById(id).textContent;

    if (text && text !== '--') {

      navigator.clipboard.writeText(text).then(() => showToast('Código copiado', 'bg-success'));

    }

  }



  function deleteAccount(id) {

    if (!confirm("¿Eliminar cuenta?")) return;

    fetch('php/2fa/delete_account.php', {

      method: 'POST',

      headers: { 'Content-Type': 'application/x-www-form-urlencoded' },

      body: `id=${id}`

    }).then(res => res.text()).then(text => showToast(text, 'bg-danger')).then(() => loadAccounts());

  }



  function usedAccount(id) {

    fetch('php/2fa/mark_used.php', {

      method: 'POST',

      headers: { 'Content-Type': 'application/x-www-form-urlencoded' },

      body: `id=${id}`

    }).then(res => res.json()).then(res => {

      if (res.success) loadAccounts();

      else showToast(res.message, 'bg-warning');

    });

  }



  function updateTotp(secret, codeId, timerId) {

    generateTotp(secret).then(code => {

      document.getElementById(codeId).textContent = code;

    });

  }



function startTotp(secret, codeId, timerId) {

  let lastInterval = -1;

  setInterval(() => {

    const epoch = Math.floor(Date.now() / 1000);

    const remaining = 30 - (epoch % 30);

    const currentInterval = Math.floor(epoch / 30);



    document.getElementById(timerId).style.setProperty('--time', (remaining / 30) * 100);



    if (currentInterval !== lastInterval) {

      updateTotp(secret, codeId, timerId);

      lastInterval = currentInterval;

    }

  }, 1000);

}





  function updateTotpManual(secret, codeId, timerId) {

    updateTotp(secret, codeId, timerId);

  }



  function generateTotp(secret) {

    const epoch = Math.floor(Date.now() / 1000);

    const time = Math.floor(epoch / 30);

    const key = base32toHex(secret);

    const timeHex = time.toString(16).padStart(16, '0');

    return crypto.subtle.importKey('raw', hexToArrayBuffer(key), { name: 'HMAC', hash: 'SHA-1' }, false, ['sign'])

      .then(key => crypto.subtle.sign('HMAC', key, hexToArrayBuffer(timeHex)))

      .then(hmac => {

        const offset = new Uint8Array(hmac)[hmac.byteLength - 1] & 0xf;

        const binCode = (new DataView(hmac).getUint32(offset) & 0x7fffffff) % 1000000;

        return binCode.toString().padStart(6, '0');

      });

  }



  function base32toHex(base32) {

    const chars = 'ABCDEFGHIJKLMNOPQRSTUVWXYZ234567';

    let bits = '';

    for (const char of base32.replace(/=+$/, '').toUpperCase()) {

      bits += chars.indexOf(char).toString(2).padStart(5, '0');

    }

    return bits.match(/.{1,4}/g).map(b => parseInt(b, 2).toString(16)).join('');

  }



  function hexToArrayBuffer(hex) {

    const bytes = new Uint8Array(hex.length / 2);

    for (let i = 0; i < bytes.length; i++) {

      bytes[i] = parseInt(hex.substr(i * 2, 2), 16);

    }

    return bytes.buffer;

  }



  function openImageUpdate(id) {

    const desc = prompt("Nueva descripción de imagen:");

    if (!desc) return;



    const formData = new FormData();

    formData.append('id', id);

    formData.append('description', desc);



    fetch('php/2fa/update_image.php', {

      method: 'POST',

      body: formData

    })

    .then(res => res.json())

    .then(data => {

      if (data.success) {

        showToast("Imagen actualizada correctamente", 'bg-success');

        loadAccounts();

      } else {

        showToast("Error al actualizar: " + data.message, 'bg-danger');

      }

    })

    .catch(err => showToast("Error al enviar solicitud", 'bg-danger'));

  }

</script>

<script src="https://cdn.jsdelivr.net/npm/bootstrap@5.3.3/dist/js/bootstrap.bundle.min.js"></script>

</body>

</html>

