import { and, count, desc, eq, ilike, not, or } from 'drizzle-orm';
import { notifications } from '@/db/schema';
import { db } from '@/lib/db';
import type { NotificationFilters } from '../api/types';
import { resolveNotificationType } from './notification-service';

type NotificationRow = typeof notifications.$inferSelect;

function buildNotificationHref(row: NotificationRow) {
  switch (row.referenceType) {
    case 'training_record':
      return row.referenceId ? `/dashboard/training-records/${row.referenceId}` : '/dashboard/training-records';
    case 'import_job':
      return '/dashboard/import-employees';
    case 'master_review':
      return '/dashboard/master-review';
    case 'announcement':
      return row.referenceId
        ? `/dashboard/announcements/${row.referenceId}`
        : '/dashboard/announcements';
    default:
      return null;
  }
}

function serializeNotification(row: NotificationRow) {
  return {
    id: row.id,
    organization_id: row.organizationId,
    user_id: row.userId,
    title: row.title,
    message: row.message,
    type: resolveNotificationType(row),
    reference_type: row.referenceType,
    reference_id: row.referenceId,
    is_read: row.isRead,
    read_at: row.readAt ? row.readAt.toISOString() : null,
    created_at: row.createdAt.toISOString(),
    href: buildNotificationHref(row)
  };
}

function toTypeClause(filterType?: NotificationFilters['type']) {
  if (!filterType) {
    return null;
  }

  switch (filterType) {
    case 'TRAINING_APPROVED':
      return and(
        eq(notifications.referenceType, 'training_record'),
        eq(notifications.type, 'approved')
      );
    case 'TRAINING_REJECTED':
      return and(
        eq(notifications.referenceType, 'training_record'),
        eq(notifications.type, 'rejected')
      );
    case 'ANNOUNCEMENT_PUBLISHED':
      return or(
        eq(notifications.referenceType, 'announcement'),
        eq(notifications.type, 'announcement')
      );
    case 'IMPORT_COMPLETED':
      return eq(notifications.referenceType, 'import_job');
    case 'MASTER_REVIEW_APPROVED':
      return and(
        eq(notifications.referenceType, 'master_review'),
        eq(notifications.type, 'approved')
      );
    case 'MASTER_REVIEW_REJECTED':
      return and(
        eq(notifications.referenceType, 'master_review'),
        eq(notifications.type, 'rejected')
      );
    case 'SYSTEM':
      return not(
        or(
          and(eq(notifications.referenceType, 'training_record'), eq(notifications.type, 'approved')),
          and(eq(notifications.referenceType, 'training_record'), eq(notifications.type, 'rejected')),
          eq(notifications.referenceType, 'announcement'),
          eq(notifications.type, 'announcement'),
          eq(notifications.referenceType, 'import_job'),
          and(eq(notifications.referenceType, 'master_review'), eq(notifications.type, 'approved')),
          and(eq(notifications.referenceType, 'master_review'), eq(notifications.type, 'rejected'))
        )!
      );
    default:
      return null;
  }
}

export async function listNotificationsForUser({
  organizationId,
  userId,
  filters
}: {
  organizationId: string;
  userId: string;
  filters: NotificationFilters;
}) {
  const page = Number(filters.page ?? 1);
  const limit = Number(filters.limit ?? 10);
  const search = filters.search?.trim();
  const offset = (page - 1) * limit;
  const typeClause = toTypeClause(filters.type);

  const clauses = [
    eq(notifications.organizationId, organizationId),
    eq(notifications.userId, userId),
    ...(filters.status === 'unread' ? [eq(notifications.isRead, false)] : []),
    ...(filters.status === 'read' ? [eq(notifications.isRead, true)] : []),
    ...(typeClause ? [typeClause] : []),
    ...(search
      ? [
          or(
            ilike(notifications.title, `%${search}%`),
            ilike(notifications.message, `%${search}%`)
          )!
        ]
      : [])
  ];

  const where = and(...clauses);
  const [totalRows, pageRows, unreadRows] = await Promise.all([
    db
      .select({ value: count() })
      .from(notifications)
      .where(where),
    db
      .select()
      .from(notifications)
      .where(where)
      .orderBy(desc(notifications.createdAt))
      .limit(limit)
      .offset(offset),
    db
      .select({ value: count() })
      .from(notifications)
      .where(
        and(
          eq(notifications.organizationId, organizationId),
          eq(notifications.userId, userId),
          eq(notifications.isRead, false)
        )
      )
  ]);

  return {
    offset,
    limit,
    total: totalRows[0]?.value ?? 0,
    unreadCount: unreadRows[0]?.value ?? 0,
    notifications: pageRows.map((row) => serializeNotification(row))
  };
}
