import { FastifyInstance } from 'fastify';
import crypto from 'node:crypto';
import { env } from '../../config/env.js';
import { prisma } from '../../config/database.js';
import { logger } from '../../utils/logger.js';
import { isClaimComment, parseBidAmount, normalizeIgUsername } from '../../utils/helpers.js';
import { emitNewComment, emitNewBid, emitCartAdded, emitStockUpdated } from '../../websocket/socket.server.js';
import { sendCartWebhook } from '../../services/cart/webhook-sender.js';
import { replyCartAdded, replyBidAccepted, replyStockOut } from '../../services/notification/ig-reply.service.js';

export async function webhookRoutes(app: FastifyInstance) {

  // GET /api/v1/webhooks/instagram — Meta webhook verification
  app.get('/instagram', async (request, reply) => {
    const query = request.query as {
      'hub.mode'?: string;
      'hub.verify_token'?: string;
      'hub.challenge'?: string;
    };

    if (
      query['hub.mode'] === 'subscribe' &&
      query['hub.verify_token'] === env.meta.webhookVerifyToken
    ) {
      logger.info('[IG Webhook] Verification successful');
      return reply.status(200).send(query['hub.challenge']);
    }

    logger.warn('[IG Webhook] Verification failed');
    return reply.status(403).send('Forbidden');
  });

  // POST /api/v1/webhooks/instagram — Incoming comments from Meta
  app.post('/instagram', {
    config: { rawBody: true },
  }, async (request, reply) => {
    // Verify signature from Meta
    const signature = request.headers['x-hub-signature-256'] as string;
    if (signature && env.meta.appSecret) {
      const rawBody = JSON.stringify(request.body);
      const expectedSignature = `sha256=${crypto
        .createHmac('sha256', env.meta.appSecret)
        .update(rawBody)
        .digest('hex')}`;

      if (signature !== expectedSignature) {
        logger.warn('[IG Webhook] Invalid signature');
        return reply.status(403).send('Invalid signature');
      }
    }

    const body = request.body as MetaWebhookPayload;

    // Respond immediately (Meta requires 200 within 20 seconds)
    reply.status(200).send('EVENT_RECEIVED');

    // Process asynchronously
    try {
      await processWebhookPayload(body);
    } catch (err) {
      logger.error({ err }, '[IG Webhook] Error processing payload');
    }
  });
}

// ---------- Types ----------

interface MetaWebhookPayload {
  object: string;
  entry: Array<{
    id: string;
    time: number;
    changes?: Array<{
      field: string;
      value: {
        from: { id: string; username: string };
        media: { id: string; media_product_type?: string };
        id: string;
        text: string;
        timestamp: string;
      };
    }>;
    messaging?: unknown[];
  }>;
}

// ---------- Processing ----------

async function processWebhookPayload(payload: MetaWebhookPayload) {
  if (payload.object !== 'instagram') return;

  for (const entry of payload.entry) {
    if (!entry.changes) continue;

    for (const change of entry.changes) {
      if (change.field === 'comments' || change.field === 'live_comments') {
        await processComment(entry.id, change.value);
      }
    }
  }
}

async function processComment(
  igAccountId: string,
  comment: {
    from: { id: string; username: string };
    media: { id: string; media_product_type?: string };
    id: string;
    text: string;
    timestamp: string;
  },
) {
  const igUsername = normalizeIgUsername(comment.from.username);
  const text = comment.text.trim();

  logger.info({ igUsername, text, igAccountId }, '[IG Webhook] Processing comment');

  // Find the tenant first to emit the raw comment
  const tenantForComment = await prisma.tenant.findFirst({ where: { igAccountId } });
  if (tenantForComment) {
    emitNewComment(tenantForComment.id, {
      igUsername,
      text,
      timestamp: comment.timestamp,
    });
  }

  // Find the tenant by IG account ID
  const tenant = await prisma.tenant.findFirst({
    where: { igAccountId },
  });

  if (!tenant) {
    logger.warn({ igAccountId }, '[IG Webhook] No tenant found for IG account');
    return;
  }

  // Find active auction for this tenant's live session
  const activeAuction = await prisma.auctionItem.findFirst({
    where: {
      status: { in: ['active', 'countdown'] },
      liveSession: { tenantId: tenant.id, status: 'live' },
    },
    include: {
      product: true,
      liveSession: true,
    },
  });

  if (!activeAuction) {
    logger.debug('[IG Webhook] No active auction, skipping');
    return;
  }

  // Check if user is blocked or exists
  let user = await prisma.user.findFirst({
    where: { tenantId: tenant.id, igUsername },
  });

  if (user?.isBlocked) {
    logger.info({ igUsername }, '[IG Webhook] Blocked user, skipping');
    return;
  }

  // Auto-create user if not exists
  if (!user) {
    user = await prisma.user.create({
      data: {
        tenantId: tenant.id,
        igUsername,
        igUserId: comment.from.id,
        igAuthMethod: 'manual',
        displayName: comment.from.username,
      },
    });
  }

  // Determine comment type
  if (isClaimComment(text)) {
    await processClaimBid(tenant.id, activeAuction, user, comment);
  } else {
    const bidAmount = parseBidAmount(text);
    if (bidAmount !== null && activeAuction.mode === 'auction') {
      await processAuctionBid(tenant.id, activeAuction, user, bidAmount, comment);
    }
  }
}

