import 'server-only'
import { prisma } from '@/server/db/client';

interface WhatsAppProvider {
  id: string;
  name: string;
  type: 'fonnte' | 'waha' | 'mpwa' | 'wablas' | 'gowa' | 'wablast' | 'kirimi' | 'baileys';
  apiKey: string;
  apiUrl: string;
  isActive: boolean;
  priority: number;
  senderNumber?: string;
}

interface SendMessageParams {
  phone: string;
  message: string;
}

/**
 * WhatsApp Service with Failover
 * Automatically tries next provider if current one fails
 */
export class WhatsAppService {
  
  /**
   * Get active providers sorted by priority
   */
  static async getActiveProviders(): Promise<WhatsAppProvider[]> {
    return await prisma.whatsapp_providers.findMany({
      where: { isActive: true },
      orderBy: { priority: 'asc' },
    }) as any[];
  }

  /**
   * Send message with automatic failover
   */
  static async sendMessage({ phone, message }: SendMessageParams) {
    const providers = await this.getActiveProviders();

    if (providers.length === 0) {
      throw new Error('No active WhatsApp providers configured');
    }

    // Clean phone number - ensure it starts with country code (62 for Indonesia)
    let cleanPhone = phone.replace(/[^0-9]/g, '');
    
    // Add 62 prefix if starts with 0
    if (cleanPhone.startsWith('0')) {
      cleanPhone = '62' + cleanPhone.substring(1);
    }
    
    // Ensure starts with 62
    if (!cleanPhone.startsWith('62')) {
      cleanPhone = '62' + cleanPhone;
    }
    
    let lastError: Error | null = null;
    const attempts: Array<{
      provider: string;
      type: string;
      success: boolean;
      error?: string;
      response?: any;
    }> = [];
    
    // Try each provider in priority order
    for (const provider of providers) {
      try {
        console.log(`[WhatsApp] Trying provider: ${provider.name} (${provider.type})`);
        
        const result = await this.sendViaProvider(provider, cleanPhone, message);
        
        // Log success
        await this.logMessage({
          phone: cleanPhone,
          message,
          status: 'sent',
          providerId: provider.id,
          providerName: provider.name,
          providerType: provider.type,
          response: JSON.stringify(result),
        });
        
        console.log(`[WhatsApp] ✅ Sent via ${provider.name}`);
        
        attempts.push({
          provider: provider.name,
          type: provider.type,
          success: true,
          response: result,
        });
        
        return {
          success: true,
          provider: provider.name,
          response: result,
          attempts,
        };
        
      } catch (error) {
        lastError = error as Error;
        console.error(`[WhatsApp] ❌ Failed via ${provider.name}:`, error);
        
        attempts.push({
          provider: provider.name,
          type: provider.type,
          success: false,
          error: lastError.message,
        });
        
        // Log failure
        await this.logMessage({
          phone: cleanPhone,
          message,
          status: 'failed',
          providerId: provider.id,
          providerName: provider.name,
          providerType: provider.type,
          response: JSON.stringify({ error: lastError.message }),
        });
        
        // Continue to next provider
        continue;
      }
    }
    
    // All providers failed — return structured failure instead of throwing
    // so the route can include per-provider attempt details in the response.
    return {
      success: false as const,
      provider: null,
      error: `All WhatsApp providers failed. Last error: ${lastError?.message}`,
      attempts,
    };
  }

  /**
   * Clean phone number to E.164 format (62xxx)
   */
  private static cleanPhone(phone: string): string {
    let clean = phone.replace(/[^0-9]/g, '');
    if (clean.startsWith('0')) clean = '62' + clean.substring(1);
    if (!clean.startsWith('62')) clean = '62' + clean;
    return clean;
  }

