'use strict';

const fs = require('fs');
const path = require('path');
const logger = require('../utils/logger');

let io = null;
const CONFIG_PATH = process.env.CONFIG_PATH || path.join(__dirname, '..', '..', 'config');
const STORE_FILE = path.join(CONFIG_PATH, 'push-subscriptions.json');
const APP_NAME = process.env.APP_NAME || 'ARKHON Monitor';
const notifiedEvents = new Map();
const DEDUPE_TTL_MS = 10 * 60 * 1000;

function ensureStoreDir() {
  try { fs.mkdirSync(CONFIG_PATH, { recursive: true }); } catch (_) {}
}

function readStore() {
  try {
    ensureStoreDir();
    if (!fs.existsSync(STORE_FILE)) return [];
    const data = JSON.parse(fs.readFileSync(STORE_FILE, 'utf8'));
    return Array.isArray(data) ? data : [];
  } catch (err) {
    logger.warn({ event: 'push_store_read_error', message: err.message });
    return [];
  }
}

function writeStore(items) {
  try {
    ensureStoreDir();
    fs.writeFileSync(STORE_FILE, JSON.stringify(items, null, 2), 'utf8');
  } catch (err) {
    logger.warn({ event: 'push_store_write_error', message: err.message });
  }
}

function endpointOf(subscription) {
  return subscription && subscription.endpoint ? String(subscription.endpoint) : '';
}

function deviceIdOf(item = {}) {
  return clean(item.deviceId || item.meta?.deviceId);
}

function normalizeStoreItem(item = {}) {
  const subscription = item.subscription || item;
  const meta = item.meta || {};
  const endpoint = item.endpoint || meta.endpoint || endpointOf(subscription);
  const deviceId = deviceIdOf(item);
  const userAgent = item.userAgent || meta.userAgent || '';
  const platform = item.platform || meta.platform || '';
  const deviceName = item.deviceName || meta.deviceName || '';
  const lastSeenAt = item.lastSeenAt || item.lastSeen || meta.updatedAt || meta.createdAt || new Date().toISOString();
  return {
    endpoint,
    subscription,
    userId: item.userId || meta.userId || null,
    deviceId: deviceId || '',
    enabled: item.enabled !== false,
    lastSeenAt,
    lastSeen: lastSeenAt,
    userAgent,
    platform,
    deviceName,
    meta: {
      ...meta,
      deviceId: deviceId || '',
      userId: item.userId || meta.userId || null,
      userAgent,
      platform,
      deviceName,
      createdAt: meta.createdAt || new Date().toISOString(),
      updatedAt: meta.updatedAt || new Date().toISOString(),
    },
  };
}

function getVapidPublicKey() {
  return process.env.WEB_PUSH_PUBLIC_KEY || '';
}

function getWebPush() {
  let webpush;
  try { webpush = require('web-push'); } catch (_) { return null; }

  const publicKey = process.env.WEB_PUSH_PUBLIC_KEY;
  const privateKey = process.env.WEB_PUSH_PRIVATE_KEY;
  if (!publicKey || !privateKey) return null;

  const subject = process.env.WEB_PUSH_SUBJECT || `mailto:${process.env.SUPPORT_EMAIL || 'admin@arkhon.local'}`;
  try { webpush.setVapidDetails(subject, publicKey, privateKey); } catch (_) {}
  return webpush;
}

function init(socketIoInstance) {
  io = socketIoInstance;
  logger.info('Notificaciones en vivo inicializadas');
}

function money(n) {
  return '$' + Number(n || 0).toLocaleString('es-MX', { minimumFractionDigits: 2, maximumFractionDigits: 2 });
}

function clean(value) {
  return value === null || value === undefined ? '' : String(value).trim();
}

function eventDate(payload) {
  return payload.fecha || payload.createdAt || payload.data?.fecha || new Date().toISOString();
}

