/**
 * Instagram N8n Configuration Service
 *
 * Manages per-company Instagram N8n publishing configuration.
 * Uses the Instagram Graph API (Content Publishing API) exclusively.
 *
 * All secrets are AES-256-GCM encrypted at rest.
 * The callback verification token is also encrypted.
 *
 * Follows the per-company pattern (like FacebookN8nConfigService).
 */

import { encryptApiKey } from '../utils/encryption';
import { decryptIgAccessToken, decryptAppId, decryptAppSecret, decryptCallbackToken, maskSecret } from '../../models/InstagramN8nConfig';
import { testWebhook } from '../n8n/N8nClient';
import crypto from 'crypto';

// ============================================
// GET CONFIG (secrets masked — safe for frontend)
// ============================================

export async function getInstagramN8nConfig(companyId: string): Promise<{
  configured: boolean;
  publishMethod?: 'graph_api';
  webhookUrl?: string;
  // Graph API fields
  igBusinessAccountId?: string;
  igAccessTokenMasked?: string;
  appIdMasked?: string;
  appSecretMasked?: string;
  // Common
  postType?: 'text' | 'photo' | 'reel' | 'story';
  callbackUrl?: string;
  status?: string;
  connectedAt?: string;
  lastUsedAt?: string;
} | null> {
  const { getModels } = await import('../../models');
  const { InstagramN8nConfig } = getModels();

  const config = await InstagramN8nConfig.findOne({ companyId })
    .select('+encryptedIgAccessToken +igAccessTokenIV +encryptedAppId +appIdIV +encryptedAppSecret +appSecretIV +encryptedCallbackToken +callbackTokenIV');

  if (!config) {
    return {
      configured: false,
      status: 'disconnected',
    };
  }

  const result: any = {
    configured: true,
    publishMethod: config.publishMethod || 'graph_api',
    webhookUrl: config.webhookUrl,
    postType: config.postType,
    callbackUrl: config.callbackUrl,
    status: config.status,
    connectedAt: config.createdAt?.toISOString(),
    lastUsedAt: config.updatedAt?.toISOString(),
  };

  // Graph API fields
  if (config.igBusinessAccountId) result.igBusinessAccountId = config.igBusinessAccountId;
  if (config.encryptedIgAccessToken && config.igAccessTokenIV) {
    const token = decryptIgAccessToken(config);
    if (token) result.igAccessTokenMasked = maskSecret(token);
  }
  if (config.encryptedAppId && config.appIdIV) {
    const appId = decryptAppId(config);
    if (appId) result.appIdMasked = maskSecret(appId);
  }
  if (config.encryptedAppSecret && config.appSecretIV) {
    const appSecret = decryptAppSecret(config);
    if (appSecret) result.appSecretMasked = maskSecret(appSecret);
  }

  return result;
}

// ============================================
// SAVE CONFIG (encrypts secrets, validates)
// ============================================

