import cron from "node-cron";
import { prisma } from "./config/prisma.js";
import { computePriorityForDate } from "./utils/priority.js";
import { sendEmail, sendExpiryAlert } from "./utils/email.js";

interface ExpirySource {
  kind: "document" | "employee_iqama" | "employee_passport" | "employee_insurance" | "company_cr";
  entityId: number;
  documentId?: number | null;
  entityType: "company" | "employee";
  entityName: string;
  companyId: number;
  companyName: string;
  title: string;
  expiryDate: Date;
  targetEmail: string | null;
}

async function getSettings() {
  const settings = await prisma.setting.findMany({
    where: { key: { in: ["alert_days", "alert_periods", "alert_expired", "cron_time", "smtp_host", "notify_email"] } },
  });
  const get = (k: string, d = "") => settings.find((s) => s.key === k)?.value ?? d;
  return {
    alertDays: Number(get("alert_days", "30")),
    alertPeriods: get("alert_periods", "60,30,14,7,3,1")
      .split(",")
      .map((s) => Number(s.trim()))
      .filter((n) => n > 0)
      .sort((a, b) => a - b), // [1,3,7,14,30,60]
    alertExpired: get("alert_expired", "true") === "true",
    cronTime: get("cron_time", "0 8 * * *"),
    smtpHost: get("smtp_host"),
    notifyEmail: get("notify_email"),
  };
}

/** يحدد معلم التنبيه الحالي بناءً على الأيام المتبقية (مرة واحدة لكل معلم). */
function currentMilestone(daysRemaining: number, periods: number[], alertExpired: boolean): string | null {
  if (daysRemaining < 0) return alertExpired ? "expired" : null;
  const period = periods.find((p) => daysRemaining <= p);
  return period ? `period-${period}` : null;
}

async function collectSources(alertDays: number, periods: number[]): Promise<ExpirySource[]> {
  const now = new Date();
  const horizon = new Date(now.getTime() + Math.max(alertDays, periods[periods.length - 1]) * 86400000);
  const sources: ExpirySource[] = [];

  const [documents, employees, companies] = await Promise.all([
    prisma.document.findMany({
      where: { status: { not: "archived" }, expiryDate: { gte: new Date(now.getTime() - 86400000), lte: horizon } },
      include: { company: { select: { id: true, name: true, email: true } }, employee: { select: { id: true, fullName: true, email: true, companyId: true } }, documentType: true },
    }),
    prisma.employee.findMany({
      where: { OR: [{ iqamaExpiry: { not: null } }, { passportExpiry: { not: null } }, { insuranceExpiry: { not: null } }] },
      include: { company: { select: { id: true, name: true, email: true } } },
    }),
    prisma.company.findMany({ select: { id: true, name: true, email: true, crExpiry: true } }),
  ]);

  for (const doc of documents) {
    const email = doc.employee?.email || doc.company.email;
    sources.push({
      kind: "document",
      entityId: doc.id,
      documentId: doc.id,
      entityType: doc.employeeId ? "employee" : "company",
      entityName: doc.employee?.fullName ?? doc.company.name,
      companyId: doc.company.id,
      companyName: doc.company.name,
      title: `${doc.documentType.nameAr} — ${doc.title}`,
      expiryDate: doc.expiryDate,
      targetEmail: email || null,
    });
  }

  for (const emp of employees) {
    const email = emp.email || emp.company.email;
    if (emp.iqamaExpiry) sources.push({ kind: "employee_iqama", entityId: emp.id, entityType: "employee", entityName: emp.fullName, companyId: emp.companyId, companyName: emp.company.name, title: `إقامة — ${emp.fullName}`, expiryDate: emp.iqamaExpiry, targetEmail: email || null });
    if (emp.passportExpiry) sources.push({ kind: "employee_passport", entityId: emp.id, entityType: "employee", entityName: emp.fullName, companyId: emp.companyId, companyName: emp.company.name, title: `جواز سفر — ${emp.fullName}`, expiryDate: emp.passportExpiry, targetEmail: email || null });
    if (emp.insuranceExpiry) sources.push({ kind: "employee_insurance", entityId: emp.id, entityType: "employee", entityName: emp.fullName, companyId: emp.companyId, companyName: emp.company.name, title: `تأمين طبي — ${emp.fullName}`, expiryDate: emp.insuranceExpiry, targetEmail: email || null });
  }

  for (const c of companies) {
    if (c.crExpiry) sources.push({ kind: "company_cr", entityId: c.id, entityType: "company", entityName: c.name, companyId: c.id, companyName: c.name, title: `السجل التجاري — ${c.name}`, expiryDate: c.crExpiry, targetEmail: c.email });
  }

  // تصفية: فقط ما دخل في معلم تنبيه فعلي ضمن المدى وفي فترة مسموحة
  return sources.filter((s) => {
    const days = Math.floor((s.expiryDate.getTime() - now.getTime()) / 86400000);
    if (days < -1) return false; // منتهي منذ أكثر من يوم
    return currentMilestone(days, periods, true) !== null;
  });
}