function stableEventId(type, payload) {
  const data = payload.data || {};
  const folio = clean(payload.folio || data.folio);
  const numcheque = clean(payload.numcheque || data.numcheque);
  const total = clean(payload.amount || payload.total || data.total || data.importe || data.venta_pagada);
  const fecha = clean(eventDate(payload));
  const idturno = clean(payload.idturno || data.idturno);
  const idmovto = clean(payload.idmovto || data.idmovto || data.movimiento?.folio || data.movimiento?.idmovto);
  const importe = clean(payload.amount || data.importe || data.movimiento?.importe);
  const movimiento = clean(payload.movimiento || data.movimiento?.folio || data.movimiento?.idmovto || fecha);

  if (type === 'cuenta_cerrada') return `cuenta:${folio}:${numcheque}:${total}:${fecha}`;
  if (type === 'venta_cerrada') return `venta:${folio}:${numcheque}:${total}:${fecha}`;
  if (type === 'retiro_caja') return `retiro:${idmovto}:${importe}:${fecha}`;
  if (type === 'deposito_caja') return `deposito:${idmovto}:${importe}:${fecha}`;
  if (type === 'corte_z') return `cortez:${idturno}:${fecha}`;
  if (type === 'turno_abierto') return `turno_abierto:${idturno}:${fecha}`;
  if (type === 'turno_cerrado') return `turno:${idturno}:${fecha}`;
  if (type === 'cortesia') return `cortesia:${folio}:${movimiento}:${importe}`;
  if (type === 'cancelacion_cuenta') return `cancelacion:${folio}:${fecha}`;
  if (type === 'producto_cancelado') return `producto_cancelado:${folio}:${clean(data.producto || payload.producto)}:${fecha}`;
  return `evento:${type}:${folio || idmovto || idturno}:${total || importe}:${fecha}`;
}

function eventTemplate(type, payload) {
  const data = payload.data || {};
  const amount = payload.amount ?? payload.total ?? data.total ?? data.importe ?? data.venta_pagada ?? data.movimiento?.importe;
  const folio = clean(payload.numcheque || data.numcheque || payload.folio || data.folio);
  const idturno = clean(payload.idturno || data.idturno);
  const motivo = clean(payload.motivo || data.motivo || data.concepto || data.movimiento?.concepto) || 'Sin motivo';
  const servicio = clean(payload.tipoServicio || data.tipoServicio || data.servicio) || 'Sin servicio';
  const producto = clean(payload.producto || data.producto) || 'Sin producto';
  const fondo = payload.fondo ?? data.fondo;

  const templates = {
    cuenta_cerrada: { title: 'Cuenta cerrada', body: folio ? `Cuenta ${folio} cerrada. Total pagado: ${money(amount)}.` : `Cuenta cerrada. Total pagado: ${money(amount)}.`, url: '/', priority: 'normal' },
    venta_cerrada: { title: 'Venta cerrada', body: `Venta registrada. Total pagado: ${money(amount)}. Servicio: ${servicio}.`, url: '/', priority: 'normal' },
    retiro_caja: { title: 'Retiro de caja', body: `Retiro registrado por ${money(amount)}. Motivo: ${motivo}.`, url: '/movimientos-caja', priority: 'alta' },
    deposito_caja: { title: 'Depósito de caja', body: `Depósito registrado por ${money(amount)}. Motivo: ${motivo}.`, url: '/movimientos-caja', priority: 'normal' },
    corte_z: { title: 'Corte Z generado', body: `Corte Z generado. Turno: ${idturno || 'sin turno'}. Venta pagada: ${money(amount)}.`, url: '/corte-z', priority: 'alta' },
    turno_abierto: { title: 'Turno abierto', body: `Turno ${idturno || 'sin turno'} abierto. Fondo de caja: ${money(fondo)}.`, url: '/', priority: 'normal' },
    turno_cerrado: { title: 'Turno cerrado', body: `Turno ${idturno || 'sin turno'} cerrado. Venta pagada: ${money(amount)}.`, url: '/corte-z', priority: 'alta' },
    cortesia: { title: 'Cortesía registrada', body: `Cortesía registrada por ${money(amount)}. Cuenta: ${folio || 'sin folio'}.`, url: '/cortesias', priority: 'alta' },
    cancelacion_cuenta: { title: 'Cuenta cancelada', body: `Cuenta ${folio || 'sin folio'} cancelada. Motivo: ${motivo}.`, url: '/cancelaciones', priority: 'alta' },
    producto_cancelado: { title: 'Producto cancelado', body: `Producto cancelado en cuenta ${folio || 'sin folio'}. Producto: ${producto}.`, url: '/productos-cancelados', priority: 'alta' },
  };
  return templates[type] || {
    title: 'Evento registrado',
    body: payload.body || 'Se registró un evento en ARKHON Monitor.',
    url: payload.url || '/',
    priority: payload.priority || 'normal',
  };
}