  /**
   * Send broadcast to multiple recipients.
   * Uses Kirimi.id's native /v1/broadcast-message when the active provider is Kirimi.id
   * (groups by unique message content for efficiency), falls back to sequential send otherwise.
   */
  static async sendBroadcast(
    messages: { phone: string; message: string }[]
  ): Promise<{ sent: number; failed: number; results: { phone: string; success: boolean; error?: string }[] }> {
    const providers = await this.getActiveProviders();

    if (providers.length === 0) {
      return {
        sent: 0,
        failed: messages.length,
        results: messages.map(m => ({ phone: m.phone, success: false, error: 'No active WhatsApp providers' })),
      };
    }

    const provider = providers[0];

    if (provider.type === 'kirimi') {
      // For a single recipient use the single-send endpoint — Kirimi.id
      // /v1/broadcast-message requires 2+ numbers in the queue.
      if (messages.length === 1) {
        const msg = messages[0];
        try {
          await this.sendViaKirimi(provider, msg.phone, msg.message);
          return { sent: 1, failed: 0, results: [{ phone: msg.phone, success: true }] };
        } catch (e: any) {
          return { sent: 0, failed: 1, results: [{ phone: msg.phone, success: false, error: e.message }] };
        }
      }
      return await this.sendBroadcastViaKirimi(provider, messages);
    }

    // Other providers: sequential (caller handles rate limiting)
    let sent = 0;
    let failed = 0;
    const results: { phone: string; success: boolean; error?: string }[] = [];

    for (const msg of messages) {
      try {
        await this.sendMessage({ phone: msg.phone, message: msg.message });
        sent++;
        results.push({ phone: msg.phone, success: true });
      } catch (e: any) {
        failed++;
        results.push({ phone: msg.phone, success: false, error: e.message });
      }
    }

    return { sent, failed, results };
  }

  /**
   * Kirimi.id native broadcast API — groups messages by unique content so
   * identical messages are sent in a single /v1/broadcast-message call.
   */
  private static async sendBroadcastViaKirimi(
    provider: WhatsAppProvider,
    messages: { phone: string; message: string }[]
  ): Promise<{ sent: number; failed: number; results: { phone: string; success: boolean; error?: string }[] }> {
    const [userCode, secret] = provider.apiKey.split(':');
    if (!userCode || !secret) {
      return {
        sent: 0,
        failed: messages.length,
        results: messages.map(m => ({ phone: m.phone, success: false, error: 'Kirimi.id API Key harus format "user_code:secret"' })),
      };
    }
    const deviceId = provider.senderNumber || '';
    if (!deviceId) {
      return {
        sent: 0,
        failed: messages.length,
        results: messages.map(m => ({ phone: m.phone, success: false, error: 'Kirimi.id membutuhkan Device ID' })),
      };
    }

    const baseUrl = provider.apiUrl.replace(/\/+$/, '');

    // Group by unique message so one API call covers all recipients with the same text
    const groupMap = new Map<string, { original: string; clean: string }[]>();
    for (const msg of messages) {
      const clean = this.cleanPhone(msg.phone);
      const arr = groupMap.get(msg.message) ?? [];
      arr.push({ original: msg.phone, clean });
      groupMap.set(msg.message, arr);
    }

    let sent = 0;
    let failed = 0;
    const results: { phone: string; success: boolean; error?: string }[] = [];

    for (const [message, group] of groupMap) {
      const numbers = group.map(g => g.clean);
      try {
        const response = await fetch(`${baseUrl}/v1/broadcast-message`, {
          method: 'POST',
          headers: { 'Content-Type': 'application/json' },
          body: JSON.stringify({
            user_code: userCode,
            secret,
            device_id: deviceId,
            label: `Broadcast-${Date.now()}`,
            numbers,
            message,
            delay: 30, // 30 seconds between messages (Kirimi.id recommendation to avoid WhatsApp block)
          }),
        });

        if (!response.ok) {
          const errorText = await response.text();
          const errMsg = `Kirimi.id API error: ${response.status} - ${errorText}`;
          for (const g of group) { failed++; results.push({ phone: g.original, success: false, error: errMsg }); }
          continue;
        }

        const result = await response.json();
        if (result.success === false) {
          const errMsg = `Kirimi.id error: ${result.message || 'Broadcast failed'}`;
          for (const g of group) { failed++; results.push({ phone: g.original, success: false, error: errMsg }); }
        } else {
          for (const g of group) { sent++; results.push({ phone: g.original, success: true }); }
        }
      } catch (e: any) {
        for (const g of group) { failed++; results.push({ phone: g.original, success: false, error: e.message }); }
      }
    }

    return { sent, failed, results };
  }

