/**
 * Utilidades y funciones helper para el Dashboard de Llamadas
 * Contiene toda la lógica de negocio, formateo y procesamiento de datos
 */

import { User, Phone, PhoneIncoming, PhoneOff, PauseCircle } from "lucide-react";
import type { 
  ExtensionStatus, 
  FriendlyStatus, 
  StatusAppearance, 
  ContactsInfo, 
  ConnectedContact, 
  ExtensionState,
  FriendlyToTechStatusMap 
} from "./types";

/**
 * Tipo para los detalles de endpoint de AMI
 */
export type EndpointDetailsPayload = {
  grouped?: {
    EndpointDetail?: Array<{
      callerid?: string;
      CallerID?: string;
      [key: string]: unknown;
    }>;
    AorDetail?: Array<{
      objectname?: string;
      ObjectName?: string;
      endpointname?: string;
      contacts?: string;
      [key: string]: unknown;
    }>;
    [key: string]: unknown;
  };
  contacts?: Record<string, Array<{
    UserAgent?: string;
    useragent?: string;
    Useragent?: string;
    "User-Agent"?: string;
    Uri?: string;
    uri?: string;
    Contact?: string;
    contact?: string;
    [key: string]: unknown;
  }>>;
  [key: string]: unknown;
};

/**
 * Constantes del sistema
 */
export const STORAGE_KEY = "dash.extensions";
export const STATUS_POLL_INTERVAL = 10000; // 10 segundos
export const CLOCK_TICK_INTERVAL = 1000; // 1 segundo

/**
 * Estados amigables disponibles para filtros
 */
export const FRIENDLY_STATUSES: FriendlyStatus[] = [
  "Disponible",
  "Timbrando",
  "Sonando", 
  "En llamada",
  "Ocupado",
  "En espera",
  "No registrado",
  "Desconectado",
  "No disponible",
  "Desconocido"
];

/**
 * Mapeo de estados amigables a estados técnicos
 */
export const FRIENDLY_TO_TECH_STATUS_MAP: FriendlyToTechStatusMap = {
  Disponible: ["Registered", "Reachable", "NotInUse"],
  Timbrando: ["Ringing"],
  Sonando: ["Ringing"],
  "En llamada": ["InUse"],
  Ocupado: ["Busy"],
  "En espera": ["Hold"],
  "No registrado": ["Unregistered"],
  Desconectado: ["Unregistered", "Unreachable"],
  "No disponible": ["Unavailable"],
  Desconocido: ["Unknown"],
};

/**
 * Obtiene la apariencia visual de un estado de extensión
 */
export const getStatusAppearance = (status: ExtensionStatus): StatusAppearance => {
  switch (status) {
    case "Registered":
    case "Reachable":
    case "NotInUse":
      return { 
        icon: User, 
        color: "bg-emerald-50 border-emerald-200",
        className: "bg-emerald-50 border-emerald-200 text-emerald-700",
        label: "Disponible" 
      };
    case "InUse":
      return { 
        icon: Phone, 
        color: "bg-sky-50 border-sky-200",
        className: "bg-sky-50 border-sky-200 text-sky-700",
        label: "En llamada" 
      };
    case "Ringing":
      return { 
        icon: PhoneIncoming, 
        color: "bg-amber-50 border-amber-200",
        className: "bg-amber-50 border-amber-200 text-amber-700",
        label: "Timbrando" 
      };
    case "Busy":
      return { 
        icon: Phone, 
        color: "bg-orange-50 border-orange-200",
        className: "bg-orange-50 border-orange-200 text-orange-700",
        label: "Ocupado" 
      };
    case "Hold":
      return { 
        icon: PauseCircle, 
        color: "bg-indigo-50 border-indigo-200",
        className: "bg-indigo-50 border-indigo-200 text-indigo-700",
        label: "En espera" 
      };
    case "Unregistered":
    case "Unreachable":
      return { 
        icon: PhoneOff, 
        color: "bg-rose-50 border-rose-200",
        className: "bg-rose-50 border-rose-200 text-rose-700",
        label: "Desconectado" 
      };
    case "Unavailable":
      return { 
        icon: PhoneOff, 
        color: "bg-gray-50 border-gray-200",
        className: "bg-gray-50 border-gray-200 text-gray-700",
        label: "No disponible" 
      };
    default:
      return { 
        icon: PhoneOff, 
        color: "bg-yellow-50 border-yellow-200",
        className: "bg-yellow-50 border-yellow-200 text-yellow-700",
        label: "Desconocido" 
      };
  }
};

