import express from 'express';
import path from 'path';
import crypto from 'crypto';
import dotenv from 'dotenv';
import { createServer as createViteServer } from 'vite';

dotenv.config();

const app = express();
const PORT = 3000;

app.use(express.json());

// In-memory store of recent CAPI events for live debugging
interface CAPIEventRecord {
  id: string;
  timestamp: string;
  eventName: string;
  eventId: string;
  status: 'sent_to_meta' | 'simulated_success' | 'failed';
  matchedParameters: string[];
  eventMatchQuality: number; // 0 - 10 score
  rawPayload: any;
  metaResponse?: any;
  error?: string;
}

const recentEvents: CAPIEventRecord[] = [];

// Helper: Normalization & SHA-256 Hashing according to Meta CAPI specification
function hashMetaField(value?: string): string | undefined {
  if (!value) return undefined;
  const normalized = value.trim().toLowerCase();
  if (!normalized) return undefined;
  return crypto.createHash('sha256').update(normalized).digest('hex');
}

function hashPhone(phone?: string): string | undefined {
  if (!phone) return undefined;
  // Keep only digits per Meta specification
  const digitsOnly = phone.replace(/\D/g, '');
  if (!digitsOnly) return undefined;
  return crypto.createHash('sha256').update(digitsOnly).digest('hex');
}

// Compute Event Match Quality (EMQ) estimation score out of 10
function calculateEstimatedEMQ(userData: Record<string, any>): { score: number; matched: string[] } {
  const matched: string[] = [];
  let score = 0;

  if (userData.em && userData.em.length > 0) {
    score += 3.5;
    matched.push('Email (em, hashed)');
  }
  if (userData.ph && userData.ph.length > 0) {
    score += 2.5;
    matched.push('Téléphone (ph, hashed)');
  }
  if (userData.fn && userData.fn.length > 0) {
    score += 0.8;
    matched.push('Prénom (fn, hashed)');
  }
  if (userData.ln && userData.ln.length > 0) {
    score += 0.8;
    matched.push('Nom (ln, hashed)');
  }
  if (userData.client_ip_address) {
    score += 1.0;
    matched.push('Adresse IP client');
  }
  if (userData.client_user_agent) {
    score += 0.5;
    matched.push('User Agent client');
  }
  if (userData.fbc) {
    score += 0.5;
    matched.push('Meta Click ID (_fbc)');
  }
  if (userData.fbp) {
    score += 0.4;
    matched.push('Meta Browser ID (_fbp)');
  }

  return {
    score: Math.min(10, Math.round(score * 10) / 10),
    matched,
  };
}

// API: Health check
app.get('/api/health', (req, res) => {
  res.json({ status: 'ok', timestamp: new Date().toISOString() });
});

// API: Meta config status check
app.get('/api/meta-config', (req, res) => {
  const pixelId = process.env.META_PIXEL_ID || '';
  const hasToken = Boolean(process.env.META_CAPI_ACCESS_TOKEN && process.env.META_CAPI_ACCESS_TOKEN.length > 5);
  const testCode = process.env.META_TEST_EVENT_CODE || '';

  res.json({
    configured: Boolean(pixelId && hasToken),
    pixelId: pixelId ? `${pixelId.slice(0, 4)}••••${pixelId.slice(-4)}` : null,
    hasAccessToken: hasToken,
    testEventCode: testCode || null,
    serverTime: Math.floor(Date.now() / 1000),
  });
});

// API: Retrieve recent CAPI log events
app.get('/api/meta-events', (req, res) => {
  res.json({ events: recentEvents });
});

// API: Clear event logs
app.delete('/api/meta-events', (req, res) => {
  recentEvents.length = 0;
  res.json({ success: true });
});