  /**
   * Send via specific provider based on type
   */
  private static async sendViaProvider(
    provider: WhatsAppProvider,
    phone: string,
    message: string
  ) {
    switch (provider.type) {
      case 'fonnte':
        return await this.sendViaFonnte(provider, phone, message);
      case 'waha':
        return await this.sendViaWAHA(provider, phone, message);
      case 'mpwa':
        return await this.sendViaMPWA(provider, phone, message);
      case 'wablas':
        return await this.sendViaWablas(provider, phone, message);
      case 'gowa':
        return await this.sendViaGOWA(provider, phone, message);
      case 'wablast':
        return await this.sendViaWABlast(provider, phone, message);
      case 'kirimi':
        return await this.sendViaKirimi(provider, phone, message);
      case 'baileys':
        return await this.sendViaBaileys(provider, phone, message);
      default:
        throw new Error(`Unsupported provider type: ${provider.type}`);
    }
  }

  /**
   * Baileys Native Service — calls internal wa-service.js Express server
   */
  private static async sendViaBaileys(
    provider: WhatsAppProvider,
    phone: string,
    message: string
  ) {
    const port = process.env.WA_SERVICE_PORT || 4000;
    const response = await fetch(`http://127.0.0.1:${port}/send`, {
      method: 'POST',
      headers: { 'Content-Type': 'application/json' },
      body: JSON.stringify({ phone, message }),
      signal: AbortSignal.timeout(30000),
    });

    if (!response.ok) {
      const errorText = await response.text();
      let detail = errorText;
      try { detail = JSON.parse(errorText)?.message || errorText; } catch { }
      throw new Error(`Baileys API error: ${response.status} - ${detail}`);
    }

    const result = await response.json();
    if (result.status === false) {
      throw new Error(`Baileys error: ${result.message || 'Failed to send message'}`);
    }
    return result;
  }

  /**
   * Fonnte API
   */
  private static async sendViaFonnte(
    provider: WhatsAppProvider,
    phone: string,
    message: string
  ) {
    const response = await fetch(provider.apiUrl, {
      method: 'POST',
      headers: {
        'Authorization': provider.apiKey,
        'Content-Type': 'application/json',
      },
      body: JSON.stringify({
        target: phone,
        message: message,
        countryCode: '62',
      }),
    });

    if (!response.ok) {
      const errText = await response.text();
      throw new Error(`Fonnte API error: ${response.status} - ${errText}`);
    }

    const result = await response.json();
    // Fonnte can return HTTP 200 with { status: false, reason: "..." }
    if (result.status === false) {
      throw new Error(`Fonnte error: ${result.reason || result.message || 'Failed to send'}`);
    }
    return result;
  }

  /**
   * WAHA (WhatsApp HTTP API)
   */
  private static async sendViaWAHA(
    provider: WhatsAppProvider,
    phone: string,
    message: string
  ) {
    try {
      // First check session status
      const statusResponse = await fetch(`${provider.apiUrl}/api/sessions`, {
        method: 'GET',
        headers: {
          'X-Api-Key': provider.apiKey,
        },
      });

      if (statusResponse.ok) {
        const sessions = await statusResponse.json();
        const defaultSession = sessions.find((s: any) => s.name === 'default');
        
        if (!defaultSession || defaultSession.status !== 'WORKING') {
          throw new Error(`WAHA session not ready. Status: ${defaultSession?.status || 'NOT_FOUND'}. Please scan QR code.`);
        }
      }

      // Send message
      const response = await fetch(`${provider.apiUrl}/api/sendText`, {
        method: 'POST',
        headers: {
          'X-Api-Key': provider.apiKey,
          'Content-Type': 'application/json',
        },
        body: JSON.stringify({
          session: 'default',
          chatId: `${phone}@c.us`,
          text: message,
        }),
      });

      if (!response.ok) {
        const errorText = await response.text();
        let errorDetail = errorText;
        
        try {
          const errorJson = JSON.parse(errorText);
          errorDetail = errorJson.message || errorJson.error || errorText;
        } catch {
          // Use raw text if not JSON
        }
        
        throw new Error(`WAHA API error: ${response.status} - ${errorDetail}`);
      }

      return await response.json();
    } catch (error) {
      if (error instanceof Error) {
        throw error;
      }
      throw new Error(`WAHA error: ${String(error)}`);
    }
  }

