import {
  MetaServerEvent,
  MetaCapiPayload,
  MetaUserData,
  CapiEventRecord,
} from '@/types';
import {
  hashMetaField,
  hashMetaPhone,
  hashMetaCountry,
  hashMetaZip,
} from './hashing';
import { db } from '@/db/client';
import { logger } from '@/lib/logger';
import crypto from 'crypto';

interface SendCapiOptions {
  pixelId: string;
  accessToken: string;
  events: MetaServerEvent[];
  testEventCode?: string;
  shopId: string;
}

/**
 * Normalizes and hashes incoming user data fields to Meta CAPI specification.
 */
export function normalizeUserData(raw: {
  email?: string;
  phone?: string;
  firstName?: string;
  lastName?: string;
  city?: string;
  zip?: string;
  country?: string;
  clientIp?: string;
  userAgent?: string;
  fbc?: string;
  fbp?: string;
  externalId?: string;
}): MetaUserData {
  const data: MetaUserData = {};

  if (raw.email) {
    const hashed = hashMetaField(raw.email);
    if (hashed) data.em = [hashed];
  }
  if (raw.phone) {
    const hashed = hashMetaPhone(raw.phone);
    if (hashed) data.ph = [hashed];
  }
  if (raw.firstName) {
    const hashed = hashMetaField(raw.firstName);
    if (hashed) data.fn = [hashed];
  }
  if (raw.lastName) {
    const hashed = hashMetaField(raw.lastName);
    if (hashed) data.ln = [hashed];
  }
  if (raw.city) {
    const hashed = hashMetaField(raw.city);
    if (hashed) data.ct = [hashed];
  }
  if (raw.zip) {
    const hashed = hashMetaZip(raw.zip);
    if (hashed) data.zp = [hashed];
  }
  if (raw.country) {
    const hashed = hashMetaCountry(raw.country);
    if (hashed) data.country = [hashed];
  }
  if (raw.clientIp) data.client_ip_address = raw.clientIp;
  if (raw.userAgent) data.client_user_agent = raw.userAgent;
  if (raw.fbc) data.fbc = raw.fbc;
  if (raw.fbp) data.fbp = raw.fbp;
  if (raw.externalId) {
    const hashed = hashMetaField(raw.externalId);
    if (hashed) data.external_id = [hashed];
  }

  return data;
}

/**
 * Generates deterministic event_id for deduplicating browser Pixel and server CAPI events.
 */
export function generateEventId(prefix: string, orderOrCartId?: string): string {
  if (orderOrCartId) {
    return `${prefix}_${orderOrCartId.replace(/^.*\//, '')}`;
  }
  return `${prefix}_${Date.now()}_${crypto.randomBytes(4).toString('hex')}`;
}

import { env } from '@/lib/config/env';

/**
 * Dispatches server-side events to Meta Conversions API via Graph API.
 */
export async function sendMetaCapiEvents(options: SendCapiOptions): Promise<{
  success: boolean;
  eventsReceived?: number;
  fbtraceId?: string;
  error?: string;
}> {
  const { pixelId, accessToken, events, testEventCode, shopId } = options;
  const version = env.meta.graphVersion;
  const url = `https://graph.facebook.com/${version}/${pixelId}/events?access_token=${encodeURIComponent(accessToken)}`;

  const payload: MetaCapiPayload = {
    data: events,
    partner_agent: env.meta.partnerAgent,
  };

  if (testEventCode) {
    payload.test_event_code = testEventCode;
  }

  const startTime = Date.now();
  let responseStatus = 200;
  let responseData: Record<string, unknown> = {};
  let errorMessage: string | null = null;

  // Handle mock/demo tokens in sandbox testing
  const isDemo = !accessToken || accessToken.startsWith('EAABwz_demo') || accessToken.startsWith('mock_');

  if (isDemo) {
    responseData = {
      events_received: events.length,
      fbtrace_id: `trace_mock_${Date.now()}`,
      messages: ['Sandbox demo event successfully registered'],
    };
    responseStatus = 200;
  } else {
    try {
      const response = await fetch(url, {
        method: 'POST',
        headers: {
          'Content-Type': 'application/json',
        },
        body: JSON.stringify(payload),
      });

      responseStatus = response.status;
      responseData = await response.json();

      if (!response.ok) {
        errorMessage = (responseData.error as { message?: string })?.message || `HTTP ${responseStatus}`;
      }
    } catch (err) {
      responseStatus = 500;
      errorMessage = err instanceof Error ? err.message : String(err);
    }
  }

  const durationMs = Date.now() - startTime;

  // Record audit trail for each event
  for (const event of events) {
    const record: CapiEventRecord = {
      id: `capi_${Date.now()}_${crypto.randomBytes(4).toString('hex')}`,
      shop_id: shopId,
      event_id: event.event_id,
      event_name: event.event_name,
      event_source_url: event.event_source_url,
      payload_hash: crypto.createHash('sha256').update(JSON.stringify(event)).digest('hex'),
      user_data_masked: {
        has_email: event.user_data.em ? 'yes (hashed)' : 'no',
        has_phone: event.user_data.ph ? 'yes (hashed)' : 'no',
        ip: event.user_data.client_ip_address ? 'present' : 'none',
      },
      custom_data: event.custom_data,
      status: errorMessage ? 'failed' : 'sent',
      meta_event_id: (responseData.fbtrace_id as string) || null,
      response_code: responseStatus,
      response_body: responseData,
      error_message: errorMessage,
      duration_ms: durationMs,
      created_at: new Date().toISOString(),
    };

    await db.recordCapiEvent(record);
  }

  logger.info(`Dispatched ${events.length} CAPI event(s) to Meta Pixel ${pixelId}`, {
    shopId,
    status: responseStatus,
    eventsReceived: (responseData.events_received as number) || 0,
  });

  return {
    success: !errorMessage,
    eventsReceived: responseData.events_received as number,
    fbtraceId: responseData.fbtrace_id as string,
    error: errorMessage || undefined,
  };
}
