import { j as json, b as private_env } from './shared-server-sSGG17Df.js';
import { L as LibraryAPNsService } from './library-apns-BqJbvSKh.js';
import { c as createPendingRequest } from './pending-requests-D8UiTw7L.js';
import { v as validateAuth } from './auth-DeCdZ83n.js';
import admin from 'firebase-admin';
import { t as toErrorMessage } from './error-DDXB3duW.js';
import '@parse/node-apn';
import 'crypto';

const MAX_HISTORY = 100;
const history = [];
function addNotification(record) {
  history.unshift(record);
  if (history.length > MAX_HISTORY) {
    history.length = MAX_HISTORY;
  }
}
function getNotifications(limit = 50) {
  return history.slice(0, limit);
}
let app = null;
function isFCMConfigured() {
  return !!(process.env.FCM_PROJECT_ID && process.env.FCM_CLIENT_EMAIL && process.env.FCM_PRIVATE_KEY);
}
async function sendFCMNotification(deviceToken, payload) {
  try {
    const fcmApp = getApp();
    const message = {
      android: {
        priority: "high",
        // Ensures delivery even in Doze mode
        ttl: 3e5
        // 5 minutes TTL (matches pending request expiry)
      },
      data: {
        body: payload.body || payload.message || "",
        category: payload.category ?? (typeof payload.data?.category === "string" ? payload.data.category : ""),
        project: typeof payload.data?.project === "string" ? payload.data.project : "",
        requestId: typeof payload.data?.requestId === "string" ? payload.data.requestId : "",
        source: typeof payload.data?.source === "string" ? payload.data.source : "",
        timestamp: (/* @__PURE__ */ new Date()).toISOString(),
        title: payload.title,
        toolInput: payload.data?.toolInput ? JSON.stringify(payload.data.toolInput) : "",
        toolName: typeof payload.data?.toolName === "string" ? payload.data.toolName : "",
        type: typeof payload.data?.type === "string" ? payload.data.type : ""
      },
      token: deviceToken
    };
    const messageId = await admin.messaging(fcmApp).send(message);
    return { messageId, success: true };
  } catch (error) {
    const errorMessage = error instanceof Error ? error.message : String(error);
    console.error("[FCM] Send failed:", errorMessage);
    return { error: errorMessage, success: false };
  }
}
function getApp() {
  if (!app) {
    const projectId = process.env.FCM_PROJECT_ID;
    const clientEmail = process.env.FCM_CLIENT_EMAIL;
    const privateKey = process.env.FCM_PRIVATE_KEY?.replace(/\\n/g, "\n");
    if (!projectId || !clientEmail || !privateKey) {
      throw new Error(
        "FCM not configured: missing FCM_PROJECT_ID, FCM_CLIENT_EMAIL, or FCM_PRIVATE_KEY"
      );
    }
    app = admin.initializeApp({
      credential: admin.credential.cert({ clientEmail, privateKey, projectId })
    });
  }
  return app;
}
let apnsSingleton = null;
function getAPNsClient() {
  if (!apnsSingleton) {
    apnsSingleton = new LibraryAPNsService();
  }
  return apnsSingleton;
}
const notificationCache = /* @__PURE__ */ new Map();
const DEDUP_WINDOW = 1e4;
function intelligentNotificationFilter(title, message, data) {
  const source = data?.source || "unknown";
  if (isDuplicateNotification(title, message, data)) {
    return {
      reason: "Duplicate notification within 10-second window",
      send: false
    };
  }
  if (source === "smart-completion-detector" || source === "shooter-completion-detector") {
    return {
      reason: "Smart completion detector - completion or intervention needed",
      send: true
    };
  }
  if (source === "stop-hook") {
    return {
      reason: "Stop hook completion notification - session finished",
      send: true
    };
  }
  const spamPatterns = [
    /PreToolUse/i,
    // Any PreToolUse notifications
    /PostToolUse/i,
    // Any PostToolUse notifications
    /^(Read|Write|Edit|Bash)\s+Starting\s*\|\s*\w+$/i,
    // Only basic tool starting patterns
    /^unknown\s*\|\s*\w+$/i
    // Only "unknown | projectname" exactly
  ];
  const isSpam = spamPatterns.some((pattern) => pattern.test(title) || pattern.test(message));
  if (isSpam) {
    return {
      reason: "Filtered spam notification from old hook system",
      send: false
    };
  }
  const importantPatterns = [
    /session complete/i,
    /intervention needed/i,
    /error/i,
    /failed/i,
    /blocked/i,
    /attention/i
  ];
  const isImportant = importantPatterns.some(
    (pattern) => pattern.test(title) || pattern.test(message)
  );
  if (isImportant) {
    return {
      reason: "Important notification - completion or intervention",
      send: true
    };
  }
  return {
    reason: "General notification - allowing by default",
    send: true
  };
}
function isDuplicateNotification(title, message, data) {
  const key = `${title}|${message}|${data?.category || "unknown"}`;
  const now = Date.now();
  if (notificationCache.has(key)) {
    const lastSent = notificationCache.get(key);
    if (now - lastSent < DEDUP_WINDOW) {
      return true;
    }
  }
  for (const [k, v] of notificationCache.entries()) {
    if (now - v > DEDUP_WINDOW) {
      notificationCache.delete(k);
    }
  }
  return false;
}
function recordNotification(title, message, data) {
  const key = `${title}|${message}|${data?.category || "unknown"}`;
  notificationCache.set(key, Date.now());
}
const POST = async ({ request }) => {
  try {
    const apnsClient = getAPNsClient();
    const authError = validateAuth(request);
    if (authError) {
      return authError;
    }
    const rawBody = await request.json();
    if (!rawBody || typeof rawBody !== "object" || Array.isArray(rawBody)) {
      return json({ error: "Request body must be a JSON object" }, { status: 400 });
    }
    const body = rawBody;
    const data = body.data;
    const requestDeviceToken = body.deviceToken;
    const message = body.message;
    const title = body.title;
    const skipPush = typeof body.skipPush === "boolean" ? body.skipPush : false;
    const waitForResponse = typeof body.waitForResponse === "boolean" ? body.waitForResponse : false;
    if (!title || typeof title !== "string" || !message || typeof message !== "string") {
      return json({ error: "Title and message are required and must be strings" }, { status: 400 });
    }
    if (requestDeviceToken !== void 0 && typeof requestDeviceToken !== "string") {
      return json({ error: "deviceToken must be a string" }, { status: 400 });
    }
    const requestId = Math.random().toString(36).substring(2, 15);
    const dataRequestId = typeof data?.requestId === "string" ? data.requestId : void 0;
    const canonicalRequestId = dataRequestId || requestId;
    const shouldSendNotification = intelligentNotificationFilter(title, message, data);
    if (!shouldSendNotification.send) {
      addNotification({
        category: data?.category ?? null,
        data: data ?? null,
        error: shouldSendNotification.reason,
        id: canonicalRequestId,
        message,
        project: data?.project ?? null,
        source: data?.source ?? null,
        status: "filtered",
        timestamp: (/* @__PURE__ */ new Date()).toISOString(),
        title,
        tool: data?.tool ?? null
      });
      return json({
        message: "Notification filtered (not sent)",
        reason: shouldSendNotification.reason,
        success: true,
        timestamp: (/* @__PURE__ */ new Date()).toISOString()
      });
    }
    if (skipPush) {
      if (waitForResponse) {
        createPendingRequest(canonicalRequestId, {
          sessionId: data?.sessionId || "",
          toolInput: data?.toolInput || {},
          toolName: data?.toolName || ""
        });
      }
      addNotification({
        category: data?.category ?? null,
        data: data ?? null,
        error: null,
        id: canonicalRequestId,
        message,
        project: data?.project ?? null,
        source: data?.source ?? null,
        status: "skipped",
        timestamp: (/* @__PURE__ */ new Date()).toISOString(),
        title,
        tool: data?.tool ?? null
      });
      return json({
        message: "Push skipped (WebSocket clients connected)",
        requestId: canonicalRequestId,
        success: true,
        timestamp: (/* @__PURE__ */ new Date()).toISOString()
      });
    }
    const payload = {
      badge: 1,
      body: message,
      category: waitForResponse ? "CLAUDE_PERMISSION" : void 0,
      data: {
        ...data,
        requestId: canonicalRequestId,
        source: "modern-apns-api",
        timestamp: (/* @__PURE__ */ new Date()).toISOString(),
        waitForResponse: waitForResponse || false
      },
      message: null,
      sound: "default",
      title
    };
    const platform = private_env.DEVICE_PLATFORM || "ios";
    if (platform === "android") {
      if (!isFCMConfigured()) {
        return json(
          {
            details: "Missing FCM_PROJECT_ID, FCM_CLIENT_EMAIL, or FCM_PRIVATE_KEY environment variables",
            error: "FCM not configured"
          },
          { status: 500 }
        );
      }
      const androidToken = requestDeviceToken?.trim() || (private_env.ANDROID_DEVICE_TOKEN || private_env.DEVICE_TOKEN)?.trim();
      if (!androidToken) {
        return json(
          {
            details: "ANDROID_DEVICE_TOKEN or DEVICE_TOKEN environment variable is missing and no deviceToken in request body",
            error: "No device token configured"
          },
          { status: 500 }
        );
      }
      const fcmResult = await sendFCMNotification(androidToken, payload);
      if (fcmResult.success) {
        recordNotification(title, message, data);
        if (waitForResponse) {
          createPendingRequest(canonicalRequestId, {
            sessionId: data?.sessionId || "",
            toolInput: data?.toolInput || {},
            toolName: data?.toolName || ""
          });
        }
        addNotification({
          category: data?.category ?? null,
          data: data ?? null,
          error: null,
          id: canonicalRequestId,
          message,
          project: data?.project ?? null,
          source: data?.source ?? null,
          status: "sent",
          timestamp: (/* @__PURE__ */ new Date()).toISOString(),
          title,
          tool: data?.tool ?? null
        });
        return json({
          message: "Notification sent successfully",
          requestId: canonicalRequestId,
          result: { messageId: fcmResult.messageId },
          success: true,
          timestamp: (/* @__PURE__ */ new Date()).toISOString()
        });
      } else {
        console.error(`[notify] FCM delivery failed: ${fcmResult.error}`);
        addNotification({
          category: data?.category ?? null,
          data: data ?? null,
          error: fcmResult.error ?? null,
          id: canonicalRequestId,
          message,
          project: data?.project ?? null,
          source: data?.source ?? null,
          status: "failed",
          timestamp: (/* @__PURE__ */ new Date()).toISOString(),
          title,
          tool: data?.tool ?? null
        });
        return json(
          {
            details: fcmResult.error,
            error: "Failed to send notification",
            requestId: canonicalRequestId,
            timestamp: (/* @__PURE__ */ new Date()).toISOString()
          },
          { status: 500 }
        );
      }
    } else {
      if (!apnsClient.isConfigured()) {
        return json(
          {
            details: "Missing APNS_KEY, APNS_KEY_ID, or APNS_TEAM_ID environment variables",
            error: "APNs client not configured"
          },
          { status: 500 }
        );
      }
      const deviceToken = requestDeviceToken?.trim() || private_env.DEVICE_TOKEN?.trim();
      if (!deviceToken) {
        return json(
          {
            details: "DEVICE_TOKEN environment variable is missing and no deviceToken in request body",
            error: "No device token configured"
          },
          { status: 500 }
        );
      }
      try {
        const result = await apnsClient.sendNotification(deviceToken, payload);
        recordNotification(title, message, data);
        if (waitForResponse) {
          createPendingRequest(canonicalRequestId, {
            sessionId: data?.sessionId || "",
            toolInput: data?.toolInput || {},
            toolName: data?.toolName || ""
          });
        }
        addNotification({
          category: data?.category ?? null,
          data: data ?? null,
          error: null,
          id: canonicalRequestId,
          message,
          project: data?.project ?? null,
          source: data?.source ?? null,
          status: "sent",
          timestamp: (/* @__PURE__ */ new Date()).toISOString(),
          title,
          tool: data?.tool ?? null
        });
        return json({
          message: "Notification sent successfully",
          requestId: canonicalRequestId,
          result,
          success: true,
          timestamp: (/* @__PURE__ */ new Date()).toISOString()
        });
      } catch (notificationError) {
        const notifErrMsg = toErrorMessage(notificationError);
        console.error(`[notify] APNs delivery failed: ${notifErrMsg}`);
        addNotification({
          category: data?.category ?? null,
          data: data ?? null,
          error: notifErrMsg,
          id: canonicalRequestId,
          message,
          project: data?.project ?? null,
          source: data?.source ?? null,
          status: "failed",
          timestamp: (/* @__PURE__ */ new Date()).toISOString(),
          title,
          tool: data?.tool ?? null
        });
        return json(
          {
            details: notifErrMsg,
            error: "Failed to send notification",
            requestId: canonicalRequestId,
            timestamp: (/* @__PURE__ */ new Date()).toISOString()
          },
          { status: 500 }
        );
      }
    }
  } catch (error) {
    console.error("Notification error:", error);
    return json(
      {
        details: toErrorMessage(error),
        error: "Failed to send notification",
        timestamp: (/* @__PURE__ */ new Date()).toISOString()
      },
      { status: 500 }
    );
  }
};
const GET = ({ request, url }) => {
  const authError = validateAuth(request);
  if (authError) {
    return authError;
  }
  const limit = parseInt(url.searchParams.get("limit") || "50");
  const notifications = getNotifications(limit);
  return json({
    count: notifications.length,
    notifications,
    timestamp: (/* @__PURE__ */ new Date()).toISOString()
  });
};

export { GET, POST };
//# sourceMappingURL=_server.ts-Ch-6iOHp.js.map
