import { Queue, Worker } from 'bullmq';
import { config } from '../config';
import { getRedis, REDIS_KEYS } from '../redis';
import type { TgEvent } from './events';
import { countryLabel, formatUserAgent } from '../geo';

const QUEUE_NAME = 'tg-notifications';

let _queue: Queue | null = null;

function getQueue(): Queue {
  if (!_queue) {
    _queue = new Queue(QUEUE_NAME, {
      connection: { url: config.redis.url },
      defaultJobOptions: {
        attempts: 3,
        backoff: { type: 'exponential', delay: 2000 },
        removeOnComplete: 100,
        removeOnFail: 200,
      },
    });
  }
  return _queue;
}

/** Publish an event to the Telegram notification queue (admin logs group). */
export async function publishEvent(event: TgEvent): Promise<void> {
  if (!config.tg.token || config.tg.adminChatIds.length === 0) return;
  try {
    await getQueue().add(event.type, event, {
      // Deduplicate rapid visit.new events from the same visitor within 1s
      jobId:
        event.type === 'visit.new'
          ? `visit-${event.visitorId}-${Math.floor(Date.now() / 1000)}`
          : undefined,
    });
  } catch (err) {
    console.error('[notifier] queue error:', (err as Error).message);
  }
}

/** Format and send event messages to all admin chat IDs */
export function startNotificationWorker(
  sendMessage: (chatId: string, text: string, extra?: object) => Promise<void>,
): void {
  if (!config.tg.token || config.tg.adminChatIds.length === 0) {
    console.log('[notifier] Telegram not configured, skipping worker');
    return;
  }

  new Worker(
    QUEUE_NAME,
    async (job) => {
      const event = job.data as TgEvent;
      const { text, keyboard } = formatEvent(event);

      for (const chatId of config.tg.adminChatIds) {
        await sendMessage(chatId, text, keyboard ? { reply_markup: keyboard } : {});
      }
    },
    {
      connection: { url: config.redis.url },
      concurrency: 1,
      limiter: { max: 20, duration: 1000 }, // stay under Telegram's 30 msg/s
    },
  );

  console.log('[notifier] BullMQ worker started');
}

// ---- Message formatters ----

