/**
 * Notification preferences query helpers — notification-preferences (spec 97).
 *
 * Stores per-user notification delivery preferences in user_preferences.notification_channels
 * JSONB under a `prefs` key, preserving the existing email[]/telegram[] arrays used
 * by NotificationAdapter.canDeliver().
 *
 * Routes must NOT import schema tables directly — use these helpers.
 */
import { and, eq } from 'drizzle-orm'
import type { Db } from '../client'
import { userPreferences } from '../schema/user-preferences'
import { auditLog } from './_audit-forward'

// ── Types ─────────────────────────────────────────────────────────────────────

export interface NotificationEventPrefs {
  invoicePaid: boolean
  invoiceOverdue: boolean
  newLead: boolean
  projectMilestone: boolean
  ticketReply: boolean
}

export interface NotificationPreferences {
  email: NotificationEventPrefs
  inApp: NotificationEventPrefs
  digest: 'none' | 'daily' | 'weekly'
}

const DEFAULT_EVENT_PREFS: NotificationEventPrefs = {
  invoicePaid: true,
  invoiceOverdue: true,
  newLead: true,
  projectMilestone: true,
  ticketReply: true,
}

const DEFAULT_PREFS: NotificationPreferences = {
  email: { ...DEFAULT_EVENT_PREFS },
  inApp: { ...DEFAULT_EVENT_PREFS },
  digest: 'daily',
}

// ── Helpers ───────────────────────────────────────────────────────────────────

function isEventPrefs(v: unknown): v is NotificationEventPrefs {
  if (!v || typeof v !== 'object') return false
  const o = v as Record<string, unknown>
  return (
    typeof o['invoicePaid'] === 'boolean' &&
    typeof o['invoiceOverdue'] === 'boolean' &&
    typeof o['newLead'] === 'boolean' &&
    typeof o['projectMilestone'] === 'boolean' &&
    typeof o['ticketReply'] === 'boolean'
  )
}

function parseStoredPrefs(raw: unknown): NotificationPreferences {
  if (!raw || typeof raw !== 'object') return DEFAULT_PREFS
  const o = raw as Record<string, unknown>
  const stored = o['prefs']
  if (!stored || typeof stored !== 'object') return DEFAULT_PREFS
  const p = stored as Record<string, unknown>

  const email = isEventPrefs(p['email']) ? p['email'] : DEFAULT_EVENT_PREFS
  const inApp = isEventPrefs(p['inApp']) ? p['inApp'] : DEFAULT_EVENT_PREFS
  const digest =
    p['digest'] === 'none' || p['digest'] === 'daily' || p['digest'] === 'weekly'
      ? (p['digest'] as 'none' | 'daily' | 'weekly')
      : 'daily'

  return { email, inApp, digest }
}

// ── Query functions ───────────────────────────────────────────────────────────

/**
 * Returns the notification preferences for a (user, tenant) pair.
 * Returns defaults when no row exists or prefs are unset.
 */
export async function getNotificationPreferences(
  db: Db,
  tenantId: string,
  userId: string,
): Promise<NotificationPreferences> {
  const [row] = await db
    .select({ notificationChannels: userPreferences.notificationChannels })
    .from(userPreferences)
    .where(and(eq(userPreferences.userId, userId), eq(userPreferences.tenantId, tenantId)))
    .limit(1)

  return parseStoredPrefs(row?.notificationChannels)
}

/**
 * Upserts notification preferences for (user, tenant) and writes an audit log
 * row in the same transaction.
 */
export async function updateNotificationPreferences(
  db: Db,
  tenantId: string,
  userId: string,
  prefs: NotificationPreferences,
  opts?: { actorIp?: string | null; requestId?: string | null },
): Promise<void> {
  await db.transaction(async (tx) => {
    // Read existing notificationChannels to preserve email[]/telegram[] arrays
    const [existing] = await tx
      .select({ notificationChannels: userPreferences.notificationChannels })
      .from(userPreferences)
      .where(and(eq(userPreferences.userId, userId), eq(userPreferences.tenantId, tenantId)))
      .limit(1)

    const currentChannels =
      existing?.notificationChannels &&
      typeof existing.notificationChannels === 'object' &&
      !Array.isArray(existing.notificationChannels)
        ? (existing.notificationChannels as Record<string, unknown>)
        : { email: [], telegram: [] }

    const merged = { ...currentChannels, prefs }

    await tx
      .insert(userPreferences)
      .values({
        userId,
        tenantId,
        notificationChannels: merged,
      })
      .onConflictDoUpdate({
        target: [userPreferences.userId, userPreferences.tenantId],
        set: {
          notificationChannels: merged,
          updatedAt: new Date(),
        },
      })

    await tx.insert(auditLog).values({
      tenantId,
      actorId: userId,
      actorType: 'user',
      entityType: 'user_preferences',
      entityId: userId,
      action: 'user_preferences.notification_preferences.updated',
      changes: { notification_preferences: [null, prefs] },
      ip: opts?.actorIp ?? null,
      requestId: opts?.requestId ?? null,
    })
  })
}
