import { prisma } from '../db/client';
import { getRedis, REDIS_KEYS } from '../redis';

const PRODUCT_URL_RE = /^\/(?:[a-z]{2}\/[a-z]{2}\/)?(.+)-([0-9a-z]{3,}[0-9a-z-]*)\/?$/i;
const DEDUP_TTL_SECONDS = 30 * 60;
const FLUSH_INTERVAL_MS = 60_000;
const FLUSH_BATCH_SIZE = 200;

let flushTimer: NodeJS.Timeout | null = null;
let flushRunning = false;

export interface ProductPage {
  productId: string;
  slug: string;
  name: string;
  url: string;
}

export function extractProductPage(url: string): ProductPage | null {
  const path = url.split('?')[0] ?? url;
  if (
    path.includes('/checkout/') ||
    path.includes('/customer/') ||
    path.includes('/skin/') ||
    path.includes('/media/') ||
    path.includes('/ajx/') ||
    path.includes('/api/')
  ) {
    return null;
  }

  const match = path.match(PRODUCT_URL_RE);
  if (!match) return null;

  const slug = match[1];
  const productId = match[2].toUpperCase();
  if (!slug || !productId.includes('-')) return null;

  return {
    productId,
    slug,
    name: titleFromSlug(slug),
    url: path,
  };
}

export async function trackProductView(url: string, visitorId: string): Promise<void> {
  const product = extractProductPage(url);
  if (!product) return;

  const redis = getRedis();
  const dedupKey = REDIS_KEYS.productViewDedup(visitorId, product.productId);
  const counted = await redis.set(dedupKey, '1', 'EX', DEDUP_TTL_SECONDS, 'NX');
  if (counted !== 'OK') return;

  await redis
    .pipeline()
    .hincrby(REDIS_KEYS.productStatsViews, product.productId, 1)
    .hmset(REDIS_KEYS.productStatsMeta(product.productId), {
      productId: product.productId,
      slug: product.slug,
      name: product.name,
      url: product.url,
      lastSeenAt: new Date().toISOString(),
    })
    .pfadd(REDIS_KEYS.productStatsUnique(product.productId), visitorId)
    .sadd(REDIS_KEYS.productStatsDirty, product.productId)
    .exec();
}

export function startProductStatsFlusher(): void {
  if (flushTimer) return;
  flushTimer = setInterval(() => {
    flushProductStats().catch((err) => {
      console.error('[product-stats] flush error:', err);
    });
  }, FLUSH_INTERVAL_MS);
  flushTimer.unref();
  console.log('[product-stats] flusher started');
}

export async function flushProductStats(): Promise<void> {
  if (flushRunning) return;
  flushRunning = true;

  try {
    const redis = getRedis();
    const productIds = await redis.spop(REDIS_KEYS.productStatsDirty, FLUSH_BATCH_SIZE);
    const ids = Array.isArray(productIds) ? productIds : productIds ? [productIds] : [];
    if (ids.length === 0) return;

    for (const productId of ids) {
      const [viewsRaw, meta, uniqueVisitorsRaw] = await Promise.all([
        redis.hget(REDIS_KEYS.productStatsViews, productId),
        redis.hgetall(REDIS_KEYS.productStatsMeta(productId)),
        redis.pfcount(REDIS_KEYS.productStatsUnique(productId)),
      ]);

      const viewDelta = parseInt(viewsRaw ?? '0', 10);
      if (viewDelta <= 0) continue;

      await prisma.productStat.upsert({
        where: { productId },
        create: {
          productId,
          slug: meta.slug || productId.toLowerCase(),
          name: meta.name || productId,
          url: meta.url || '/',
          totalViews: viewDelta,
          uniqueVisitors: uniqueVisitorsRaw,
          lastSeenAt: meta.lastSeenAt ? new Date(meta.lastSeenAt) : new Date(),
        },
        update: {
          slug: meta.slug || undefined,
          name: meta.name || undefined,
          url: meta.url || undefined,
          totalViews: { increment: viewDelta },
          uniqueVisitors: uniqueVisitorsRaw,
          lastSeenAt: meta.lastSeenAt ? new Date(meta.lastSeenAt) : new Date(),
        },
      });

      await redis.hincrby(REDIS_KEYS.productStatsViews, productId, -viewDelta);
    }
  } finally {
    flushRunning = false;
  }
}

function titleFromSlug(slug: string): string {
  return slug
    .split('-')
    .filter(Boolean)
    .map((part) => part.charAt(0).toUpperCase() + part.slice(1))
    .join(' ');
}
