/**
 * Threads N8n Callback Webhook Route
 *
 * Receives publication result callbacks from n8n workflows.
 * This is a PUBLIC endpoint (no authentication) because n8n calls it.
 * Security is provided by a per-company verification token in the query string.
 *
 * Expected callback payload from n8n:
 * {
 *   publicationId: string,
 *   status: 'published' | 'failed',
 *   platformPostId?: string,       // Threads post ID on success
 *   platformUrl?: string,          // Threads post URL on success
 *   error?: { code: string, message: string }  // Error details on failure
 * }
 *
 * The token is verified using timing-safe comparison to prevent timing attacks.
 */

import express, { Request, Response } from 'express';
import crypto from 'crypto';
import { getModels } from '../models';
import { getCallbackToken } from '../services/threads-n8n/threadsN8nConfigService';

const router = express.Router();

/**
 * Constant-time string comparison to avoid leaking the secret via timing.
 */
function timingSafeEqualStr(a: string, b: string): boolean {
  const ab = Buffer.from(a);
  const bb = Buffer.from(b);
  if (ab.length !== bb.length) return false;
  return crypto.timingSafeEqual(ab, bb);
}

/**
 * POST /threads-n8n — Callback from n8n workflow
 *
 * Query params:
 *   companyId — required, identifies the tenant
 *   token — required, must match the stored callback verification token
 */
router.post('/threads-n8n', async (req: Request, res: Response) => {
  const { companyId, token } = req.query;

  // ── Verify required query params ──
  if (!companyId || typeof companyId !== 'string') {
    return res.status(400).json({ error: 'Missing companyId parameter' });
  }
  if (!token || typeof token !== 'string') {
    return res.status(400).json({ error: 'Missing token parameter' });
  }

  // ── Verify the callback token ──
  const storedToken = await getCallbackToken(companyId);
  if (!storedToken) {
    return res.status(401).json({ error: 'No callback configuration found for this company' });
  }
  if (!timingSafeEqualStr(token, storedToken)) {
    return res.status(401).json({ error: 'Invalid token' });
  }

  // ── Parse the callback payload ──
  const { publicationId, status, platformPostId, platformUrl, error } = req.body;

  if (!publicationId) {
    return res.status(400).json({ error: 'Missing publicationId' });
  }
  if (!status || !['published', 'failed'].includes(status)) {
    return res.status(400).json({ error: 'Invalid status. Must be "published" or "failed"' });
  }

  try {
    const { SocialMediaPublication } = getModels();

    // Find the publication, ensuring it belongs to this company and platform
    const publication = await SocialMediaPublication.findOne({
      _id: publicationId,
      companyId,
      platform: 'threads-n8n',
    });

    if (!publication) {
      return res.status(404).json({ error: 'Publication not found' });
    }

    // Only process publications that are in a state that can receive callbacks
    if (publication.status !== 'processing' && publication.status !== 'uploading') {
      return res.status(200).json({
        message: `Publication is in status "${publication.status}", callback ignored`,
        publicationId,
      });
    }

    if (status === 'published') {
      // ── Success: mark as published ──
      publication.status = 'published';
      publication.publishedAt = new Date();
      publication.workerLockedAt = null;

      if (platformPostId) {
        publication.platformPostId = platformPostId;
      }
      if (platformUrl) {
        publication.platformUrl = platformUrl;
      }

      await publication.save();

      console.log(`[ThreadsN8n] Publication ${publicationId} published successfully. Post ID: ${platformPostId}`);

      // Notify the user
      try {
        const { notificationService } = await import('../services/notificationService');
        void notificationService.notifyUser(publication.createdBy, {
          type: 'social.publish.completed',
          message: 'Your Threads N8n post is live.',
          organizationId: publication.companyId,
          entityType: 'social_publication',
          entityId: String(publication._id),
          actionUrl: '/social-media-os',
          notifyActor: true,
        });
      } catch (notifyErr) {
        console.error('[ThreadsN8n] Failed to send publication notification:', notifyErr);
      }

      return res.status(200).json({
        message: 'Publication marked as published',
        publicationId,
        status: 'published',
      });
    }

    if (status === 'failed') {
      // ── Failure: mark as failed ──
      publication.status = 'failed';
      publication.lastError = {
        code: error?.code || 'n8n_callback_error',
        message: error?.message || 'n8n workflow reported a failure',
        at: new Date(),
      };
      publication.errorHistory = publication.errorHistory || [];
      publication.errorHistory.push(publication.lastError);
      publication.workerLockedAt = null;

      await publication.save();

      console.error(`[ThreadsN8n] Publication ${publicationId} failed: ${error?.message || 'unknown error'}`);

      // Notify the user
      try {
        const { notificationService } = await import('../services/notificationService');
        void notificationService.notifyUser(publication.createdBy, {
          type: 'social.publish.failed',
          message: `Publishing to Threads N8n failed: ${error?.message || 'the workflow reported an error'}`,
          organizationId: publication.companyId,
          entityType: 'social_publication',
          entityId: String(publication._id),
          actionUrl: '/social-media-os',
          notifyActor: true,
        });
      } catch (notifyErr) {
        console.error('[ThreadsN8n] Failed to send failure notification:', notifyErr);
      }

      return res.status(200).json({
        message: 'Publication marked as failed',
        publicationId,
        status: 'failed',
      });
    }

    // Should not reach here due to the status check above
    return res.status(400).json({ error: 'Unhandled status' });
  } catch (err: any) {
    console.error('[ThreadsN8n] Callback processing error:', err);
    // Still return 200 to prevent n8n from retrying endlessly
    return res.status(200).json({
      message: 'Callback received but processing encountered an error',
      error: err.message,
    });
  }
});

export default router;