import { eq, and, desc, sql } from 'drizzle-orm';
import { db } from '../db/index';
import { notifications } from '../db/schema';
import { eventBus } from '../events/bus';

// ═══════════════════════════════════════════════════════════
// Notification Service — Multi-channel delivery
// ═══════════════════════════════════════════════════════════

// ─── Channel Delivery Adapters ──────────────────────────

type ChannelAdapter = (notification: {
  userId: string;
  title: string;
  body: string | null;
  data: Record<string, unknown>;
  actionUrl: string | null;
}) => Promise<boolean>;

const channelAdapters: Record<string, ChannelAdapter> = {
  dashboard: async (_notif) => {
    // Dashboard notifications are stored in DB — always "delivered"
    return true;
  },

  discord: async (notif) => {
    // TODO: integrate with Discord bot/webhook
    console.log(`[Notification:Discord] ${notif.title}`);
    return true;
  },

  email: async (notif) => {
    // TODO: integrate with email provider (SendGrid, SES, etc.)
    console.log(`[Notification:Email] ${notif.title} → ${notif.userId}`);
    return true;
  },

  sms: async (notif) => {
    // TODO: integrate with Twilio/etc.
    console.log(`[Notification:SMS] ${notif.title} → ${notif.userId}`);
    return true;
  },

  push: async (notif) => {
    // TODO: integrate with push provider (FCM, APNs)
    console.log(`[Notification:Push] ${notif.title} → ${notif.userId}`);
    return true;
  },
};

// ─── Core CRUD ──────────────────────────────────────────

export async function createNotification(data: {
  userId: string;
  type: string;
  title: string;
  body?: string;
  data?: Record<string, unknown>;
  channels?: string[];
  entityType?: string;
  entityId?: string;
  actionUrl?: string;
}) {
  const channels = data.channels || ['dashboard'];

  const [notif] = await db
    .insert(notifications)
    .values({
      userId: data.userId,
      type: data.type,
      title: data.title,
      body: data.body,
      data: data.data || {},
      channels,
      entityType: data.entityType,
      entityId: data.entityId,
      actionUrl: data.actionUrl,
    })
    .returning();

  // Deliver to all requested channels
  const deliveredVia: string[] = [];
  for (const channel of channels) {
    const adapter = channelAdapters[channel];
    if (adapter) {
      try {
        const success = await adapter({
          userId: data.userId,
          title: data.title,
          body: data.body || null,
          data: data.data || {},
          actionUrl: data.actionUrl || null,
        });
        if (success) deliveredVia.push(channel);
      } catch (err) {
        console.error(`[Notification] Channel ${channel} delivery failed:`, err);
      }
    }
  }

  // Update delivered_via
  if (deliveredVia.length > 0) {
    await db
      .update(notifications)
      .set({ deliveredVia })
      .where(eq(notifications.id, notif.id));
  }

  return { ...notif, deliveredVia };
}

export async function listNotifications(
  userId: string,
  opts: { unread?: boolean; type?: string; page?: number; limit?: number }
) {
  const { page = 1, limit = 20 } = opts;
  const offset = (page - 1) * limit;

  const conditions = [
    eq(notifications.userId, userId),
    eq(notifications.dismissed, false),
  ];
  if (opts.unread !== undefined) conditions.push(eq(notifications.read, !opts.unread));
  if (opts.type) conditions.push(eq(notifications.type, opts.type));

  const where = and(...conditions);

  const [items, countResult] = await Promise.all([
    db
      .select()
      .from(notifications)
      .where(where)
      .orderBy(desc(notifications.createdAt))
      .limit(limit)
      .offset(offset),
    db
      .select({ count: sql<number>`count(*)` })
      .from(notifications)
      .where(where),
  ]);

  const total = Number(countResult[0]?.count || 0);

  return {
    items,
    meta: { page, limit, total, totalPages: Math.ceil(total / limit) },
  };
}

export async function markRead(notificationId: string, userId: string) {
  const [updated] = await db
    .update(notifications)
    .set({ read: true, readAt: new Date() })
    .where(
      and(eq(notifications.id, notificationId), eq(notifications.userId, userId))
    )
    .returning();

  return updated || null;
}

export async function markAllRead(userId: string) {
  const result = await db
    .update(notifications)
    .set({ read: true, readAt: new Date() })
    .where(
      and(
        eq(notifications.userId, userId),
        eq(notifications.read, false)
      )
    )
    .returning();

  return { updated: result.length };
}

export async function dismiss(notificationId: string, userId: string) {
  const [updated] = await db
    .update(notifications)
    .set({ dismissed: true })
    .where(
      and(eq(notifications.id, notificationId), eq(notifications.userId, userId))
    )
    .returning();

  return updated || null;
}

export async function deleteNotification(notificationId: string, userId: string) {
  // Dismiss is our "soft delete"
  return dismiss(notificationId, userId);
}
