import { Expo } from "expo-server-sdk";

const expo = new Expo();

async function ensurePushTokenTable(db) {
  await db.query(
    `
      CREATE TABLE IF NOT EXISTS user_push_tokens (
        id BIGSERIAL PRIMARY KEY,
        user_id BIGINT NOT NULL REFERENCES users (id) ON DELETE CASCADE,
        push_token TEXT NOT NULL,
        platform VARCHAR(30),
        is_active BOOLEAN NOT NULL DEFAULT TRUE,
        last_seen_at TIMESTAMPTZ NOT NULL DEFAULT CURRENT_TIMESTAMP,
        created_at TIMESTAMPTZ NOT NULL DEFAULT CURRENT_TIMESTAMP,
        CONSTRAINT uq_user_push_tokens_token UNIQUE (push_token)
      )
    `,
  );

  await db.query(
    `
      CREATE INDEX IF NOT EXISTS idx_user_push_tokens_user_id
      ON user_push_tokens (user_id, is_active)
    `,
  );
}

export async function sendPush(pushToken, title, body, data = {}) {
  if (!Expo.isExpoPushToken(pushToken)) {
    return null;
  }

  const chunks = expo.chunkPushNotifications([
    {
      to: pushToken,
      title,
      body,
      data,
      sound: "default",
      priority: data.urgent ? "high" : "normal",
    },
  ]);

  const tickets = [];

  for (const chunk of chunks) {
    const response = await expo.sendPushNotificationsAsync(chunk);
    tickets.push(...response);
  }

  return tickets;
}

export async function sendPushToUsers(db, { userIds, title, body, data = {} }) {
  if (!Array.isArray(userIds) || !userIds.length) {
    return [];
  }

  await ensurePushTokenTable(db);

  const { rows } = await db.query(
    `
      SELECT DISTINCT push_token
      FROM user_push_tokens
      WHERE user_id = ANY($1::bigint[])
        AND is_active = TRUE
    `,
    [userIds],
  );

  const tickets = [];

  for (const row of rows) {
    try {
      const response = await sendPush(row.push_token, title, body, data);
      if (response) {
        tickets.push(...response);
      }
    } catch (error) {
      console.error("Push notification failed", {
        message: error?.message || "unknown",
      });
    }
  }

  return tickets;
}
