/**
 * Shared utilities for Zabbix WAN host management.
 * Used by both the RMM discovery sync and the manual host creation endpoints.
 */

import { ZabbixClient } from '@/lib/services/zabbix-client';
import { ZabbixHostMacro, ZabbixHostTag, ZabbixHostCreateParams } from '@/lib/types/zabbix';

// ---------------------------------------------------------------------------
// ISP info type
// ---------------------------------------------------------------------------

export interface IspInfo {
  isp: string;       // "Comcast Cable Communications, LLC"
  asn: string;       // "AS7922"
  city: string;
  region: string;
  country: string;
}

// ---------------------------------------------------------------------------
// Zabbix host technical name sanitization
// Zabbix rejects: + ' , . & ( ) and other special chars in the `host` field.
// We sanitize to alphanumeric, spaces, hyphens, underscores only.
// The display `name` field is left as-is (accepts any UTF-8).
// ---------------------------------------------------------------------------

export function sanitizeHostname(name: string): string {
  return name
    .replace(/[^a-zA-Z0-9 \-_]/g, '')  // strip disallowed chars
    .replace(/\s+/g, ' ')               // collapse multiple spaces
    .trim();
}

// ---------------------------------------------------------------------------
// ISP lookup via ipinfo.io (free, no key required for basic fields)
// Results are cached within the module lifetime to avoid duplicate lookups.
// Call clearIspCache() at the start of each request if needed.
// ---------------------------------------------------------------------------

const ispCache = new Map<string, IspInfo | null>();

export function clearIspCache(): void {
  ispCache.clear();
}

export async function lookupIsp(ip: string): Promise<IspInfo | null> {
  if (ispCache.has(ip)) return ispCache.get(ip)!;

  try {
    const token = process.env.IPINFO_TOKEN;
    const headers: Record<string, string> = { Accept: 'application/json' };
    if (token) headers['Authorization'] = `Bearer ${token}`;

    const res = await fetch(`https://ipinfo.io/${ip}/json`, {
      headers,
      cache: 'no-store',
      signal: AbortSignal.timeout(6000),
    });
    if (!res.ok) { ispCache.set(ip, null); return null; }

    const data = await res.json();
    // org field format: "AS7922 Comcast Cable Communications, LLC"
    const org: string = data.org ?? '';
    const m = org.match(/^(AS\d+)\s+(.+)$/);

    const info: IspInfo = {
      isp:     m ? m[2] : org,
      asn:     m ? m[1] : '',
      city:    data.city    ?? '',
      region:  data.region  ?? '',
      country: data.country ?? '',
    };
    ispCache.set(ip, info);
    return info;
  } catch {
    ispCache.set(ip, null);
    return null;
  }
}

// ---------------------------------------------------------------------------
// Build the full Zabbix host upsert params from common inputs.
// ---------------------------------------------------------------------------

export interface BuildHostParamsInput {
  siteName: string;
  wanIp: string;
  companyId?: number;
  companyName?: string;
  rmmSiteUid?: string;
  ispInfo: IspInfo | null;
  multiWan?: boolean;
  allIps?: string[];
  singleDeviceFallback?: boolean;
  onlineDeviceCount?: number;
  source?: string;                // tag value for "source" (default "datto-rmm")
  icmpTemplateId: string | null;
  globalGroupId: string;
  zabbix: ZabbixClient;
}