function formatEvent(event: TgEvent): { text: string; keyboard?: object } {
  switch (event.type) {
    case 'visit.new':
      return {
        text:
          `🆕 <b>Новый визит</b> · <code>#${event.visitorId}</code>\n` +
          `🕐 ${fmtTime(event.at)}\n` +
          `🌐 IP: <code>${esc(event.ip)}</code>\n` +
          `🌍 ${esc(countryLabel(event.country, event.city, event.countryName))}` +
          (event.geoSource ? ` · <i>${esc(event.geoSource)}</i>` : '') + '\n' +
          `🖥 ${esc(formatUserAgent(event.userAgent))}\n` +
          (event.referer ? `↩️ Source: <code>${esc(event.referer)}</code>\n` : '') +
          `📄 URL: <code>${esc(event.landingUrl)}</code>`,
        keyboard: {
          inline_keyboard: [
            [{ text: '📋 История визитора', callback_data: `visitor:${event.visitorId}:history` }],
          ],
        },
      };

    case 'cart.add':
      return {
        text:
          `🛒 <b>Добавил в корзину</b> · <code>#${event.visitorId}</code>\n` +
          `🕐 ${fmtTime(event.at)}\n` +
          (event.ip ? `🌐 <code>${esc(event.ip)}</code> · ${esc(countryLabel(event.country, event.city, event.countryName))}\n` : '') +
          `🖥 ${esc(event.device || formatUserAgent(event.userAgent))}\n` +
          `📦 ${esc(event.productName)} — <b>${event.price.toFixed(2)} ${event.currency}</b>` +
          (event.size ? ` (${esc(event.size)})` : '') + ` ×${event.quantity}\n` +
          (event.referer ? `↩️ Source: <code>${esc(event.referer)}</code>\n` : '') +
          (event.sourceUrl ? `📄 URL: <code>${esc(event.sourceUrl)}</code>\n` : '') +
          `🧮 Корзина: ${event.cartItemsCount} товара / <b>${event.cartTotal.toFixed(2)} ${event.currency}</b>`,
        keyboard: {
          inline_keyboard: [
            [{ text: '📋 История визитора', callback_data: `visitor:${event.visitorId}:history` }],
          ],
        },
      };

    case 'cart.remove':
      return {
        text:
          `🗑 <b>Удалён из корзины</b> · <code>#${event.visitorId}</code>\n` +
          `🕐 ${fmtTime(event.at)}\n` +
          (event.ip ? `🌐 <code>${esc(event.ip)}</code> · ${esc(countryLabel(event.country, event.city, event.countryName))}\n` : '') +
          (event.device ? `🖥 ${esc(event.device)}\n` : '') +
          `📦 ${esc(event.productName)}` +
          (event.quantity ? ` ×${event.quantity}` : '') +
          (event.cartTotal !== undefined ? `\n🧮 Осталось: ${event.cartItemsCount ?? 0} / <b>${event.cartTotal.toFixed(2)} ${event.currency ?? 'PLN'}</b>` : ''),
      };

    case 'checkout.start': {
      const itemsList = event.items
        .slice(0, 8)
        .map(
          (i) =>
            `  • ${esc(i.name)}${i.size ? ` (${i.size})` : ''} ×${i.qty} — ${(i.price * i.qty).toFixed(2)} ${event.currency}`,
        )
        .join('\n');
      return {
        text:
          `🛍 <b>Переход к оформлению</b> · <code>#${event.visitorId}</code>\n` +
          `🕐 ${fmtTime(event.at)}\n` +
          (event.ip ? `🌐 <code>${esc(event.ip)}</code> · ${esc(countryLabel(event.country, event.city, event.countryName))}\n` : '') +
          (event.device ? `🖥 ${esc(event.device)}\n` : '') +
          `🧮 Корзина: ${event.cartItemsCount} / <b>${event.cartTotal.toFixed(2)} ${event.currency}</b>\n` +
          (itemsList ? `<b>Товары:</b>\n${itemsList}` : ''),
      };
    }

    case 'order.new': {
      const itemsList = event.items
        .map((i) => `  • ${esc(i.name)}${i.size ? ` (${i.size})` : ''} ×${i.qty} — ${(i.price * i.qty).toFixed(2)} ${event.currency}`)
        .join('\n');
      return {
        text:
          `💰 <b>НОВЫЙ ЗАКАЗ #${event.orderId}</b> · ${event.total.toFixed(2)} ${event.currency}\n` +
          `🕐 ${fmtTime(event.at)}\n` +
          `📋 Ref: <code>${esc(event.reference)}</code> · 💳 ${esc(event.paymentMethod)}\n` +
          (event.visitorId ? `👁 Visitor: <code>#${esc(event.visitorId)}</code>\n` : '') +
          (event.visitorIp ? `🌐 <code>${esc(event.visitorIp)}</code> · ${esc(countryLabel(event.visitorCountry, event.visitorCity, event.visitorCountryName))}\n` : '') +
          (event.visitorDevice ? `🖥 ${esc(event.visitorDevice)}\n` : '') +
          (event.referer ? `↩️ Source: <code>${esc(event.referer)}</code>\n` : '') +
          (event.landingUrl ? `📄 Landing: <code>${esc(event.landingUrl)}</code>\n` : '') +
          `👤 ${esc(event.name)} · 📞 ${esc(event.phone)}\n` +
          (event.email ? `📧 ${esc(event.email)}\n` : '') +
          `📍 ${esc(event.city)}, ${esc(event.address)}\n\n` +
          `<b>Товары:</b>\n${itemsList}`,
        keyboard: {
          inline_keyboard: [
            [
              { text: '✅ Обработан', callback_data: `order:${event.orderId}:done` },
              { text: '❌ Отмена', callback_data: `order:${event.orderId}:cancel` },
            ],
          ],
        },
      };
    }

    case 'payment.update': {
      const statusEmoji: Record<string, string> = {
        paid: '✅',
        failed: '❌',
        cancelled: '🚫',
        processing: '⏳',
      };
      const emoji = statusEmoji[event.status] ?? '🔔';
      return {
        text:
          `${emoji} <b>Статус оплаты изменён</b>\n` +
          `🕐 ${fmtTime(event.at)}\n` +
          `Заказ <b>#${event.orderId}</b> (ref <code>${esc(event.reference)}</code>)\n` +
          (event.visitorId ? `👁 Visitor: <code>#${esc(event.visitorId)}</code>\n` : '') +
          (event.visitorIp ? `🌐 <code>${esc(event.visitorIp)}</code> · ${esc(countryLabel(event.visitorCountry, event.visitorCity, event.visitorCountryName))}\n` : '') +
          `Статус: <b>${esc(event.status)}</b> · 💳 ${esc(event.paymentMethod)}\n` +
          `Сумма: ${event.total.toFixed(2)} ${event.currency}`,
      };
    }

    default:
      return { text: `📬 Событие: <code>${esc(JSON.stringify(event))}</code>` };
  }
}

function esc(text?: string): string {
  if (!text) return '';
  return text
    .replace(/&/g, '&amp;')
    .replace(/</g, '&lt;')
    .replace(/>/g, '&gt;')
    .replace(/"/g, '&quot;');
}

function fmtTime(value: string): string {
  try {
    return new Date(value).toLocaleString('ru-RU', { timeZone: 'Europe/Warsaw' });
  } catch {
    return value;
  }
}
