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

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

// ---------------------------------------------------------------------------
// Helpers
// ---------------------------------------------------------------------------

const ID_FIELDS = ['id', 'jid', 'user', 'remoteJid', 'wid'] as const;

function extractIdCandidate(rec: any): string | null {
  for (const field of ID_FIELDS) {
    const val = rec?.[field];
    if (typeof val === 'string' && val) return val;
  }
  // wid._serialized (nested)
  const widSerialized = rec?.wid?._serialized;
  if (typeof widSerialized === 'string' && widSerialized) return widSerialized;
  return null;
}

function tryLearnAlias(rec: any): void {
  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 { /* best-effort */ }
}

function extractRecordArrays(data: any): any[][] {
  if (Array.isArray(data)) return [data];

  if (!data || typeof data !== 'object') return [];

  const arrays: any[][] = [];
  const knownKeys = ['contacts', 'chats', 'data', 'payload', 'updates', 'results'];
  for (const key of knownKeys) {
    if (Array.isArray(data[key])) arrays.push(data[key]);
  }

  // Also try wrapping the single object
  arrays.push([data]);
  return arrays;
}

/** Processes a single contact/channel record and caches its display name. */
function processRecord(rec: any, cache: Map<string, CacheEntry>, ttlMs: number): void {
  const idCandidate = extractIdCandidate(rec);
  if (!idCandidate) return;

  tryLearnAlias(rec);

  // Skip groups
  if (typeof idCandidate === 'string' && idCandidate.endsWith('@g.us')) return;

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

  const name = ContactsService.extractName(rec);
  if (!name) return;

  cache.set(normalized, { name, expiresAt: Date.now() + ttlMs });
}

// ---------------------------------------------------------------------------
// ContactsService
// ---------------------------------------------------------------------------

export class ContactsService {
  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',
    };
  }

  static extractName(obj: any): string | null {
    if (!obj || typeof obj !== 'object') return null;
    const candidates = [
      obj.name, obj.pushname, obj.verifiedName, obj.notify,
      obj.shortName, obj.displayName, obj.formattedName, obj.subject,
      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 {
      const arrays = extractRecordArrays(data);
      for (const arr of arrays) {
        for (const rec of arr) {
          processRecord(rec, this.cache, 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 = extractIdCandidate(rec);
        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: Date.now() + this.TTL_MS });
          return name;
        }
      }
      return null;
    } catch {
      return null;
    }
  }

  private static async maybeFetchFromApi(digitsId: string): Promise<string | null> {
    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 } };

    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 > Date.now()) return cached.name;

    const fetched = await this.maybeFetchFromApi(normalized);
    return fetched || null;
  }
}