/**
 * Mapea un estado de dispositivo a ExtensionStatus
 * Estados válidos de Asterisk: UNKNOWN, NOT_INUSE, INUSE, BUSY, INVALID, UNAVAILABLE, RINGING, RINGINUSE, ONHOLD
 * También maneja formatos con espacios como "Not in use", "In use", etc.
 */
export const mapDeviceStateToStatus = (deviceState: string, peerId?: string): ExtensionStatus => {
  const state = deviceState.toUpperCase().replace(/\s+/g, '_');
  
  console.log(`🔄 Mapping device state: "${deviceState}" -> "${state}" for ${peerId}`);
  
  // Estados específicos de Asterisk PJSIP
  if (state === "UNAVAILABLE") return "Unavailable";
  if (state === "INUSE" || state === "IN_USE") return "InUse";
  if (state === "BUSY") return "Busy";
  if (state === "RINGING" || state === "RINGINUSE") return "Ringing";
  if (state === "ONHOLD" || state === "ON_HOLD") return "InUse"; // En espera se considera como en uso
  
  // NOT_INUSE es el estado normal cuando no hay llamadas
  // Maneja tanto "NOT_INUSE" como "Not in use"
  if (state === "NOT_INUSE" || state === "NOT_IN_USE") {
    // Para troncales, NOT_INUSE significa que está disponible/conectado
    if (peerId && isTrunk(peerId)) {
      console.log(`📞 Trunk ${peerId}: NOT_IN_USE -> Reachable`);
      return "Reachable";
    }
    // Para extensiones, NOT_INUSE significa que no está en uso (disponible)
    console.log(`📱 Extension ${peerId}: NOT_IN_USE -> NotInUse`);
    return "NotInUse";
  }
  
  // Estados de registro (para compatibilidad con estados anteriores)
  if (state.includes("REGISTERED")) return "Registered";
  if (state.includes("UNREGISTERED")) return "Unregistered";
  if (state.includes("REACHABLE")) return "Reachable";
  if (state.includes("UNREACHABLE")) return "Unreachable";
  if (state.includes("LAGGED")) return "Lagged";
  
  console.log(`❓ Unknown device state: "${deviceState}" -> Unknown for ${peerId}`);
  return "Unknown";
};

/**
 * Formatea una duración en milisegundos a formato MM:SS
 * Puede recibir un timestamp de inicio y calcular la duración hasta ahora
 */
export const formatDuration = (startTimeOrMs?: number, currentTime?: number): string => {
  let ms: number;
  
  if (currentTime !== undefined && startTimeOrMs !== undefined) {
    // Calcular duración desde timestamp de inicio hasta tiempo actual
    ms = currentTime - startTimeOrMs;
  } else {
    // Usar directamente los milisegundos proporcionados
    ms = startTimeOrMs ?? 0;
  }
  
  if (ms < 0) return "00:00";
  const seconds = Math.floor(ms / 1000);
  const minutes = Math.floor(seconds / 60);
  const remainingSeconds = seconds % 60;
  return `${String(minutes).padStart(2, "0")}:${String(remainingSeconds).padStart(2, "0")}`;
};

/**
 * Extrae información de contactos (IPs y latencia) desde la cadena de contactos
 */
export const parseContactsInfo = (contacts?: string): ContactsInfo => {
  const ips: string[] = [];
  let latencyMs: number | undefined;
  
  if (contacts) {
    // Extraer IPs
    for (const match of contacts.matchAll(/@([\w\.-]+(?::\d+)?)/g)) {
      ips.push(match[1]);
    }
    
    // Extraer latencia
    const latencyMatch = /(?:Avail|NonQual)\s+(-?\d+(?:\.\d+)?)/.exec(contacts);
    if (latencyMatch) {
      const value = parseFloat(latencyMatch[1]);
      if (!Number.isNaN(value) && value >= 0) {
        latencyMs = value;
      }
    }
  }
  
  return { ips, latencyMs };
};

/**
 * Obtiene la clase CSS para el badge de latencia según el valor
 */
export const getLatencyBadgeClass = (latency?: number | null): string => {
  if (latency === undefined || latency === null) {
    return "bg-slate-200 text-slate-800";
  }
  if (latency < 200) return "bg-emerald-600 text-white";
  if (latency <= 400) return "bg-amber-500 text-white";
  return "bg-rose-600 text-white";
};

/**
 * Extrae el nombre del CallerID desde el payload de detalles del endpoint
 */