export async function saveInstagramN8nConfig(
  companyId: string,
  data: {
    webhookUrl: string;
    publishMethod: 'graph_api';
    igBusinessAccountId?: string;
    igAccessToken?: string;
    appId?: string;
    appSecret?: string;
    postType?: 'text' | 'photo' | 'reel' | 'story';
    updatedBy?: string;
  },
): Promise<{ success: boolean; error?: string; callbackUrl?: string }> {
  const { getModels } = await import('../../models');
  const { InstagramN8nConfig } = getModels();

  try {
    // Validate webhook URL
    if (!data.webhookUrl?.trim()) {
      return { success: false, error: 'Webhook URL is required' };
    }
    try {
      new URL(data.webhookUrl);
    } catch {
      return { success: false, error: 'Invalid webhook URL format' };
    }

    const publishMethod = data.publishMethod || 'graph_api';

    // Validate IG Business Account ID (required for graph_api)
    if (publishMethod === 'graph_api') {
      if (!data.igBusinessAccountId?.trim()) {
        return { success: false, error: 'IG Business Account ID is required for Graph API method' };
      }
      // igAccessToken is required on first save, optional on updates
      const existingConfig = await InstagramN8nConfig.findOne({ companyId })
        .select('+encryptedIgAccessToken +igAccessTokenIV');
      if (!data.igAccessToken?.trim() && !(existingConfig?.encryptedIgAccessToken)) {
        return { success: false, error: 'IG Access Token is required for Graph API method' };
      }
    }

    // Build update object
    const update: any = {
      companyId,
      webhookUrl: data.webhookUrl.replace(/\/+$/, ''),
      publishMethod,
      postType: data.postType || 'text',
      status: 'connected',
      updatedBy: data.updatedBy,
    };

    // ── Graph API fields ──
    update.igBusinessAccountId = data.igBusinessAccountId?.trim();
    if (data.igAccessToken?.trim()) {
      const { encrypted, iv } = encryptApiKey(data.igAccessToken.trim());
      update.encryptedIgAccessToken = encrypted;
      update.igAccessTokenIV = iv;
    }
    if (data.appId?.trim()) {
      const { encrypted, iv } = encryptApiKey(data.appId.trim());
      update.encryptedAppId = encrypted;
      update.appIdIV = iv;
    } else {
      update.encryptedAppId = '';
      update.appIdIV = '';
    }
    if (data.appSecret?.trim()) {
      const { encrypted, iv } = encryptApiKey(data.appSecret.trim());
      update.encryptedAppSecret = encrypted;
      update.appSecretIV = iv;
    } else {
      update.encryptedAppSecret = '';
      update.appSecretIV = '';
    }

    // ── Callback URL (auto-generated, kept across method switches) ──
    const existingForCallback = await InstagramN8nConfig.findOne({ companyId })
      .select('+encryptedCallbackToken +callbackTokenIV');
    let callbackToken: string;
    let encryptedCallbackToken: string;
    let callbackTokenIV: string;

    if (existingForCallback?.encryptedCallbackToken && existingForCallback.callbackTokenIV) {
      callbackToken = decryptCallbackToken(existingForCallback) || crypto.randomBytes(32).toString('hex');
      const encrypted = encryptApiKey(callbackToken);
      encryptedCallbackToken = encrypted.encrypted;
      callbackTokenIV = encrypted.iv;
    } else {
      callbackToken = crypto.randomBytes(32).toString('hex');
      const encrypted = encryptApiKey(callbackToken);
      encryptedCallbackToken = encrypted.encrypted;
      callbackTokenIV = encrypted.iv;
    }

    const webhookBaseUrl = process.env.WEBHOOK_BASE_URL || process.env.API_URL || 'http://localhost:3000';
    const callbackUrl = `${webhookBaseUrl.replace(/\/+$/, '')}/webhooks/instagram-n8n?companyId=${companyId}&token=${callbackToken}`;
    update.callbackUrl = callbackUrl;
    update.encryptedCallbackToken = encryptedCallbackToken;
    update.callbackTokenIV = callbackTokenIV;

    // Upsert config
    await InstagramN8nConfig.findOneAndUpdate(
      { companyId },
      update,
      { upsert: true, new: true },
    );

    return { success: true, callbackUrl };
  } catch (error: any) {
    console.error('[InstagramN8nConfig] Save error:', error);
    return { success: false, error: error.message || 'Failed to save Instagram N8n configuration' };
  }
}

// ============================================
// DELETE CONFIG
// ============================================

export async function deleteInstagramN8nConfig(companyId: string): Promise<{ success: boolean; error?: string }> {
  const { getModels } = await import('../../models');
  const { InstagramN8nConfig } = getModels();

  try {
    await InstagramN8nConfig.deleteOne({ companyId });
    return { success: true };
  } catch (error: any) {
    console.error('[InstagramN8nConfig] Delete error:', error);
    return { success: false, error: error.message || 'Failed to delete Instagram N8n configuration' };
  }
}

// ============================================
// DISCONNECT (marks as disconnected, keeps config)
// ============================================

