/**
 * Notification Service — fires alerts across email/in-app/SMS channels
 * and evaluates alert conditions against current platform data.
 */

import { prisma } from "@/lib/prisma";
import { sendEmail } from "./email-sender";
import { sendSms } from "./sms-sender";
import { countedConversionWhere } from "@/lib/conversions/counted-where";

// ─── Condition evaluator ────────────────────────────────────────────────────

interface AlertCondition { metric: string; operator: string; threshold: number; period: string }

function evaluateCondition(cond: AlertCondition, current: number, previous: number): boolean {
  switch (cond.operator) {
    case "drops_by": return previous > 0 && ((previous - current) / previous * 100) >= cond.threshold;
    case "increases_by": return previous > 0 && ((current - previous) / previous * 100) >= cond.threshold;
    case "falls_below": return current < cond.threshold;
    case "exceeds": return current > cond.threshold;
    case "new_event": return current > 0;
    default: return false;
  }
}

function periodDays(period: string): number {
  if (period === "1d") return 1;
  if (period === "7d") return 7;
  if (period === "14d") return 14;
  return 30;
}

// ─── Fire alert across channels ─────────────────────────────────────────────

interface AlertData { title: string; message: string; link?: string; currentValue?: number; previousValue?: number }

export async function fireAlert(ruleId: string, data: AlertData): Promise<void> {
  const rule = await prisma.alertRule.findUnique({ where: { id: ruleId } });
  if (!rule || !rule.isActive) return;

  // Cooldown check
  if (rule.lastFiredAt) {
    const cooldownMs = (rule.cooldownMinutes || 60) * 60000;
    if (Date.now() - rule.lastFiredAt.getTime() < cooldownMs) return;
  }

  const channels = rule.channels ?? [];
  const metadata = { link: data.link, currentValue: data.currentValue, previousValue: data.previousValue } as object;
  const sevLabel = rule.severity === "critical" ? "CRITICAL" : rule.severity === "high" ? "HIGH" : rule.severity;

  // In-app notification
  if (channels.includes("in_app")) {
    await prisma.notification.create({
      data: { brandId: rule.brandId, alertRuleId: ruleId, title: data.title, message: data.message, type: "alert", severity: rule.severity, channel: "in_app", metadata },
    });
  }

  // Email
  if (channels.includes("email") && rule.recipients.length > 0) {
    const subject = `[${sevLabel}] ${data.title}`;
    const html = `<div style="font-family:sans-serif;max-width:500px"><div style="background:${rule.severity === "critical" ? "#ef4444" : rule.severity === "high" ? "#f97316" : "#3b82f6"};color:#fff;padding:16px 20px;border-radius:8px 8px 0 0;font-weight:700">${sevLabel}: ${data.title}</div><div style="padding:20px;border:1px solid #e2e8f0;border-top:none;border-radius:0 0 8px 8px"><p style="margin:0 0 12px;color:#334155">${data.message}</p>${data.link ? `<a href="${data.link}" style="display:inline-block;padding:10px 20px;background:#EF4444;color:#fff;border-radius:6px;text-decoration:none;font-weight:600;font-size:14px">View in meSEO</a>` : ""}</div></div>`;
    await sendEmail(rule.recipients, subject, html);
    await prisma.notification.create({
      data: { brandId: rule.brandId, alertRuleId: ruleId, title: data.title, message: data.message, type: "alert", severity: rule.severity, channel: "email", emailSent: true, metadata },
    });
  }

  // SMS
  if (channels.includes("sms") && rule.smsRecipients.length > 0) {
    const smsMsg = `[meSEO ${sevLabel}] ${data.title}${data.link ? ` View: ${data.link}` : ""}`;
    for (const phone of rule.smsRecipients) {
      const result = await sendSms(phone, smsMsg);
      await prisma.notification.create({
        data: { brandId: rule.brandId, alertRuleId: ruleId, title: data.title, message: data.message, type: "alert", severity: rule.severity, channel: "sms", smsSent: result.success, smsError: result.error ?? null, metadata },
      });
    }
  }

  await prisma.alertRule.update({ where: { id: ruleId }, data: { lastFiredAt: new Date() } });
}

// ─── Check all conditions for a brand ───────────────────────────────────────