async function processClaimBid(
  tenantId: string,
  auctionItem: any,
  user: any,
  comment: any,
) {
  if (auctionItem.mode === 'fixed_price') {
    // Check stock
    const existingClaims = await prisma.bid.count({
      where: {
        auctionItemId: auctionItem.id,
        bidType: 'claim',
        status: { in: ['pending', 'won'] },
      },
    });

    if (existingClaims >= auctionItem.product.stockQuantity) {
      // Add to waitlist (record anyway)
      await prisma.bid.create({
        data: {
          auctionItemId: auctionItem.id,
          userId: user.id,
          igUsername: user.igUsername,
          igCommentId: comment.id,
          bidType: 'claim',
          amount: auctionItem.currentPrice,
          status: 'lost',
          isMatched: !!user.email || !!user.externalUserId,
          commentTimestamp: new Date(comment.timestamp),
          processedAt: new Date(),
        },
      });
      logger.info({ igUsername: user.igUsername }, '[Auction] Stock exhausted, added to waitlist');

      // IG auto-reply: stock exhausted
      replyStockOut(tenantId, comment.id, user.igUsername).catch(() => {});
      return;
    }

    // Check duplicate claim from same user
    const existingClaim = await prisma.bid.findFirst({
      where: {
        auctionItemId: auctionItem.id,
        userId: user.id,
        bidType: 'claim',
        status: { in: ['pending', 'won'] },
      },
    });
    if (existingClaim) {
      logger.debug({ igUsername: user.igUsername }, '[Auction] Duplicate claim, skipping');
      return;
    }

    // Create winning bid
    const bid = await prisma.bid.create({
      data: {
        auctionItemId: auctionItem.id,
        userId: user.id,
        igUsername: user.igUsername,
        igCommentId: comment.id,
        bidType: 'claim',
        amount: auctionItem.currentPrice,
        status: 'won',
        isMatched: !!user.email || !!user.externalUserId,
        commentTimestamp: new Date(comment.timestamp),
        processedAt: new Date(),
      },
    });

    // Create cart event
    await prisma.cartEvent.create({
      data: {
        tenantId,
        bidId: bid.id,
        userId: user.id,
        auctionItemId: auctionItem.id,
        status: 'pending',
      },
    });

    logger.info({ igUsername: user.igUsername, productName: auctionItem.product.name }, '[Auction] Claim accepted, cart event created');

    // Emit to dashboard
    emitCartAdded(tenantId, {
      igUsername: user.igUsername,
      productName: auctionItem.product.name,
      amount: Number(auctionItem.currentPrice),
    });
    emitNewBid(tenantId, {
      igUsername: user.igUsername,
      amount: Number(auctionItem.currentPrice),
      bidType: 'claim',
      productName: auctionItem.product.name,
      status: 'won',
    });

    // Update stock display
    const remainingStock = auctionItem.product.stockQuantity - (existingClaims + 1);
    emitStockUpdated(tenantId, {
      productId: auctionItem.product.id,
      remaining: Math.max(0, remainingStock),
    });

    // Send webhook to external e-commerce site
    sendCartWebhook(cartEvent.id).catch(() => {});

    // IG comment auto-reply
    replyCartAdded(tenantId, comment.id, user.igUsername).catch(() => {});
  }
}

async function processAuctionBid(
  tenantId: string,
  auctionItem: any,
  user: any,
  amount: number,
  comment: any,
) {
  // Validate bid amount
  const currentPrice = Number(auctionItem.currentPrice);
  const minIncrement = Number(auctionItem.minBidIncrement ?? 10);

  if (amount <= currentPrice) {
    logger.debug({ igUsername: user.igUsername, amount, currentPrice }, '[Auction] Bid too low');
    return;
  }

  if (amount < currentPrice + minIncrement) {
    logger.debug({ igUsername: user.igUsername, amount, required: currentPrice + minIncrement }, '[Auction] Bid below min increment');
    return;
  }

  // Create bid
  const bid = await prisma.bid.create({
    data: {
      auctionItemId: auctionItem.id,
      userId: user.id,
      igUsername: user.igUsername,
      igCommentId: comment.id,
      bidType: 'bid',
      amount,
      status: 'pending',
      isMatched: !!user.email || !!user.externalUserId,
      commentTimestamp: new Date(comment.timestamp),
    },
  });

  // Update current price
  await prisma.auctionItem.update({
    where: { id: auctionItem.id },
    data: { currentPrice: amount },
  });

  logger.info({ igUsername: user.igUsername, amount, productName: auctionItem.product.name }, '[Auction] Bid accepted');

  // Emit to dashboard
  emitNewBid(tenantId, {
    igUsername: user.igUsername,
    amount,
    bidType: 'bid',
    productName: auctionItem.product.name,
    status: 'pending',
  });

  // IG comment auto-reply with current highest bid
  replyBidAccepted(tenantId, comment.id, user.igUsername, amount, amount).catch(() => {});
}
