import { FastifyRequest, FastifyReply } from 'fastify';
import { v4 as uuidv4 } from 'uuid';
import { prisma } from '../db/client';
import { getRedis, REDIS_KEYS } from '../redis';
import { config } from '../config';
import { publishEvent } from '../tg/notifier';
import { trackProductView } from '../stats/productStats';
import { extractClientIp, formatUserAgent, getGeo } from '../geo';
import { isBot } from '../security/botDetector';

export async function visitorMiddleware(
  request: FastifyRequest,
  reply: FastifyReply,
): Promise<void> {
  // Skip bots, crawlers, and automated tools entirely — no DB record, no Telegram notification
  if (isBot(request.headers['user-agent'])) return;
  const cookieName = config.session.visitorCookieName;
  let visitorId: string = (request.cookies as Record<string, string>)[cookieName] ?? '';

  const isNew = !visitorId;
  if (isNew) {
    visitorId = uuidv4();
    reply.setCookie(cookieName, visitorId, {
      path: '/',
      maxAge: config.session.visitorMaxAge,
      httpOnly: true,
      sameSite: 'lax',
    });
  }

  // Attach to request for later use
  (request as FastifyRequest & { visitorId: string }).visitorId = visitorId;

  // Update "online now" sorted set (TTL-based presence)
  const redis = getRedis();
  const now = Date.now();
  await redis.zadd(REDIS_KEYS.onlineVisitors, now, visitorId);
  // Remove stale entries older than the online window
  const cutoff = now - config.onlineWindowSec * 1000;
  await redis.zremrangebyscore(REDIS_KEYS.onlineVisitors, '-inf', cutoff);
  await trackProductView(request.url, visitorId);

  if (!isNew) return;

  // New visitor: persist to DB and send Telegram notification
  const ip = extractClientIp(request);
  const geo = await getGeo(ip);
  const ua = request.headers['user-agent'];
  const referer = request.headers['referer'];
  const landingUrl = request.url;

  try {
    const dbVisitor = await prisma.visitor.upsert({
      where: { uuid: visitorId },
      update: {},
      create: {
        uuid: visitorId,
        ip,
        country: geo.countryCode,
        city: geo.city,
        userAgent: ua,
        referer,
        landingUrl,
      },
    });

    await prisma.visitorEvent.create({
      data: {
        visitorId: dbVisitor.id,
        type: 'visit.new',
        data: {
          visitorId: visitorId.slice(0, 8),
          ip,
          country: geo.countryCode,
          countryCode: geo.countryCode,
          countryName: geo.countryName,
          city: geo.city,
          flag: geo.flag,
          geoSource: geo.source,
          userAgent: ua,
          device: formatUserAgent(ua),
          referer,
          landingUrl,
          currentUrl: landingUrl,
          at: new Date().toISOString(),
        },
      },
    });

    await publishEvent({
      type: 'visit.new',
      visitorId: visitorId.slice(0, 8),
      ip,
      country: geo.countryCode,
      countryName: geo.countryName,
      city: geo.city,
      flag: geo.flag,
      geoSource: geo.source,
      userAgent: ua,
      referer,
      landingUrl,
      at: new Date().toISOString(),
    });
  } catch (err) {
    console.error('[visitor] DB error:', (err as Error).message);
  }
}