export async function checkAlertConditions(brandId: string): Promise<number> {
  const rules = await prisma.alertRule.findMany({ where: { brandId, isActive: true } });
  let fired = 0;
  const APP_URL = process.env.NEXT_PUBLIC_APP_URL || "https://app.meseo.io";

  for (const rule of rules) {
    try {
      const cond = rule.condition as unknown as AlertCondition;
      const days = periodDays(cond.period);
      const now = new Date();
      const since = new Date(now.getTime() - days * 86400000);
      const prevSince = new Date(since.getTime() - days * 86400000);
      let current = 0, previous = 0, shouldFire = false, title = "", message = "";

      if (rule.type === "traffic_drop" || rule.type === "conversion_drop") {
        const metric = cond.metric === "conversions" ? "conversions" : "clicks";
        if (metric === "clicks") {
          const cur = await prisma.gscPage.aggregate({ where: { brandId, date: { gte: since } }, _sum: { clicks: true } });
          const prev = await prisma.gscPage.aggregate({ where: { brandId, date: { gte: prevSince, lt: since } }, _sum: { clicks: true } });
          current = cur._sum.clicks ?? 0; previous = prev._sum.clicks ?? 0;
        } else {
          current = await prisma.siteConversion.count({ where: countedConversionWhere(brandId, { since }) });
          previous = await prisma.siteConversion.count({ where: { ...countedConversionWhere(brandId), timestamp: { gte: prevSince, lt: since } } });
        }
        shouldFire = evaluateCondition(cond, current, previous);
        title = `${metric === "clicks" ? "Traffic" : "Conversions"} dropped ${previous > 0 ? Math.round((previous - current) / previous * 100) : 0}%`;
        message = `${metric} went from ${previous} to ${current} over the last ${days} days.`;
      } else if (rule.type === "new_review") {
        const badReviews = await prisma.gbpReview.count({ where: { location: { brandId }, rating: { lte: cond.threshold }, publishedAt: { gte: since } } });
        current = badReviews; shouldFire = badReviews > 0;
        title = `${badReviews} negative review${badReviews !== 1 ? "s" : ""} received`;
        message = `${badReviews} review${badReviews !== 1 ? "s" : ""} with rating ${cond.threshold} or below in the last ${days} day${days !== 1 ? "s" : ""}.`;
      } else if (rule.type === "page_404") {
        const new404s = await prisma.pageRecord.count({ where: { brandId, currentStatus: 404 } });
        current = new404s; shouldFire = new404s > 0;
        title = `${new404s} page${new404s !== 1 ? "s" : ""} returned 404`;
        message = `Detected ${new404s} broken page${new404s !== 1 ? "s" : ""} on your site.`;
      } else if (rule.type === "backlink_alert") {
        const toxic = await prisma.backlink.count({ where: { brandId, toxicityScore: { gte: 60 }, firstSeenAt: { gte: since } } });
        current = toxic; shouldFire = toxic > 0;
        title = `${toxic} toxic backlink${toxic !== 1 ? "s" : ""} detected`;
        message = `New potentially harmful backlinks found. Review and consider disavowing.`;
      }

      if (shouldFire) {
        await fireAlert(rule.id, { title, message, link: `${APP_URL}/dashboard`, currentValue: current, previousValue: previous });
        fired++;
      }
    } catch (err) {
      console.error(`[alerts] rule ${rule.id} failed`, err);
    }
  }
  return fired;
}

// ─── Default rules ──────────────────────────────────────────────────────────

export async function createDefaultRules(brandId: string): Promise<number> {
  const defaults = [
    { name: "Traffic Drop", type: "traffic_drop", condition: { metric: "clicks", operator: "drops_by", threshold: 20, period: "7d" }, severity: "high", channels: ["email", "in_app"] },
    { name: "Bad Review Received", type: "new_review", condition: { metric: "review_rating", operator: "falls_below", threshold: 3, period: "1d" }, severity: "critical", channels: ["email", "in_app", "sms"] },
    { name: "Page Went 404", type: "page_404", condition: { metric: "status", operator: "new_event", threshold: 1, period: "1d" }, severity: "medium", channels: ["email", "in_app"] },
    { name: "Conversion Drop", type: "conversion_drop", condition: { metric: "conversions", operator: "drops_by", threshold: 30, period: "7d" }, severity: "high", channels: ["email", "in_app"] },
    { name: "Toxic Backlink", type: "backlink_alert", condition: { metric: "toxicity", operator: "new_event", threshold: 1, period: "1d" }, severity: "medium", channels: ["email", "in_app"] },
  ];
  for (const d of defaults) {
    await prisma.alertRule.create({ data: { brandId, ...d, condition: d.condition as object, recipients: [], smsRecipients: [] } });
  }
  return defaults.length;
}