  /**
   * GOWA (Go WhatsApp Multi-Device)
   */
  private static async sendViaGOWA(
    provider: WhatsAppProvider,
    phone: string,
    message: string
  ) {
    try {
      // Extra clean: Remove @s.whatsapp.net suffix if present
      let cleanPhone = phone.replace(/@s\.whatsapp\.net$/, '');
      // Remove any non-numeric characters
      cleanPhone = cleanPhone.replace(/[^0-9]/g, '');
      
      const headers: Record<string, string> = {
        'Content-Type': 'application/json',
      };

      // Add basic auth if API key is in username:password format
      if (provider.apiKey && provider.apiKey.includes(':')) {
        const base64Auth = Buffer.from(provider.apiKey).toString('base64');
        headers['Authorization'] = `Basic ${base64Auth}`;
      }

      const response = await fetch(`${provider.apiUrl}/send/message`, {
        method: 'POST',
        headers,
        body: JSON.stringify({
          phone: cleanPhone,
          message: message,
        }),
      });

      if (!response.ok) {
        const errorData = await response.json().catch(() => ({}));
        throw new Error(`GOWA API error: ${response.status} - ${errorData.message || 'Unknown error'}`);
      }

      const result = await response.json();
      
      // Check GOWA response: { code: "SUCCESS", message: "...", results: {...} }
      if (result.code !== 'SUCCESS') {
        throw new Error(`GOWA error: ${result.message || 'Failed to send message'}`);
      }

      return result;
    } catch (error) {
      if (error instanceof Error) {
        throw error;
      }
      throw new Error(`GOWA error: ${String(error)}`);
    }
  }

  /**
   * MPWA (Multi Purpose WhatsApp API)
   */
  private static async sendViaMPWA(
    provider: WhatsAppProvider,
    phone: string,
    message: string
  ) {
    // MPWA uses query params, not JSON body
    const url = new URL(`${provider.apiUrl}/send-message`);
    url.searchParams.append('api_key', provider.apiKey);
    url.searchParams.append('sender', provider.senderNumber || '');
    url.searchParams.append('number', phone);
    url.searchParams.append('message', message);

    const response = await fetch(url.toString(), {
      method: 'GET',
    });

    if (!response.ok) {
      const errorText = await response.text();
      throw new Error(`MPWA API error: ${response.status} - ${errorText}`);
    }

    return await response.json();
  }

  /**
   * Wablas API V2
   * Docs: https://wablas.com/documentation/api
   * Auth: apiKey = "{token}.{secret_key}" (token dot secret_key)
   * Base URL: https://{server}.wablas.com (e.g. wa, pati, deu, kudus, solo)
   * V2 POST /api/v2/send-message expects JSON body with "data" array
   */
  private static async sendViaWablas(
    provider: WhatsAppProvider,
    phone: string,
    message: string
  ) {
    // Use Wablas simple GET endpoint (/api/send-message) — most universally
    // compatible across all Wablas server versions (wa, deu, jakarta, etc.).
    // token param accepts both "token" and "token.secret_key" formats.
    const baseUrl = provider.apiUrl.replace(/\/+$/, '');
    const url = new URL(`${baseUrl}/api/send-message`);
    url.searchParams.set('token', provider.apiKey);
    url.searchParams.set('phone', phone);
    url.searchParams.set('message', message);
    url.searchParams.set('flag', 'instant');

    const response = await fetch(url.toString());

    if (!response.ok) {
      const errorText = await response.text();
      let detail = errorText;
      try {
        const errJson = JSON.parse(errorText);
        detail = errJson.message || errorText;
      } catch { /* use raw text */ }
      throw new Error(`Wablas API error: ${response.status} - ${detail}`);
    }

    const result = await response.json();

    // Wablas returns { status: true/false, message: "...", data: {...} }
    if (result.status === false) {
      throw new Error(`Wablas error: ${result.message || 'Failed to send message'}`);
    }

    return result;
  }