function normalizeLiveEvent(rawEvent = {}) {
  const now = new Date();
  const aliases = {
    retiro_efectivo: 'retiro_caja',
    deposito_efectivo: 'deposito_caja',
    cancelacion_producto: 'producto_cancelado',
  };
  const type = aliases[rawEvent.type] || rawEvent.type || 'evento_registrado';
  const template = eventTemplate(type, rawEvent);
  const data = rawEvent.data || {};
  const fecha = eventDate(rawEvent);
  const eventId = rawEvent.eventId || rawEvent.id || stableEventId(type, rawEvent);
  const unsafeTurnoText = type !== 'corte_z' && type !== 'turno_abierto' && type !== 'turno_cerrado' &&
    String(rawEvent.title || '').toLowerCase().includes('turno cerrado');
  const genericTurnoTitle = String(rawEvent.title || '').toLowerCase().includes('corte z') &&
    String(rawEvent.title || '').toLowerCase().includes('turno cerrado');

  return {
    id: eventId,
    eventId,
    type,
    title: rawEvent.title && !genericTurnoTitle && !unsafeTurnoText ? rawEvent.title : template.title,
    body: rawEvent.body && !(type !== 'corte_z' && type !== 'turno_abierto' && type !== 'turno_cerrado' && String(rawEvent.body).toLowerCase().includes('turno')) ? rawEvent.body : template.body,
    amount: rawEvent.amount ?? rawEvent.total ?? data.total ?? data.importe ?? data.venta_pagada ?? data.movimiento?.importe ?? null,
    folio: rawEvent.folio ?? data.folio ?? null,
    numcheque: rawEvent.numcheque ?? data.numcheque ?? null,
    idturno: rawEvent.idturno ?? data.idturno ?? null,
    usuario: rawEvent.usuario ?? data.usuario ?? data.movimiento?.usuario ?? null,
    fecha,
    sourceTable: rawEvent.sourceTable || data.sourceTable || '',
    url: rawEvent.url || template.url,
    priority: rawEvent.priority || template.priority,
    createdAt: rawEvent.createdAt || now.toISOString(),
    data,
  };
}

function normalizeNotification(payload) {
  return normalizeLiveEvent(payload);
}

function toSafePushNotification(notification) {
  const titleMap = {
    cuenta_cerrada: 'Cuenta cerrada',
    venta_cerrada: 'Venta registrada',
    retiro_caja: 'Movimiento de caja',
    deposito_caja: 'Movimiento de caja',
    corte_z: 'Corte Z generado',
    turno_abierto: 'Turno abierto',
    turno_cerrado: 'Turno cerrado',
    cortesia: 'Cortesia registrada',
    cancelacion_cuenta: 'Cuenta cancelada',
    producto_cancelado: 'Producto cancelado',
  };

  return {
    id: notification.id,
    eventId: notification.eventId,
    type: notification.type,
    title: titleMap[notification.type] || notification.title || 'Evento registrado',
    body: 'Abre ARKHON Monitor para revisar el detalle.',
    url: notification.url || '/',
    priority: notification.priority || 'normal',
    createdAt: notification.createdAt,
    safe: true,
  };
}

function isDuplicate(eventId) {
  const now = Date.now();
  for (const [key, ts] of notifiedEvents) {
    if (now - ts > DEDUPE_TTL_MS) notifiedEvents.delete(key);
  }
  if (!eventId) return false;
  if (notifiedEvents.has(eventId)) return true;
  notifiedEvents.set(eventId, now);
  return false;
}

/*
function normalizeNotificationOld(payload) {
  const now = new Date();
  return {
    id: payload.id || `${Date.now()}-${Math.random().toString(36).slice(2)}`,
    type: payload.type || 'sistema',
    title: payload.title || 'Notificación de ARKHON',
    body: payload.body || '',
    url: payload.url || '/',
    priority: payload.priority || 'normal',
    createdAt: payload.createdAt || now.toISOString(),
    data: payload.data || {},
  };
}
*/

async function sendWebPush(notification) {
  const webpush = getWebPush();
  if (!webpush) return { sent: 0, skipped: true };

  const subs = readStore().map(normalizeStoreItem).filter(item => item.enabled !== false);
  if (!subs.length) return { sent: 0, skipped: false };

  const payload = JSON.stringify(toSafePushNotification(notification));
  const alive = [];
  let sent = 0;

  for (const item of subs) {
    const subscription = item.subscription || item;
    try {
      await webpush.sendNotification(subscription, payload);
      alive.push(item);
      sent++;
    } catch (err) {
      const code = err.statusCode || err.status;
      if (code !== 404 && code !== 410) alive.push(item);
      logger.warn({ event: 'web_push_send_error', statusCode: code, message: err.message });
    }
  }

  if (alive.length !== subs.length) writeStore(alive);
  return { sent, skipped: false };
}