export const extractCallerName = (payload: EndpointDetailsPayload | null): string => {
  if (!payload) return "-";
  
  const raw = payload?.grouped?.EndpointDetail?.[0]?.callerid ?? 
             payload?.grouped?.EndpointDetail?.[0]?.CallerID;
  
  if (!raw) return "-";
  
  const rawString = String(raw);
  const nameMatch = /"(.+?)"/.exec(rawString);
  
  if (nameMatch?.[1]) return nameMatch[1];
  
  return rawString.replace(/<.*?>/g, "").replace(/"/g, "").trim();
};

/**
 * Genera las iniciales de un nombre para el avatar
 */
export const getNameInitials = (name?: string): string => {
  if (!name) return "?";
  
  const parts = String(name).trim().split(/\s+/);
  const first = parts[0]?.[0] ?? "";
  const second = parts[1]?.[0] ?? "";
  
  return `${first}${second}`.toUpperCase() || (first || "?").toUpperCase();
};

/**
 * Extrae los contactos conectados desde el payload de detalles del endpoint
 */
export const extractConnectedContacts = (payload: EndpointDetailsPayload | null): ConnectedContact[] => {
  if (!payload) return [];
  
  const contacts: ConnectedContact[] = [];
  const aors = Array.isArray(payload?.grouped?.AorDetail) 
    ? payload.grouped.AorDetail 
    : [];
  const contactsMap = payload?.contacts ?? {};

  for (const aor of aors) {
    const aorName = aor.objectname ?? aor.ObjectName ?? aor.endpointname ?? "";
    const contactList = contactsMap[aorName];
    
    if (Array.isArray(contactList) && contactList.length > 0) {
      for (const contact of contactList) {
        const userAgent = contact.UserAgent ?? 
                         contact.useragent ?? 
                         contact.Useragent ?? 
                         contact["User-Agent"];
        const uri = contact.Uri ?? 
                   contact.uri ?? 
                   contact.Contact ?? 
                   contact.contact ?? "";
        const ipMatch = /@([\w\.-]+)(?::\d+)?/.exec(String(uri));
        const ip = ipMatch?.[1];
        const device = String(uri).split("/")[0] || String(aorName);
        
        if (ip) {
          contacts.push({ device, ip, userAgent });
        }
      }
    } else if (typeof aor.contacts === "string" && aor.contacts.length > 0) {
      const contactString = String(aor.contacts);
      const device = contactString.split("/")[0] || String(aorName);
      const ipMatch = /@([\d\.]+)(?::\d+)?/.exec(contactString);
      const ip = ipMatch?.[1];
      
      if (ip) {
        contacts.push({ device, ip });
      }
    }
  }
  
  // Eliminar duplicados por combinación device-ip
  const uniqueContacts: Record<string, ConnectedContact> = {};
  for (const contact of contacts) {
    uniqueContacts[`${contact.device}-${contact.ip}`] = contact;
  }
  
  return Object.values(uniqueContacts);
};

/**
 * Carga datos persistidos del localStorage
 */
export const loadPersistedExtensions = (): Record<string, Partial<ExtensionState>> => {
  try {
    const raw = localStorage.getItem(STORAGE_KEY);
    return raw ? JSON.parse(raw) as Record<string, ExtensionState> : {};
  } catch {
    return {};
  }
};

/**
 * Persiste extensiones en localStorage (solo datos esenciales)
 * No persiste estados temporales de llamada para evitar estados incorrectos al recargar
 */
export const persistExtensions = (extensions: Record<string, ExtensionState>): void => {
  try {
    const minimal: Record<string, Partial<ExtensionState>> = {};
    
    for (const [key, extension] of Object.entries(extensions)) {
      // No persistir estados temporales de llamada
      const shouldPersistStatus = !["InUse", "Ringing", "Busy"].includes(extension.status);
      
      minimal[key] = {
        peer: extension.peer,
        // Solo persistir status si no es un estado temporal de llamada
        ...(shouldPersistStatus && { status: extension.status }),
        callerIdName: extension.callerIdName,
        // No persistir información de llamada temporal
        aors: extension.aors,
        contacts: extension.contacts,
        transport: extension.transport,
      };
    }
    
    localStorage.setItem(STORAGE_KEY, JSON.stringify(minimal));
  } catch {
    // Silenciar errores de localStorage
  }
};

/**
 * Verifica si un peer es un anexo (solo números)
 */
export const isExtension = (peer: string): boolean => {
  return /^\d+$/.test(peer);
};

/**
 * Verifica si un peer es una troncal (no solo números)
 */
export const isTrunk = (peer: string): boolean => {
  return !/^\d+$/.test(peer);
};

/**
 * Extrae el peer ID desde el nombre del objeto
 */
export const extractPeerIdFromObjectName = (objectName: string): string => {
  return /(\d+)/.exec(String(objectName))?.[1] ?? String(objectName);
};

/**
 * Extrae el peer ID desde un canal
 */
export const extractPeerIdFromChannel = (channel?: string): string | undefined => {
  return /PJSIP\/(\d+)/.exec(channel ?? "")?.[1];
};