  /**
   * WABlast API (self-hosted WhatsApp gateway)
   * Common for ISP in Indonesia — many run local WABlast Jakarta instances
   */
  private static async sendViaWABlast(
    provider: WhatsAppProvider,
    phone: string,
    message: string
  ) {
    // WABlast self-hosted gateway — POST JSON to /send-message
    // apiKey can be empty string if the server runs without auth
    const baseUrl = provider.apiUrl.replace(/\/+$/, ''); // strip trailing slash
    const headers: Record<string, string> = { 'Content-Type': 'application/json' };
    if (provider.apiKey) {
      headers['Authorization'] = provider.apiKey;
    }

    const response = await fetch(`${baseUrl}/send-message`, {
      method: 'POST',
      headers,
      body: JSON.stringify({ phone, message }),
    });

    if (!response.ok) {
      const errorText = await response.text();
      let detail = errorText;
      try {
        const errJson = JSON.parse(errorText);
        detail = errJson.message || errJson.error || errorText;
      } catch { /* use raw text */ }
      throw new Error(`WABlast API error: ${response.status} - ${detail}`);
    }

    const result = await response.json();
    if (result.status === false || result.status === 'error') {
      throw new Error(`WABlast error: ${result.message || 'Failed to send'}`);
    }
    return result;
  }

  /**
   * Kirimi.id API
   * Docs: https://kirimi.id/docs
   * Auth: apiKey = "user_code:secret", senderNumber = device_id
   * Base URL: https://api.kirimi.id
   */
  private static async sendViaKirimi(
    provider: WhatsAppProvider,
    phone: string,
    message: string
  ) {
    // apiKey stores "user_code:secret"
    const [userCode, secret] = provider.apiKey.split(':');
    if (!userCode || !secret) {
      throw new Error('Kirimi.id API Key harus format "user_code:secret"');
    }
    const deviceId = provider.senderNumber || '';
    if (!deviceId) {
      throw new Error('Kirimi.id membutuhkan Device ID (isi di field Sender Number)');
    }

    const baseUrl = provider.apiUrl.replace(/\/+$/, ''); // strip trailing slash
    const response = await fetch(`${baseUrl}/v1/send-message`, {
      method: 'POST',
      headers: {
        'Content-Type': 'application/json',
      },
      body: JSON.stringify({
        user_code: userCode,
        secret: secret,
        device_id: deviceId,
        receiver: phone,   // Kirimi.id uses 'receiver', not 'number'
        message: message,
      }),
    });

    if (!response.ok) {
      const errorText = await response.text();
      throw new Error(`Kirimi.id API error: ${response.status} - ${errorText}`);
    }

    const result = await response.json();
    if (result.success === false) {
      throw new Error(`Kirimi.id error: ${result.message || 'Failed to send'}`);
    }
    return result;
  }

  /**
   * Log message to history
   */
  private static async logMessage(data: {
    phone: string;
    message: string;
    status: 'sent' | 'failed';
    providerId: string;
    providerName: string;
    providerType: string;
    response: string;
  }) {
    try {
      await prisma.whatsapp_history.create({
        data: {
          id: crypto.randomUUID(),
          phone: data.phone,
          message: data.message,
          status: data.status,
          response: data.response,
          providerName: data.providerName,
          providerType: data.providerType,
        },
      });
    } catch (error) {
      console.error('[WhatsApp] Failed to log message:', error);
      // Don't throw, logging failure shouldn't break the flow
    }
  }

  /**
   * Test provider connection
   */
  static async testProvider(providerId: string, testPhone: string) {
    const provider = await prisma.whatsapp_providers.findUnique({
      where: { id: providerId },
    });

    if (!provider) {
      throw new Error('Provider not found');
    }

    try {
      const result = await this.sendViaProvider(
        provider as any,
        testPhone.replace(/[^0-9]/g, ''),
        'Test message from SALFANET RADIUS 📱\n\nJika Anda menerima pesan ini, WhatsApp provider berhasil terhubung!'
      );

      return {
        success: true,
        provider: provider.name,
        response: result,
      };
    } catch (error) {
      return {
        success: false,
        provider: provider.name,
        error: error instanceof Error ? error.message : 'Unknown error',
      };
    }
  }
}