// API: Receive form submission and fire Meta CAPI event
app.post('/api/meta-capi', async (req, res) => {
  try {
    const {
      userData = {},
      trackingData = {},
      assessmentData = {},
      eventName = 'Lead',
      eventId = `lead_${Date.now()}_${Math.random().toString(36).substring(2, 9)}`,
      testCodeOverride,
    } = req.body;

    // 1. Extract Client IP and User Agent (Critical for high EMQ match score)
    const forwarded = req.headers['x-forwarded-for'];
    let clientIp = typeof forwarded === 'string' ? forwarded.split(',')[0].trim() : req.socket.remoteAddress || '127.0.0.1';
    // Clean IPv6 mapped IPv4
    if (clientIp.startsWith('::ffff:')) {
      clientIp = clientIp.substring(7);
    }
    const clientUserAgent = (req.headers['user-agent'] as string) || trackingData.userAgent || 'Mozilla/5.0';

    // 2. Hash User Personal Identifiable Information (PII)
    const hashedEmail = hashMetaField(userData.email);
    const hashedPhone = hashPhone(userData.phone);
    const hashedFirstName = hashMetaField(userData.firstName);
    const hashedLastName = hashMetaField(userData.lastName);
    const hashedCity = hashMetaField(userData.city);
    const hashedZip = hashMetaField(userData.zip);

    // 3. Format FBC & FBP Meta parameters
    // If fbclid is provided but no _fbc exists, format fb.1.{timestamp}.{fbclid}
    let fbc = trackingData.fbc;
    if (!fbc && trackingData.fbclid) {
      fbc = `fb.1.${Date.now()}.${trackingData.fbclid}`;
    }
    const fbp = trackingData.fbp || `fb.1.${Date.now()}.${Math.floor(Math.random() * 9000000000) + 1000000000}`;

    const metaUserData: Record<string, any> = {
      client_ip_address: clientIp,
      client_user_agent: clientUserAgent,
      fbc: fbc || undefined,
      fbp: fbp || undefined,
    };

    if (hashedEmail) metaUserData.em = [hashedEmail];
    if (hashedPhone) metaUserData.ph = [hashedPhone];
    if (hashedFirstName) metaUserData.fn = [hashedFirstName];
    if (hashedLastName) metaUserData.ln = [hashedLastName];
    if (hashedCity) metaUserData.ct = [hashedCity];
    if (hashedZip) metaUserData.zp = [hashedZip];
    metaUserData.country = [hashMetaField('fr')]; // Default ISO country code for matching

    // 4. Build custom_data including all test assessment answers & Meta UTM campaign tags
    const customData: Record<string, any> = {
      content_name: "Test d'Évaluation d'Anglais Gratuit",
      content_category: "Language Assessment & Cognitive Placement",
      currency: "EUR",
      value: 0,
      // Assessment answers
      english_goal: assessmentData.goal || 'General Mastery',
      current_level: assessmentData.currentLevel || 'A2_Intermediate',
      primary_blocker: assessmentData.blocker || 'Speaking Inhibition',
      weekly_availability: assessmentData.availability || '2-3 hours/week',
      target_timeline: assessmentData.timeline || '3 months',
      preferred_format: assessmentData.preferredFormat || 'Neuro-Adaptive Test',
      // Meta marketing variables
      fbclid: trackingData.fbclid || undefined,
      utm_source: trackingData.utm_source || undefined,
      utm_medium: trackingData.utm_medium || undefined,
      utm_campaign: trackingData.utm_campaign || undefined,
      utm_content: trackingData.utm_content || undefined,
      utm_term: trackingData.utm_term || undefined,
      ad_id: trackingData.ad_id || undefined,
      campaign_id: trackingData.campaign_id || undefined,
    };

    // Remove undefined values from customData
    Object.keys(customData).forEach((key) => {
      if (customData[key] === undefined) delete customData[key];
    });

    const eventPayload = {
      data: [
        {
          event_name: eventName,
          event_time: Math.floor(Date.now() / 1000),
          event_id: eventId,
          event_source_url: trackingData.pageUrl || req.headers.referer || 'https://example.com/test-anglais',
          action_source: 'website',
          user_data: metaUserData,
          custom_data: customData,
        },
      ],
      test_event_code: testCodeOverride || process.env.META_TEST_EVENT_CODE || undefined,
    };

    if (!eventPayload.test_event_code) {
      delete eventPayload.test_event_code;
    }

    const { score: emqScore, matched: matchedParams } = calculateEstimatedEMQ(metaUserData);

    const pixelId = process.env.META_PIXEL_ID;
    const accessToken = process.env.META_CAPI_ACCESS_TOKEN;

    let dispatchResult: any;
    let eventStatus: 'sent_to_meta' | 'simulated_success' | 'failed' = 'simulated_success';
    let errorMessage: string | undefined;

    if (pixelId && accessToken) {
      // Direct POST to Meta Graph API v19.0
      try {
        const metaUrl = `https://graph.facebook.com/v19.0/${pixelId}/events?access_token=${accessToken}`;
        const metaResponse = await fetch(metaUrl, {
          method: 'POST',
          headers: { 'Content-Type': 'application/json' },
          body: JSON.stringify(eventPayload),
        });

        dispatchResult = await metaResponse.json();

        if (metaResponse.ok && dispatchResult.events_received) {
          eventStatus = 'sent_to_meta';
        } else {
          eventStatus = 'failed';
          errorMessage = dispatchResult?.error?.message || 'Meta API returned an error';
        }
      } catch (err: any) {
        eventStatus = 'failed';
        errorMessage = err.message || 'Network error connecting to Meta Graph API';
        dispatchResult = { error: errorMessage };
      }
    } else {
      // In development or when credentials are not yet entered, simulate a verified Meta response
      dispatchResult = {
        events_received: 1,
        messages: [],
        fbtrace_id: `trace_${Math.random().toString(36).substring(2, 12)}`,
        note: 'Mode simulation actif (ajoutez META_PIXEL_ID et META_CAPI_ACCESS_TOKEN dans les paramètres pour un envoi direct aux serveurs Meta)',
      };
      eventStatus = 'simulated_success';
    }

    const record: CAPIEventRecord = {
      id: `ev_${Date.now()}`,
      timestamp: new Date().toLocaleTimeString('fr-FR'),
      eventName,
      eventId,
      status: eventStatus,
      matchedParameters: matchedParams,
      eventMatchQuality: emqScore,
      rawPayload: eventPayload,
      metaResponse: dispatchResult,
      error: errorMessage,
    };

    // Store in memory (limit to 30)
    recentEvents.unshift(record);
    if (recentEvents.length > 30) recentEvents.pop();

    return res.json({
      success: eventStatus !== 'failed',
      status: eventStatus,
      eventId,
      emqScore,
      matchedParameters: matchedParams,
      metaResponse: dispatchResult,
      payloadPreview: {
        eventName,
        eventId,
        hashedEmailMask: hashedEmail ? `${hashedEmail.slice(0, 8)}...` : null,
        fbc,
        fbp,
        clientIp,
        customParametersCount: Object.keys(customData).length,
      },
      error: errorMessage,
    });
  } catch (error: any) {
    console.error('Erreur CAPI:', error);
    return res.status(500).json({
      success: false,
      error: error.message || 'Erreur interne lors du traitement CAPI',
    });
  }
});

// Setup Vite middleware for development or serve dist in production
async function startServer() {
  if (process.env.NODE_ENV !== 'production') {
    const vite = await createViteServer({
      server: { middlewareMode: true },
      appType: 'spa',
    });
    app.use(vite.middlewares);
  } else {
    const distPath = path.join(process.cwd(), 'dist');
    app.use(express.static(distPath));
    app.get('*', (req, res) => {
      res.sendFile(path.join(distPath, 'index.html'));
    });
  }

  app.listen(PORT, '0.0.0.0', () => {
    console.log(`Serveur Meta CAPI opérationnel sur http://0.0.0.0:${PORT}`);
  });
}

startServer();