export async function buildHostParams(input: BuildHostParamsInput): Promise<ZabbixHostCreateParams> {
  const {
    siteName, wanIp, companyId, companyName, rmmSiteUid,
    ispInfo, multiWan, allIps, singleDeviceFallback,
    onlineDeviceCount, source, icmpTemplateId, globalGroupId, zabbix,
  } = input;

  const templates = icmpTemplateId ? [{ templateid: icmpTemplateId }] : undefined;

  // Build groups: always global, + per-client, + per-ISP
  const groups: Array<{ groupid: string }> = [{ groupid: globalGroupId }];

  if (companyName) {
    const clientGroupId = await zabbix.ensureHostGroup(`Clients/${companyName}`);
    groups.push({ groupid: clientGroupId });
  }
  if (ispInfo?.isp) {
    const ispGroupId = await zabbix.ensureHostGroup(`ISP/${ispInfo.isp}`);
    groups.push({ groupid: ispGroupId });
  }

  // Build macros: Autotask identity + ISP context
  const macros: ZabbixHostMacro[] = [];
  if (companyId && companyName) {
    macros.push(
      { macro: '{$AUTOTASK_COMPANY_ID}',   value: String(companyId), description: 'Autotask company ID' },
      { macro: '{$AUTOTASK_COMPANY_NAME}', value: companyName,        description: 'Autotask company name' },
    );
  }
  if (rmmSiteUid) {
    macros.push(
      { macro: '{$RMM_SITE_UID}', value: rmmSiteUid, description: 'Datto RMM site UID' },
    );
  }
  if (ispInfo) {
    macros.push(
      { macro: '{$ISP_NAME}',    value: ispInfo.isp,     description: 'ISP / carrier name' },
      { macro: '{$ASN}',         value: ispInfo.asn,     description: 'Autonomous System Number' },
      { macro: '{$ISP_CITY}',    value: ispInfo.city,    description: 'City (from IP geolocation)' },
      { macro: '{$ISP_REGION}',  value: ispInfo.region,  description: 'Region (from IP geolocation)' },
      { macro: '{$ISP_COUNTRY}', value: ispInfo.country, description: 'Country code (from IP geolocation)' },
    );
  }
  if (multiWan && allIps) {
    macros.push({ macro: '{$MULTI_WAN_IPS}', value: allIps.join(', '), description: 'All public IPs seen (multi-WAN site)' });
  }

  // Build tags: for dashboard filtering and problem correlation
  const sourceTag = source ?? 'datto-rmm';
  const tags: ZabbixHostTag[] = [{ tag: 'source', value: sourceTag }];
  if (companyName) {
    tags.push({ tag: 'client', value: companyName });
  }
  if (ispInfo?.isp) {
    tags.push({ tag: 'isp', value: ispInfo.isp });
  }
  if (ispInfo?.asn) {
    tags.push({ tag: 'asn', value: ispInfo.asn });
  }
  if (multiWan) {
    tags.push({ tag: 'multi-wan', value: 'true' });
  }
  if (singleDeviceFallback) {
    tags.push({ tag: 'single-device-fallback', value: 'true' });
  }

  const descParts = [
    onlineDeviceCount != null
      ? `Datto RMM site – WAN IP from ${onlineDeviceCount} online devices`
      : `Manual host – WAN IP ${wanIp}`,
    ispInfo ? `ISP: ${ispInfo.isp} (${ispInfo.asn}) — ${ispInfo.city}, ${ispInfo.region}, ${ispInfo.country}` : null,
    multiWan && allIps ? `Multi-WAN detected: ${allIps.join(', ')}` : null,
    singleDeviceFallback ? `Note: IP sourced from single device (no multi-device confirmation)` : null,
  ].filter(Boolean).join('\n');

  return {
    host: sanitizeHostname(siteName),
    name: siteName,
    description: descParts,
    interfaces: [{ type: 1, main: 1, useip: 1, ip: wanIp, dns: '', port: '10050' }],
    groups,
    templates,
    macros: macros.length > 0 ? macros : undefined,
    tags,
  };
}

// ---------------------------------------------------------------------------
// Discover ICMP template — tries multiple common names
// ---------------------------------------------------------------------------

const ICMP_TEMPLATE_NAMES = [
  'ICMP Ping',
  'Template Module ICMP Ping',
  'Template Module ICMP Ping by Zabbix agent',
];

export async function discoverIcmpTemplate(zabbix: ZabbixClient): Promise<string | null> {
  for (const name of ICMP_TEMPLATE_NAMES) {
    const tmpl = await zabbix.findTemplate(name);
    if (tmpl) return tmpl.templateid;
  }
  return null;
}