async function runExpiryCheck() {
  const { alertPeriods, alertExpired, smtpHost, notifyEmail } = await getSettings();
  console.log(`[cron] Running expiry check (periods: ${alertPeriods.join(",")}, expired: ${alertExpired})...`);

  const sources = await collectSources(0, alertPeriods);
  const now = new Date();

  let sent = 0;
  let failed = 0;
  let deduped = 0;

  for (const src of sources) {
    const days = Math.floor((src.expiryDate.getTime() - now.getTime()) / 86400000);
    const milestone = currentMilestone(days, alertPeriods, alertExpired);
    if (!milestone) continue;
    const eventKey = `${src.kind}:${src.entityId}:${milestone}`;

    // منع التكرار: إن كان نفس الحدث (نفس البند + نفس المعلم) قد أُرسل مسبقاً
    const exists = await prisma.notification.findUnique({ where: { eventKey } });
    if (exists) {
      deduped++;
      continue;
    }

    const priority = await computePriorityForDate(src.expiryDate);
    const subject = days < 0
      ? `⚠️ منتهي: ${src.title}`
      : `⚠️ ${milestone === "period-1" ? "أيام أخيرة" : "تنبيه"}: ${src.title} ينتهي خلال ${days} يوم`;

    const to = src.targetEmail || notifyEmail || "";

    if (!to) {
      // لا يوجد بريد قابل للإرسال — تنبيه داخلي فقط مع تسجيل eventKey لتجنب التكرار
      await prisma.notification.create({
        data: {
          type: "in_app",
          documentId: src.documentId ?? null,
          eventKey,
          entityType: src.entityType,
          entityName: src.entityName,
          subject,
          message: `${src.title} • الباقي: ${days < 0 ? "منتهي" : `${days} يوم`}`,
          isSuccess: true,
        },
      });
      if (src.kind === "document" && src.documentId) {
        await prisma.document.update({ where: { id: src.documentId }, data: { isAlerted: true } });
      }
      sent++;
      continue;
    }

    const ok = await sendExpiryAlert({
      documentId: src.documentId ?? null,
      title: src.title,
      entityType: src.entityType,
      entityName: src.entityName,
      companyName: src.companyName,
      expiryDate: src.expiryDate,
      daysRemaining: days,
      priorityLabel: priority.label,
      priorityColor: priority.color,
      to,
      eventKey, // عند النجاح يُسجَّل السجل بمعرّف الحدث لمنع التكرار
    });

    if (ok) {
      sent++;
      if (src.kind === "document" && src.documentId) {
        await prisma.document.update({ where: { id: src.documentId }, data: { isAlerted: true } });
      }
    } else {
      failed++;
    }
  }

  // التقرير اليومي
  if (smtpHost && notifyEmail) {
    const expiredCount = await prisma.document.count({ where: { expiryDate: { lt: now } } });
    const expiringCount = await prisma.document.count({ where: { expiryDate: { gte: now, lte: new Date(now.getTime() + alertPeriods[alertPeriods.length - 1] * 86400000) } } });
    try {
      await sendEmail(
        notifyEmail,
        "📋 التقرير اليومي للمستندات",
        `<div dir="rtl" style="font-family:Tahoma,Arial;padding:20px;">
          <h2>التقرير اليومي للمستندات</h2>
          <p>عدد المستندات المنتهية: <b>${expiredCount}</b></p>
          <p>المستندات القريبة من الانتهاء: <b>${expiringCount}</b></p>
          <p>تم إرسال تنبيهات اليوم: <b>${sent}</b></p>
        </div>`
      );
    } catch (e) {
      console.error("summary email failed:", e);
    }
  }

  console.log(`[cron] Done. sent=${sent} failed=${failed} deduped=${deduped} total=${sources.length}`);
}

export function startCron() {
  const cronTime = process.env.CRON_TIME || "0 8 * * *";
  cron.schedule(cronTime, () => {
    runExpiryCheck().catch((e) => console.error("[cron] error:", e));
  });
  console.log(`[cron] Scheduled expiry check at: ${cronTime}`);
  // Run once on startup in development
  if (process.env.NODE_ENV !== "production") {
    setTimeout(() => runExpiryCheck().catch((e) => console.error("[cron] startup run error:", e)), 5000);
  }
}

export { runExpiryCheck };