function emitLive(notification) {
  if (io) io.emit('arkhon-notification', notification);
}

async function notify(payload) {
  const notification = normalizeNotification(payload);
  if (isDuplicate(notification.eventId)) return notification;
  emitLive(notification);
  sendWebPush(notification).catch(err => logger.warn({ event: 'web_push_async_error', message: err.message }));
  return notification;
}

function subscribe(subscription, meta = {}) {
  if (!subscription || !subscription.endpoint) throw new Error('Suscripción push inválida');
  const subs = readStore();
  const endpoint = endpointOf(subscription);
  const now = new Date().toISOString();
  const item = normalizeStoreItem({
    endpoint,
    subscription,
    userId: meta.userId || null,
    deviceId: meta.deviceId || '',
    enabled: true,
    lastSeenAt: now,
    userAgent: meta.userAgent || '',
    platform: meta.platform || '',
    deviceName: meta.deviceName || '',
    meta: {
      deviceId: meta.deviceId || '',
      userId: meta.userId || null,
      userAgent: meta.userAgent || '',
      platform: meta.platform || '',
      deviceName: meta.deviceName || '',
      createdAt: meta.createdAt || now,
      updatedAt: now,
    },
  });
  const idx = subs.findIndex(s => {
    const current = normalizeStoreItem(s);
    const sameEndpoint = endpointOf(current.subscription || current) === endpoint;
    const sameDevice = Boolean(item.deviceId) && current.deviceId === item.deviceId &&
      String(current.userId || '') === String(item.userId || '');
    return sameEndpoint || sameDevice;
  });
  if (idx >= 0) subs[idx] = normalizeStoreItem({ ...subs[idx], ...item, meta: { ...(subs[idx].meta || {}), ...item.meta } });
  else subs.push(item);
  writeStore(subs);
  return { count: subs.length };
}

function unsubscribe(endpoint, deviceId) {
  const subs = readStore();
  const now = new Date().toISOString();
  const requestedDeviceId = clean(deviceId);
  const next = subs.map(item => {
    const normalized = normalizeStoreItem(item);
    const sameEndpoint = endpoint && endpointOf(normalized.subscription || normalized) === endpoint;
    const sameDevice = requestedDeviceId && deviceIdOf(normalized) === requestedDeviceId;
    if (sameEndpoint || sameDevice) {
      return {
        ...normalized,
        enabled: false,
        lastSeenAt: now,
        lastSeen: now,
        meta: { ...(normalized.meta || {}), updatedAt: now },
      };
    }
    return normalized;
  });
  writeStore(next);
  return {
    count: next.length,
    removed: subs.length - next.length,
    disabled: next.filter(item => item.enabled === false).length,
  };
}

function status(deviceId) {
  const subs = readStore().map(normalizeStoreItem);
  const requestedDeviceId = clean(deviceId);
  const currentDevice = requestedDeviceId
    ? [...subs].reverse().find(item => item.deviceId === requestedDeviceId)
    : null;
  return {
    enabled: process.env.NOTIFICATIONS_ENABLED !== 'false',
    appName: APP_NAME,
    publicKey: getVapidPublicKey(),
    webPushConfigured: Boolean(getWebPush()),
    subscriptions: subs.filter(item => item.enabled !== false).length,
    device: requestedDeviceId ? {
      deviceId: requestedDeviceId,
      enabled: Boolean(currentDevice && currentDevice.enabled !== false),
      lastSeenAt: currentDevice ? (currentDevice.lastSeenAt || currentDevice.lastSeen) : null,
      lastSeen: currentDevice ? (currentDevice.lastSeenAt || currentDevice.lastSeen) : null,
      userAgent: currentDevice ? currentDevice.userAgent : '',
      platform: currentDevice ? currentDevice.platform : '',
      deviceName: currentDevice ? currentDevice.deviceName : '',
    } : null,
  };
}

module.exports = {
  init,
  notify,
  normalizeLiveEvent,
  subscribe,
  unsubscribe,
  status,
};
