import { normalizeWhatsAppId, isUserJid } from '../utils/whatsapp';
import { IdentityService } from './identity';

type CacheEntry = {
  name: string;
  expiresAt: number;
};

export class ContactsService {
  // Caché en memoria: userId(normalizado, solo dígitos) -> nombre
  private static readonly cache = new Map<string, CacheEntry>();
  private static readonly TTL_MS = 12 * 60 * 60 * 1000; // 12h

  private static getHeaders(): HeadersInit {
    return {
      apikey: process.env.EVOLUTION_API_KEY || '',
      'Content-Type': 'application/json',
    };
  }

  private static now() {
    return Date.now();
  }

  private static extractName(obj: any): string | null {
    if (!obj || typeof obj !== 'object') return null;
    // Intentar múltiples campos posibles que suelen aparecer en catálogos/contactos
    const candidates = [
      obj.name,
      obj.pushname,
      obj.verifiedName,
      obj.notify,
      obj.shortName,
      obj.displayName,
      obj.formattedName,
      obj.subject, // a veces para grupos; pero evitaremos grupos abajo
      obj.contactName
    ].filter(Boolean) as string[];
    const name = candidates.find(v => typeof v === 'string' && v.trim().length > 0);
    return name ? name.trim() : null;
  }

  static updateFromWebhook(data: any): void {
    try {
      // Aceptar varios formatos posibles
      const tryArrays: any[] = [];
      if (Array.isArray(data)) {
        tryArrays.push(data);
      } else if (data && typeof data === 'object') {
        for (const key of ['contacts', 'chats', 'data', 'payload', 'updates', 'results']) {
          if (Array.isArray((data as any)[key])) {
            tryArrays.push((data as any)[key]);
          }
        }
        // Algunos eventos pueden traer una sola entrada
        tryArrays.push([data]);
      }

      for (const arr of tryArrays) {
        for (const rec of arr) {
          const idCandidate = rec?.id ?? rec?.jid ?? rec?.user ?? rec?.remoteJid ?? rec?.wid ?? rec?.wid?._serialized;
          if (!idCandidate) continue;

          // Aprender mapping alias→número si vienen ambos (id con @lid y jid de usuario)
          try {
            const rawId = typeof rec?.id === 'string' ? rec.id : null;
            const rawJid = typeof rec?.jid === 'string' ? rec.jid : null;
            if (rawId && rawJid && rawId.includes('@lid') && rawJid.endsWith('@s.whatsapp.net')) {
              IdentityService.upsertAlias(rawId, rawJid, 'contacts.update');
              if (process.env.NODE_ENV !== 'test') {
                console.log('[A0] contacts.update learned alias', { alias: rawId, jid: rawJid });
              }
            }
          } catch {}

          // Evitar grupos
          if (typeof idCandidate === 'string' && idCandidate.endsWith('@g.us')) continue;

          const normalized = normalizeWhatsAppId(String(idCandidate));
          if (!normalized) continue;

          const name = this.extractName(rec);
          if (!name) continue;

          this.cache.set(normalized, { name, expiresAt: this.now() + this.TTL_MS });
        }
      }
    } catch (e) {
      if (process.env.NODE_ENV !== 'test') {
        console.warn('ContactsService.updateFromWebhook: parse error', e);
      }
    }
  }

  private static async fetchNameFrom(url: string, body: any, digitsId: string): Promise<string | null> {
    try {
      const res = await fetch(url, {
        method: 'POST',
        headers: this.getHeaders(),
        body: JSON.stringify(body),
      });

      if (!res.ok) {
        return null;
      }

      const data = await res.json().catch(() => null);
      const arrayCandidates: any[] = Array.isArray(data)
        ? data
        : Array.isArray((data as any)?.contacts)
          ? (data as any).contacts
          : Array.isArray((data as any)?.data)
            ? (data as any).data
            : Array.isArray((data as any)?.result)
              ? (data as any).result
              : (data && typeof data === 'object')
                ? [data]
                : [];

      for (const rec of arrayCandidates) {
        const recId = rec?.id ?? rec?.jid ?? rec?.user ?? rec?.remoteJid ?? rec?.wid ?? rec?.wid?._serialized;
        if (!recId) continue;
        const norm = normalizeWhatsAppId(String(recId));
        if (!norm || norm !== digitsId) continue;
        const name = this.extractName(rec);
        if (name) {
          this.cache.set(digitsId, { name, expiresAt: this.now() + this.TTL_MS });
          return name;
        }
      }
      return null;
    } catch {
      return null;
    }
  }

  private static async maybeFetchFromApi(digitsId: string): Promise<string | null> {
    // Evitar llamadas de red en entorno de test
    if (process.env.NODE_ENV === 'test') return null;

    const baseUrl = process.env.EVOLUTION_API_URL;
    const instance = process.env.EVOLUTION_API_INSTANCE;
    const apikey = process.env.EVOLUTION_API_KEY;

    if (!baseUrl || !instance || !apikey) return null;

    const jid = `${digitsId}@s.whatsapp.net`;
    const body = { where: { id: jid } };

    // Probar múltiples rutas conocidas según versión de Evolution
    const candidates = [
      `${baseUrl}/chat/findContacts/${instance}`,
      `${baseUrl}/contact/findContacts/${instance}`,
      `${baseUrl}/contact/find/${instance}`,
      `${baseUrl}/chat/find/${instance}`,
    ];

    for (const url of candidates) {
      const name = await this.fetchNameFrom(url, body, digitsId);
      if (name) return name;
    }

    return null;
  }

  static async getDisplayName(idOrJid: string | null | undefined): Promise<string | null> {
    const normalized = normalizeWhatsAppId(idOrJid || '');
    if (!normalized) return null;

    const cached = this.cache.get(normalized);
    if (cached && cached.expiresAt > this.now()) {
      return cached.name;
    }

    // Intento de fetch perezoso
    const fetched = await this.maybeFetchFromApi(normalized);
    return fetched || null;
  }
}