export async function disconnectInstagramN8n(companyId: string): Promise<{ success: boolean; error?: string }> {
  const { getModels } = await import('../../models');
  const { InstagramN8nConfig } = getModels();

  try {
    await InstagramN8nConfig.findOneAndUpdate(
      { companyId },
      { status: 'disconnected' },
    );
    return { success: true };
  } catch (error: any) {
    console.error('[InstagramN8nConfig] Disconnect error:', error);
    return { success: false, error: error.message || 'Failed to disconnect Instagram N8n' };
  }
}

// ============================================
// GET CREDENTIALS (decrypted — for publisher internal use only)
// ============================================

export async function getInstagramN8nCredentials(companyId: string): Promise<{
  publishMethod: 'graph_api';
  webhookUrl: string;
  callbackUrl: string;
  postType: 'text' | 'photo' | 'reel' | 'story';
  // Graph API credentials
  igBusinessAccountId?: string;
  igAccessToken?: string;
  appId?: string;
  appSecret?: string;
} | null> {
  const { getModels } = await import('../../models');
  const { InstagramN8nConfig } = getModels();

  const config = await InstagramN8nConfig.findOne({ companyId })
    .select('+encryptedIgAccessToken +igAccessTokenIV +encryptedAppId +appIdIV +encryptedAppSecret +appSecretIV');

  if (!config || config.status !== 'connected') {
    return null;
  }

  const result: any = {
    publishMethod: 'graph_api' as const,
    webhookUrl: config.webhookUrl,
    callbackUrl: config.callbackUrl || '',
    postType: config.postType,
  };

  result.igBusinessAccountId = config.igBusinessAccountId;
  result.igAccessToken = decryptIgAccessToken(config) || undefined;
  result.appId = decryptAppId(config) || undefined;
  result.appSecret = decryptAppSecret(config) || undefined;

  return result;
}

// ============================================
// GET CALLBACK TOKEN (decrypted — for webhook verification only)
// ============================================

export async function getCallbackToken(companyId: string): Promise<string | null> {
  const { getModels } = await import('../../models');
  const { InstagramN8nConfig } = getModels();

  const config = await InstagramN8nConfig.findOne({ companyId })
    .select('+encryptedCallbackToken +callbackTokenIV');

  if (!config) return null;

  return decryptCallbackToken(config);
}

// ============================================
// TEST CONNECTION (sends test payload to n8n webhook URL)
// ============================================

export async function testInstagramN8nConnection(companyId: string): Promise<{
  success: boolean;
  message?: string;
  data?: any;
}> {
  const { getModels } = await import('../../models');
  const { InstagramN8nConfig } = getModels();

  const config = await InstagramN8nConfig.findOne({ companyId });

  if (!config) {
    return { success: false, message: 'Instagram N8n is not configured yet' };
  }

  if (!config.webhookUrl) {
    return { success: false, message: 'Webhook URL is not set' };
  }

  try {
    const testPayload: Record<string, any> = {
      test: true,
      source: 'mengo-instagram-n8n',
      publishMethod: 'graph_api',
      timestamp: new Date().toISOString(),
      callbackUrl: config.callbackUrl || '',
    };

    if (config.igBusinessAccountId) testPayload.igBusinessAccountId = config.igBusinessAccountId;

    const result = await testWebhook(config.webhookUrl, testPayload);

    // Update connection status based on result
    await InstagramN8nConfig.findOneAndUpdate(
      { companyId },
      {
        status: result.success ? 'connected' : 'disconnected',
      },
    );

    let message = result.message || (result.success ? 'Successfully connected to the n8n webhook' : 'Connection test failed');

    // Warn if the callback URL uses localhost
    if (result.success && config.callbackUrl && (config.callbackUrl.includes('://localhost') || config.callbackUrl.includes('://127.0.0.1'))) {
      message += ' ⚠️ Warning: The callback URL uses localhost. If n8n is running on a different server, it will not be able to reach Mengo for callbacks. Set the WEBHOOK_BASE_URL environment variable to your public URL.';
    }

    return {
      success: result.success,
      message,
      data: result.data,
    };
  } catch (error: any) {
    return {
      success: false,
      message: error.message || 'Failed to test connection',
    };
  }
}