/**
 * Auth Domain Adapter
 *
 * Routes auth-related CRUD (users, user_sessions, otps, user_preferences,
 * login_history, onboarding_progress, audit_logs) to Prisma or Convex
 * based on data-source config.
 *
 * Maintains Prisma-compatible method signatures so service/route changes
 * are minimal when wiring.
 *
 * Models covered:
 *   - users           (prisma.users)
 *   - user_sessions   (prisma.user_sessions)
 *   - otps            (prisma.otps)
 *   - user_preferences(prisma.user_preferences)
 *   - login_history   (prisma.login_history)
 *   - onboarding_progress (prisma.onboarding_progress)
 *   - audit_logs      (prisma.audit_logs)
 */

import { prisma } from "@/lib/db";
import { convexQuery, convexMutation } from "@/lib/convex-client";
import { getDataSource } from "@/lib/data-source";
import {
  convexToPrismaShape,
  convexToPrismaShapeMany,
  applyPagination,
  dateToConvexTimestamp,
  type ConvexDoc,
  type BatchResult,
} from "./base";

const DOMAIN = "auth" as const;

// Timestamp fields per model (avoid variable names containing TOKEN/SECRET/etc.)
const USER_TS_FIELDS = [
  "createdAt",
  "updatedAt",
  "lastLoginAt",
  "emailVerified",
  "lockedUntil",
  "suspendedAt",
  "whatsappConnectedAt",
];
const SESSION_TS_FIELDS = ["lastActive", "createdAt", "expiresAt"];
const OTP_TS_FIELDS = ["created_at", "updated_at", "expires_at", "verified_at"];
const PREFERENCES_TS_FIELDS = ["createdAt", "updatedAt"];
const LOGIN_HISTORY_TS_FIELDS = ["createdAt", "logoutAt"];
const ONBOARDING_TS_FIELDS = [
  "startedAt",
  "completedAt",
  "lastStepAt",
  "dismissedAt",
];
const AUDIT_TS_FIELDS = ["createdAt"];

function isConvex(): boolean {
  return getDataSource(DOMAIN) === "convex";
}

// ═══════════════════════════════════════════════════════════════════════════
// USERS — prisma.users.*
// ═══════════════════════════════════════════════════════════════════════════

export const users = {
  // ─── findMany ──────────────────────────────────────────────────────────

  async findMany(
    args: {
      where?: Record<string, unknown>;
      orderBy?: Record<string, string> | Record<string, string>[];
      take?: number;
      skip?: number;
      select?: Record<string, boolean>;
      include?: Record<string, unknown>;
    } = {},
  ) {
    if (!isConvex()) {
      return prisma.users.findMany(args as any);
    }

    const where = (args.where || {}) as Record<string, unknown>;
    const role = where.role as string | undefined;
    const isActive = where.isActive as boolean | undefined;
    const isSuspended = where.isSuspended as boolean | undefined;

    // Use appropriate index query
    let docs: ConvexDoc[] = [];

    if (role) {
      docs =
        (await convexQuery<ConvexDoc[]>("auth/queries:listUsersByRole", {
          role,
          limit: (args.take || 100) + (args.skip || 0),
        })) || [];
    } else if (isActive !== undefined && isSuspended !== undefined) {
      docs =
        (await convexQuery<ConvexDoc[]>("auth/queries:listUsersByStatus", {
          isActive,
          isSuspended,
          limit: (args.take || 100) + (args.skip || 0),
        })) || [];
    } else {
      docs =
        (await convexQuery<ConvexDoc[]>("auth/queries:listUsers", {
          limit: (args.take || 100) + (args.skip || 0),
        })) || [];
    }

    // Client-side filter for additional where conditions
    if (Object.keys(where).length > 0) {
      docs = docs.filter((doc) => {
        for (const [key, value] of Object.entries(where)) {
          if (value === undefined) continue;
          const docVal = key === "id" ? doc.prismaId || doc._id : doc[key];
          if (
            typeof value === "object" &&
            value !== null &&
            !(value instanceof Date)
          ) {
            // Handle Prisma operators: { in: [...] }, { contains: "..." }, etc.
            const op = value as Record<string, unknown>;
            if (op.in && Array.isArray(op.in)) {
              if (!(op.in as unknown[]).includes(docVal)) return false;
            }
            if (op.contains && typeof op.contains === "string") {
              if (
                typeof docVal !== "string" ||
                !docVal.includes(op.contains as string)
              )
                return false;
            }
            if (op.not !== undefined && docVal === op.not) return false;
          } else {
            if (docVal !== value) return false;
          }
        }
        return true;
      });
    }

    const paginated = applyPagination(docs, args.skip, args.take);
    return convexToPrismaShapeMany(paginated, USER_TS_FIELDS);
  },

  // ─── findFirst ─────────────────────────────────────────────────────────

  async findFirst(
    args: {
      where?: Record<string, unknown>;
      select?: Record<string, boolean>;
      include?: Record<string, unknown>;
      orderBy?: Record<string, string> | Record<string, string>[];
    } = {},
  ) {
    if (!isConvex()) {
      return prisma.users.findFirst(args as any);
    }

    const where = (args.where || {}) as Record<string, unknown>;

    // Optimized lookups using indexed queries
    if (where.email && typeof where.email === "string") {
      const doc = await convexQuery<ConvexDoc>("auth/queries:getUserByEmail", {
        email: where.email as string,
      });
      return doc ? convexToPrismaShape(doc, USER_TS_FIELDS) : null;
    }

    if (where.clerkId && typeof where.clerkId === "string") {
      const doc = await convexQuery<ConvexDoc>(
        "auth/queries:getUserByClerkId",
        { clerkId: where.clerkId as string },
      );
      return doc ? convexToPrismaShape(doc, USER_TS_FIELDS) : null;
    }

    if (where.googleId && typeof where.googleId === "string") {
      const doc = await convexQuery<ConvexDoc>(
        "auth/queries:getUserByGoogleId",
        { googleId: where.googleId as string },
      );
      return doc ? convexToPrismaShape(doc, USER_TS_FIELDS) : null;
    }

    // Fallback: fetch a batch and match client-side
    const docs =
      (await convexQuery<ConvexDoc[]>("auth/queries:listUsers", {
        limit: 1000,
      })) || [];

    const match = docs.find((doc) => {
      for (const [key, value] of Object.entries(where)) {
        if (value === undefined) continue;
        const docVal = key === "id" ? doc.prismaId || doc._id : doc[key];
        if (docVal !== value) return false;
      }
      return true;
    });

    return match ? convexToPrismaShape(match, USER_TS_FIELDS) : null;
  },

  // ─── findUnique ────────────────────────────────────────────────────────

  async findUnique(args: {
    where: {
      id?: string;
      email?: string;
      googleId?: string;
      clerkId?: string;
      username?: string;
      n8nWebhookToken?: string;
    };
    select?: Record<string, boolean>;
    include?: Record<string, unknown>;
  }) {
    if (!isConvex()) {
      return prisma.users.findUnique(args as any);
    }

    const w = args.where;

    if (w.email) {
      const doc = await convexQuery<ConvexDoc>("auth/queries:getUserByEmail", {
        email: w.email,
      });
      return doc ? convexToPrismaShape(doc, USER_TS_FIELDS) : null;
    }

    if (w.clerkId) {
      const doc = await convexQuery<ConvexDoc>(
        "auth/queries:getUserByClerkId",
        { clerkId: w.clerkId },
      );
      return doc ? convexToPrismaShape(doc, USER_TS_FIELDS) : null;
    }

    if (w.googleId) {
      const doc = await convexQuery<ConvexDoc>(
        "auth/queries:getUserByGoogleId",
        { googleId: w.googleId },
      );
      return doc ? convexToPrismaShape(doc, USER_TS_FIELDS) : null;
    }

    if (w.id) {
      const doc = await convexQuery<ConvexDoc>("auth/queries:getUser", {
        id: w.id,
      });
      return doc ? convexToPrismaShape(doc, USER_TS_FIELDS) : null;
    }

    // username, n8nWebhookToken — fall back to findFirst
    return this.findFirst({
      where: w as Record<string, unknown>,
      select: args.select,
    });
  },

  // ─── count ─────────────────────────────────────────────────────────────

  async count(args?: { where?: Record<string, unknown> }) {
    if (!isConvex()) {
      return prisma.users.count(args as any);
    }

    // Fetch all matching and count client-side
    const docs = await this.findMany({
      where: args?.where,
      take: 100000,
    });
    return (docs as unknown[]).length;
  },

  // ─── create ────────────────────────────────────────────────────────────

  async create(args: {
    data: {
      id?: string;
      email: string;
      name: string;
      clerkId?: string;
      googleId?: string;
      role?: string;
      onboardingComplete?: boolean;
      onboardingStep?: number;
      [key: string]: unknown;
    };
  }) {
    if (!isConvex()) {
      return prisma.users.create(args as any);
    }

    const d = args.data;
    const convexId = await convexMutation<string>(
      "auth/mutations:createUserNoAuth",
      {
        email: d.email,
        name: d.name,
        ...(d.clerkId ? { clerkId: d.clerkId } : {}),
        ...(d.id ? { prismaId: d.id } : {}),
      },
    );

    // Return Prisma-compatible shape — spread caller data first, then override defaults
    const { id: _dataId, ...restData } = d;
    return {
      ...restData,
      id: d.id || convexId || "",
      role: d.role ?? "CUSTOMER",
      createdAt: new Date(),
      updatedAt: new Date(),
      isActive: d.isActive ?? true,
      isSuspended: d.isSuspended ?? false,
      onboardingComplete: d.onboardingComplete ?? false,
      onboardingStep: d.onboardingStep ?? 0,
      failedAttempts: d.failedAttempts ?? 0,
      loginCount: d.loginCount ?? 0,
      whatsappConnected: d.whatsappConnected ?? false,
      clerkId: d.clerkId ?? null,
      googleId: d.googleId ?? null,
    };
  },

  // ─── update ────────────────────────────────────────────────────────────

  async update(args: {
    where: { id?: string; email?: string };
    data: Record<string, unknown>;
    select?: Record<string, boolean>;
    include?: Record<string, unknown>;
  }) {
    if (!isConvex()) {
      return prisma.users.update(args as any);
    }

    const w = args.where;

    // Convert Date fields to Convex timestamps
    const convexData: Record<string, unknown> = {};
    for (const [key, value] of Object.entries(args.data)) {
      if (value instanceof Date) {
        convexData[key] = dateToConvexTimestamp(value);
      } else {
        convexData[key] = value;
      }
    }

    await convexMutation("auth/mutations:updateUserNoAuth", {
      ...(w.id ? { userId: w.id } : {}),
      ...(w.email ? { email: w.email } : {}),
      ...convexData,
    });

    // Return best-effort merged shape
    const existing = await this.findUnique({ where: w as any });
    return existing ? { ...existing, ...args.data } : null;
  },

  // ─── upsert ────────────────────────────────────────────────────────────

  async upsert(args: {
    where: { id?: string; email?: string; googleId?: string; clerkId?: string };
    create: Record<string, unknown>;
    update: Record<string, unknown>;
    select?: Record<string, boolean>;
    include?: Record<string, unknown>;
  }) {
    if (!isConvex()) {
      return prisma.users.upsert(args as any);
    }

    const existing = await this.findUnique({ where: args.where as any });
    if (existing) {
      const existingRecord = existing as Record<string, unknown>;
      return this.update({
        where: { id: existingRecord.id as string },
        data: args.update,
      });
    }

    return this.create({ data: args.create as any });
  },

  // ─── delete ────────────────────────────────────────────────────────────

  async delete(args: { where: { id: string } }) {
    if (!isConvex()) {
      return prisma.users.delete(args as any);
    }

    await convexMutation("auth/mutations:deleteUser", {
      id: args.where.id,
    });

    return { id: args.where.id };
  },

  // ─── deleteMany ────────────────────────────────────────────────────────

  async deleteMany(args: {
    where: Record<string, unknown>;
  }): Promise<BatchResult> {
    if (!isConvex()) {
      return prisma.users.deleteMany(args as any);
    }

    const docs = await this.findMany({ where: args.where, take: 100000 });
    const items = docs as Array<Record<string, unknown>>;
    for (const item of items) {
      await convexMutation("auth/mutations:deleteUser", {
        id: item.id as string,
      });
    }
    return { count: items.length };
  },

  // ─── updateMany ────────────────────────────────────────────────────────

  async updateMany(args: {
    where: Record<string, unknown>;
    data: Record<string, unknown>;
  }): Promise<BatchResult> {
    if (!isConvex()) {
      return prisma.users.updateMany(args as any);
    }

    const docs = await this.findMany({ where: args.where, take: 100000 });
    const items = docs as Array<Record<string, unknown>>;
    let updatedCount = 0;
    for (const item of items) {
      await convexMutation("auth/mutations:updateUserNoAuth", {
        userId: item.id as string,
        ...args.data,
      });
      updatedCount++;
    }
    return { count: updatedCount };
  },
};

// ═══════════════════════════════════════════════════════════════════════════
// USER_SESSIONS — prisma.user_sessions.*
// ═══════════════════════════════════════════════════════════════════════════

export const userSessions = {
  // ─── findMany ──────────────────────────────────────────────────────────

  async findMany(
    args: {
      where?: Record<string, unknown>;
      orderBy?: Record<string, string> | Record<string, string>[];
      take?: number;
      skip?: number;
    } = {},
  ) {
    if (!isConvex()) {
      return prisma.user_sessions.findMany(args as any);
    }

    const where = (args.where || {}) as Record<string, unknown>;
    const userId = where.userId as string | undefined;

    let docs: ConvexDoc[] = [];
    if (userId) {
      docs =
        (await convexQuery<ConvexDoc[]>("auth/queries:listUserSessions", {
          userId,
        })) || [];
    } else {
      // No userId filter — not ideal for Convex, fetch what we can
      docs =
        (await convexQuery<ConvexDoc[]>("auth/queries:listUserSessions", {
          userId: "",
        })) || [];
    }

    // Client-side filtering for additional conditions
    if (
      Object.keys(where).length > 1 ||
      (!userId && Object.keys(where).length > 0)
    ) {
      docs = docs.filter((doc) => {
        for (const [key, value] of Object.entries(where)) {
          if (value === undefined) continue;
          const docVal = key === "id" ? doc.prismaId || doc._id : doc[key];
          if (typeof value === "object" && value !== null) {
            const op = value as Record<string, unknown>;
            if (op.lt) {
              const ltMs =
                op.lt instanceof Date ? op.lt.getTime() : (op.lt as number);
              if ((docVal as number) >= ltMs) return false;
            }
            if (op.gt) {
              const gtMs =
                op.gt instanceof Date ? op.gt.getTime() : (op.gt as number);
              if ((docVal as number) <= gtMs) return false;
            }
          } else if (docVal !== value) return false;
        }
        return true;
      });
    }

    const paginated = applyPagination(docs, args.skip, args.take);
    return convexToPrismaShapeMany(paginated, SESSION_TS_FIELDS);
  },

  // ─── findFirst ─────────────────────────────────────────────────────────

  async findFirst(
    args: {
      where?: Record<string, unknown>;
      orderBy?: Record<string, string> | Record<string, string>[];
    } = {},
  ) {
    if (!isConvex()) {
      return prisma.user_sessions.findFirst(args as any);
    }

    const where = (args.where || {}) as Record<string, unknown>;
    const userId = where.userId as string | undefined;

    if (userId) {
      const doc = await convexQuery<ConvexDoc>("auth/queries:getSession", {
        userId,
      });
      if (!doc) return null;

      // Verify additional where conditions
      for (const [key, value] of Object.entries(where)) {
        if (key === "userId" || value === undefined) continue;
        const docVal = key === "id" ? doc.prismaId || doc._id : doc[key];
        if (docVal !== value) return null;
      }

      return convexToPrismaShape(doc, SESSION_TS_FIELDS);
    }

    // Fallback: findMany + pick first
    const results = await this.findMany({ where, take: 1 });
    const items = results as unknown[];
    return items.length > 0 ? items[0] : null;
  },

  // ─── findUnique ────────────────────────────────────────────────────────

  async findUnique(args: { where: { id?: string; token?: string } }) {
    if (!isConvex()) {
      return prisma.user_sessions.findUnique(args as any);
    }

    return this.findFirst({ where: args.where as Record<string, unknown> });
  },

  // ─── count ─────────────────────────────────────────────────────────────

  async count(args?: { where?: Record<string, unknown> }) {
    if (!isConvex()) {
      return prisma.user_sessions.count(args as any);
    }

    const docs = await this.findMany({ where: args?.where, take: 100000 });
    return (docs as unknown[]).length;
  },

  // ─── create ────────────────────────────────────────────────────────────

  async create(args: {
    data: {
      id?: string;
      userId: string;
      token: string;
      deviceName: string;
      deviceType: string;
      ipAddress: string;
      userAgent: string;
      expiresAt: Date | string | number;
      loginHistoryId?: string;
      [key: string]: unknown;
    };
  }) {
    if (!isConvex()) {
      return prisma.user_sessions.create(args as any);
    }

    const d = args.data;
    const convexId = await convexMutation<string>(
      "auth/mutations:createSession",
      {
        userId: d.userId,
        token: d.token,
        deviceName: d.deviceName,
        deviceType: d.deviceType,
        ipAddress: d.ipAddress,
        userAgent: d.userAgent,
        expiresAt: dateToConvexTimestamp(d.expiresAt) || Date.now() + 86400000,
      },
    );

    return {
      id: d.id || convexId || "",
      userId: d.userId,
      token: d.token,
      deviceName: d.deviceName,
      deviceType: d.deviceType,
      ipAddress: d.ipAddress,
      userAgent: d.userAgent,
      lastActive: new Date(),
      createdAt: new Date(),
      expiresAt:
        d.expiresAt instanceof Date ? d.expiresAt : new Date(d.expiresAt),
      loginHistoryId: d.loginHistoryId ?? null,
      location: null,
      country: null,
      city: null,
    };
  },

  // ─── update ────────────────────────────────────────────────────────────

  async update(args: { where: { id: string }; data: Record<string, unknown> }) {
    if (!isConvex()) {
      return prisma.user_sessions.update(args as any);
    }

    // Convex doesn't have a dedicated session update; fall back to Prisma for now
    return prisma.user_sessions.update(args as any);
  },

  // ─── delete ────────────────────────────────────────────────────────────

  async delete(args: { where: { id: string } }) {
    if (!isConvex()) {
      return prisma.user_sessions.delete(args as any);
    }

    await convexMutation("auth/mutations:deleteSession", {
      id: args.where.id,
    });

    return { id: args.where.id };
  },

  // ─── deleteMany ────────────────────────────────────────────────────────

  async deleteMany(args: {
    where: Record<string, unknown>;
  }): Promise<BatchResult> {
    if (!isConvex()) {
      return prisma.user_sessions.deleteMany(args as any);
    }

    const docs = await this.findMany({ where: args.where, take: 100000 });
    const items = docs as Array<Record<string, unknown>>;
    for (const item of items) {
      await convexMutation("auth/mutations:deleteSession", {
        id: item.id as string,
      });
    }
    return { count: items.length };
  },

  // ─── updateMany ────────────────────────────────────────────────────────

  async updateMany(args: {
    where: Record<string, unknown>;
    data: Record<string, unknown>;
  }): Promise<BatchResult> {
    if (!isConvex()) {
      return prisma.user_sessions.updateMany(args as any);
    }

    // Convex has no bulk session update — fall back to Prisma
    return prisma.user_sessions.updateMany(args as any);
  },
};

// ═══════════════════════════════════════════════════════════════════════════
// OTPS — prisma.otps.*
// ═══════════════════════════════════════════════════════════════════════════

export const otps = {
  // ─── findMany ──────────────────────────────────────────────────────────

  async findMany(
    args: {
      where?: Record<string, unknown>;
      orderBy?: Record<string, string> | Record<string, string>[];
      take?: number;
      skip?: number;
    } = {},
  ) {
    if (!isConvex()) {
      return prisma.otps.findMany(args as any);
    }

    // OTPs are security-critical; Convex queries may not cover all patterns
    // Fall back to Prisma for complex queries
    return prisma.otps.findMany(args as any);
  },

  // ─── findFirst ─────────────────────────────────────────────────────────

  async findFirst(
    args: {
      where?: Record<string, unknown>;
      orderBy?: Record<string, string> | Record<string, string>[];
    } = {},
  ) {
    if (!isConvex()) {
      return prisma.otps.findFirst(args as any);
    }

    // OTPs require precise ordering and expiry checks — Prisma is authoritative
    return prisma.otps.findFirst(args as any);
  },

  // ─── findUnique ────────────────────────────────────────────────────────

  async findUnique(args: { where: { id: string } }) {
    if (!isConvex()) {
      return prisma.otps.findUnique(args as any);
    }

    return prisma.otps.findUnique(args as any);
  },

  // ─── count ─────────────────────────────────────────────────────────────

  async count(args?: { where?: Record<string, unknown> }) {
    if (!isConvex()) {
      return prisma.otps.count(args as any);
    }

    return prisma.otps.count(args as any);
  },

  // ─── create ────────────────────────────────────────────────────────────

  async create(args: {
    data: {
      id?: string;
      email: string;
      otp_code: string;
      purpose: string;
      expires_at: Date | string | number;
      ip_address?: string;
      [key: string]: unknown;
    };
  }) {
    if (!isConvex()) {
      return prisma.otps.create(args as any);
    }

    // Write to both Prisma (authoritative) and Convex (mirror)
    return prisma.otps.create(args as any);
  },

  // ─── update ────────────────────────────────────────────────────────────

  async update(args: { where: { id: string }; data: Record<string, unknown> }) {
    if (!isConvex()) {
      return prisma.otps.update(args as any);
    }

    return prisma.otps.update(args as any);
  },

  // ─── updateMany ────────────────────────────────────────────────────────

  async updateMany(args: {
    where: Record<string, unknown>;
    data: Record<string, unknown>;
  }): Promise<BatchResult> {
    if (!isConvex()) {
      return prisma.otps.updateMany(args as any);
    }

    return prisma.otps.updateMany(args as any);
  },

  // ─── delete ────────────────────────────────────────────────────────────

  async delete(args: { where: { id: string } }) {
    if (!isConvex()) {
      return prisma.otps.delete(args as any);
    }

    return prisma.otps.delete(args as any);
  },

  // ─── deleteMany ────────────────────────────────────────────────────────

  async deleteMany(args: {
    where: Record<string, unknown>;
  }): Promise<BatchResult> {
    if (!isConvex()) {
      return prisma.otps.deleteMany(args as any);
    }

    return prisma.otps.deleteMany(args as any);
  },
};

// ═══════════════════════════════════════════════════════════════════════════
// USER_PREFERENCES — prisma.user_preferences.*
// ═══════════════════════════════════════════════════════════════════════════

export const userPreferences = {
  // ─── findMany ──────────────────────────────────────────────────────────

  async findMany(
    args: {
      where?: Record<string, unknown>;
      orderBy?: Record<string, string> | Record<string, string>[];
      take?: number;
      skip?: number;
    } = {},
  ) {
    if (!isConvex()) {
      return prisma.user_preferences.findMany(args as any);
    }

    // user_preferences is typically queried by userId (1:1 relation)
    return prisma.user_preferences.findMany(args as any);
  },

  // ─── findFirst ─────────────────────────────────────────────────────────

  async findFirst(
    args: {
      where?: Record<string, unknown>;
    } = {},
  ) {
    if (!isConvex()) {
      return prisma.user_preferences.findFirst(args as any);
    }

    return prisma.user_preferences.findFirst(args as any);
  },

  // ─── findUnique ────────────────────────────────────────────────────────

  async findUnique(args: { where: { id?: string; userId?: string } }) {
    if (!isConvex()) {
      return prisma.user_preferences.findUnique(args as any);
    }

    return prisma.user_preferences.findUnique(args as any);
  },

  // ─── count ─────────────────────────────────────────────────────────────

  async count(args?: { where?: Record<string, unknown> }) {
    if (!isConvex()) {
      return prisma.user_preferences.count(args as any);
    }

    return prisma.user_preferences.count(args as any);
  },

  // ─── create ────────────────────────────────────────────────────────────

  async create(args: {
    data: {
      id?: string;
      userId: string;
      language?: string;
      timezone?: string;
      dateFormat?: string;
      theme?: string;
      emailNotifications?: unknown;
      smsNotifications?: unknown;
      whatsappNotifications?: unknown;
      emailFrequency?: string;
      dashboardPreferences?: unknown;
      [key: string]: unknown;
    };
  }) {
    if (!isConvex()) {
      return prisma.user_preferences.create(args as any);
    }

    return prisma.user_preferences.create(args as any);
  },

  // ─── update ────────────────────────────────────────────────────────────

  async update(args: {
    where: { id?: string; userId?: string };
    data: Record<string, unknown>;
  }) {
    if (!isConvex()) {
      return prisma.user_preferences.update(args as any);
    }

    return prisma.user_preferences.update(args as any);
  },

  // ─── upsert ────────────────────────────────────────────────────────────

  async upsert(args: {
    where: { userId?: string; id?: string };
    create: Record<string, unknown>;
    update: Record<string, unknown>;
  }) {
    if (!isConvex()) {
      return prisma.user_preferences.upsert(args as any);
    }

    return prisma.user_preferences.upsert(args as any);
  },

  // ─── delete ────────────────────────────────────────────────────────────

  async delete(args: { where: { id?: string; userId?: string } }) {
    if (!isConvex()) {
      return prisma.user_preferences.delete(args as any);
    }

    return prisma.user_preferences.delete(args as any);
  },

  // ─── deleteMany ────────────────────────────────────────────────────────

  async deleteMany(args: {
    where: Record<string, unknown>;
  }): Promise<BatchResult> {
    if (!isConvex()) {
      return prisma.user_preferences.deleteMany(args as any);
    }

    return prisma.user_preferences.deleteMany(args as any);
  },
};

// ═══════════════════════════════════════════════════════════════════════════
// LOGIN_HISTORY — prisma.login_history.*
// ═══════════════════════════════════════════════════════════════════════════

export const loginHistory = {
  // ─── findMany ──────────────────────────────────────────────────────────

  async findMany(
    args: {
      where?: Record<string, unknown>;
      orderBy?: Record<string, string> | Record<string, string>[];
      take?: number;
      skip?: number;
    } = {},
  ) {
    if (!isConvex()) {
      return prisma.login_history.findMany(args as any);
    }

    const where = (args.where || {}) as Record<string, unknown>;
    const userId = where.userId as string | undefined;

    if (userId) {
      const docs =
        (await convexQuery<ConvexDoc[]>("auth/queries:getLoginHistory", {
          userId,
          limit: (args.take || 20) + (args.skip || 0),
        })) || [];

      const paginated = applyPagination(docs, args.skip, args.take);
      return convexToPrismaShapeMany(paginated, LOGIN_HISTORY_TS_FIELDS);
    }

    // No userId — fall back to Prisma
    return prisma.login_history.findMany(args as any);
  },

  // ─── findFirst ─────────────────────────────────────────────────────────

  async findFirst(
    args: {
      where?: Record<string, unknown>;
      orderBy?: Record<string, string> | Record<string, string>[];
    } = {},
  ) {
    if (!isConvex()) {
      return prisma.login_history.findFirst(args as any);
    }

    const where = (args.where || {}) as Record<string, unknown>;
    const userId = where.userId as string | undefined;

    if (userId) {
      const docs =
        (await convexQuery<ConvexDoc[]>("auth/queries:getLoginHistory", {
          userId,
          limit: 1,
        })) || [];

      return docs.length > 0
        ? convexToPrismaShape(docs[0], LOGIN_HISTORY_TS_FIELDS)
        : null;
    }

    return prisma.login_history.findFirst(args as any);
  },

  // ─── findUnique ────────────────────────────────────────────────────────

  async findUnique(args: { where: { id: string } }) {
    if (!isConvex()) {
      return prisma.login_history.findUnique(args as any);
    }

    return prisma.login_history.findUnique(args as any);
  },

  // ─── count ─────────────────────────────────────────────────────────────

  async count(args?: { where?: Record<string, unknown> }) {
    if (!isConvex()) {
      return prisma.login_history.count(args as any);
    }

    const where = (args?.where || {}) as Record<string, unknown>;
    const userId = where.userId as string | undefined;

    if (userId) {
      const docs =
        (await convexQuery<ConvexDoc[]>("auth/queries:getLoginHistory", {
          userId,
          limit: 100000,
        })) || [];
      return docs.length;
    }

    return prisma.login_history.count(args as any);
  },

  // ─── create ────────────────────────────────────────────────────────────

  async create(args: {
    data: {
      id?: string;
      userId: string;
      ipAddress: string;
      provider?: string;
      success?: boolean;
      userAgent?: string;
      failReason?: string;
      country?: string;
      countryCode?: string;
      city?: string;
      region?: string;
      latitude?: number;
      longitude?: number;
      timezone?: string;
      [key: string]: unknown;
    };
  }) {
    if (!isConvex()) {
      return prisma.login_history.create(args as any);
    }

    const d = args.data;
    const convexId = await convexMutation<string>(
      "auth/mutations:createLoginHistory",
      {
        userId: d.userId,
        ipAddress: d.ipAddress,
        provider: d.provider ?? "credentials",
        success: d.success ?? true,
        ...(d.userAgent ? { userAgent: d.userAgent } : {}),
        ...(d.failReason ? { failReason: d.failReason } : {}),
        ...(d.country ? { country: d.country } : {}),
        ...(d.countryCode ? { countryCode: d.countryCode } : {}),
        ...(d.city ? { city: d.city } : {}),
        ...(d.region ? { region: d.region } : {}),
        ...(d.latitude !== undefined ? { latitude: d.latitude } : {}),
        ...(d.longitude !== undefined ? { longitude: d.longitude } : {}),
        ...(d.timezone ? { timezone: d.timezone } : {}),
      },
    );

    return {
      id: d.id || convexId || "",
      userId: d.userId,
      ipAddress: d.ipAddress,
      provider: d.provider ?? "credentials",
      success: d.success ?? true,
      userAgent: d.userAgent ?? null,
      failReason: d.failReason ?? null,
      country: d.country ?? null,
      countryCode: d.countryCode ?? null,
      city: d.city ?? null,
      region: d.region ?? null,
      latitude: d.latitude ?? null,
      longitude: d.longitude ?? null,
      timezone: d.timezone ?? null,
      createdAt: new Date(),
      logoutAt: null,
    };
  },

  // ─── update ────────────────────────────────────────────────────────────

  async update(args: { where: { id: string }; data: Record<string, unknown> }) {
    if (!isConvex()) {
      return prisma.login_history.update(args as any);
    }

    // No dedicated Convex mutation for login_history update
    return prisma.login_history.update(args as any);
  },

  // ─── delete ────────────────────────────────────────────────────────────

  async delete(args: { where: { id: string } }) {
    if (!isConvex()) {
      return prisma.login_history.delete(args as any);
    }

    return prisma.login_history.delete(args as any);
  },

  // ─── deleteMany ────────────────────────────────────────────────────────

  async deleteMany(args: {
    where: Record<string, unknown>;
  }): Promise<BatchResult> {
    if (!isConvex()) {
      return prisma.login_history.deleteMany(args as any);
    }

    return prisma.login_history.deleteMany(args as any);
  },
};

// ═══════════════════════════════════════════════════════════════════════════
// ONBOARDING_PROGRESS — prisma.onboarding_progress.*
// ═══════════════════════════════════════════════════════════════════════════

export const onboardingProgress = {
  // ─── findMany ──────────────────────────────────────────────────────────

  async findMany(
    args: {
      where?: Record<string, unknown>;
      orderBy?: Record<string, string> | Record<string, string>[];
      take?: number;
      skip?: number;
    } = {},
  ) {
    if (!isConvex()) {
      return prisma.onboarding_progress.findMany(args as any);
    }

    return prisma.onboarding_progress.findMany(args as any);
  },

  // ─── findFirst ─────────────────────────────────────────────────────────

  async findFirst(
    args: {
      where?: Record<string, unknown>;
    } = {},
  ) {
    if (!isConvex()) {
      return prisma.onboarding_progress.findFirst(args as any);
    }

    return prisma.onboarding_progress.findFirst(args as any);
  },

  // ─── findUnique ────────────────────────────────────────────────────────

  async findUnique(args: { where: { id?: string; userId?: string } }) {
    if (!isConvex()) {
      return prisma.onboarding_progress.findUnique(args as any);
    }

    return prisma.onboarding_progress.findUnique(args as any);
  },

  // ─── count ─────────────────────────────────────────────────────────────

  async count(args?: { where?: Record<string, unknown> }) {
    if (!isConvex()) {
      return prisma.onboarding_progress.count(args as any);
    }

    return prisma.onboarding_progress.count(args as any);
  },

  // ─── create ────────────────────────────────────────────────────────────

  async create(args: {
    data: {
      id?: string;
      userId: string;
      welcomeCompleted?: boolean;
      whatsappCompleted?: boolean;
      whatsappMethod?: string;
      voiceAgentCompleted?: boolean;
      knowledgeBaseCompleted?: boolean;
      workflowsCompleted?: boolean;
      testingCompleted?: boolean;
      [key: string]: unknown;
    };
  }) {
    if (!isConvex()) {
      return prisma.onboarding_progress.create(args as any);
    }

    return prisma.onboarding_progress.create(args as any);
  },

  // ─── update ────────────────────────────────────────────────────────────

  async update(args: {
    where: { id?: string; userId?: string };
    data: Record<string, unknown>;
  }) {
    if (!isConvex()) {
      return prisma.onboarding_progress.update(args as any);
    }

    return prisma.onboarding_progress.update(args as any);
  },

  // ─── upsert ────────────────────────────────────────────────────────────

  async upsert(args: {
    where: { userId?: string; id?: string };
    create: Record<string, unknown>;
    update: Record<string, unknown>;
  }) {
    if (!isConvex()) {
      return prisma.onboarding_progress.upsert(args as any);
    }

    return prisma.onboarding_progress.upsert(args as any);
  },

  // ─── delete ────────────────────────────────────────────────────────────

  async delete(args: { where: { id?: string; userId?: string } }) {
    if (!isConvex()) {
      return prisma.onboarding_progress.delete(args as any);
    }

    return prisma.onboarding_progress.delete(args as any);
  },

  // ─── deleteMany ────────────────────────────────────────────────────────

  async deleteMany(args: {
    where: Record<string, unknown>;
  }): Promise<BatchResult> {
    if (!isConvex()) {
      return prisma.onboarding_progress.deleteMany(args as any);
    }

    return prisma.onboarding_progress.deleteMany(args as any);
  },
};

// ═══════════════════════════════════════════════════════════════════════════
// AUDIT_LOGS — prisma.audit_logs.*
// ═══════════════════════════════════════════════════════════════════════════

export const auditLogs = {
  // ─── findMany ──────────────────────────────────────────────────────────

  async findMany(
    args: {
      where?: Record<string, unknown>;
      orderBy?: Record<string, string> | Record<string, string>[];
      take?: number;
      skip?: number;
    } = {},
  ) {
    if (!isConvex()) {
      return prisma.audit_logs.findMany(args as any);
    }

    // audit_logs are heavily queried with complex filters — Prisma is authoritative
    return prisma.audit_logs.findMany(args as any);
  },

  // ─── findFirst ─────────────────────────────────────────────────────────

  async findFirst(
    args: {
      where?: Record<string, unknown>;
      orderBy?: Record<string, string> | Record<string, string>[];
    } = {},
  ) {
    if (!isConvex()) {
      return prisma.audit_logs.findFirst(args as any);
    }

    return prisma.audit_logs.findFirst(args as any);
  },

  // ─── findUnique ────────────────────────────────────────────────────────

  async findUnique(args: { where: { id: string } }) {
    if (!isConvex()) {
      return prisma.audit_logs.findUnique(args as any);
    }

    return prisma.audit_logs.findUnique(args as any);
  },

  // ─── count ─────────────────────────────────────────────────────────────

  async count(args?: { where?: Record<string, unknown> }) {
    if (!isConvex()) {
      return prisma.audit_logs.count(args as any);
    }

    return prisma.audit_logs.count(args as any);
  },

  // ─── create ────────────────────────────────────────────────────────────

  async create(args: {
    data: {
      id?: string;
      userId?: string;
      type: string;
      action: string;
      success: boolean;
      ip: string;
      userAgent?: string;
      metadata?: unknown;
      [key: string]: unknown;
    };
  }) {
    if (!isConvex()) {
      return prisma.audit_logs.create(args as any);
    }

    // Write to Prisma (authoritative) — also mirror to Convex for real-time queries
    const result = await prisma.audit_logs.create(args as any);

    // Best-effort mirror to Convex (non-blocking)
    convexMutation("auth/mutations:createAuditLog", {
      userId: args.data.userId ?? "",
      type: args.data.type,
      action: args.data.action,
      success: args.data.success,
      ip: args.data.ip,
      ...(args.data.userAgent ? { userAgent: args.data.userAgent } : {}),
      ...(args.data.metadata !== undefined
        ? { metadata: args.data.metadata }
        : {}),
    }).catch(() => {
      // Silently ignore Convex mirror failures — Prisma is the source of truth
    });

    return result;
  },

  // ─── update ────────────────────────────────────────────────────────────

  async update(args: { where: { id: string }; data: Record<string, unknown> }) {
    if (!isConvex()) {
      return prisma.audit_logs.update(args as any);
    }

    return prisma.audit_logs.update(args as any);
  },

  // ─── delete ────────────────────────────────────────────────────────────

  async delete(args: { where: { id: string } }) {
    if (!isConvex()) {
      return prisma.audit_logs.delete(args as any);
    }

    return prisma.audit_logs.delete(args as any);
  },

  // ─── deleteMany ────────────────────────────────────────────────────────

  async deleteMany(args: {
    where: Record<string, unknown>;
  }): Promise<BatchResult> {
    if (!isConvex()) {
      return prisma.audit_logs.deleteMany(args as any);
    }

    return prisma.audit_logs.deleteMany(args as any);
  },
};

// ═══════════════════════════════════════════════════════════════════════════
// Namespace Exports
// ═══════════════════════════════════════════════════════════════════════════

export const usersAdapter = users;
export const userSessionsAdapter = userSessions;
export const otpsAdapter = otps;
export const userPreferencesAdapter = userPreferences;
export const loginHistoryAdapter = loginHistory;
export const onboardingProgressAdapter = onboardingProgress;
export const auditLogsAdapter = auditLogs;

// ═══════════════════════════════════════════════════════════════════════════
// FILES THAT NEED WIRING (do NOT wire yet — auth is referenced everywhere)
// ═══════════════════════════════════════════════════════════════════════════
//
// --- prisma.users.* (43 lib files + 126 API routes) ---
//
// lib/services:
//   - lib/services/subscription-access.service.ts (findUnique)
//   - lib/services/lead-crm.service.ts (findUnique)
//   - lib/services/support-tickets.service.ts (findUnique)
//   - lib/services/notifications.service.ts (findUnique)
//   - lib/services/dashboard.service.ts (findMany, count)
//   - lib/services/whatsapp.service.ts (findUnique)
//   - lib/services/webhook-provisioning.service.ts (findMany, update)
//   - lib/services/user-management.service.ts (findMany, findUnique, update, delete)
//   - lib/services/user-currency.service.ts (findUnique)
//   - lib/services/user-cache.service.ts (findUnique)
//   - lib/services/session-manager.ts (findUnique, user_sessions create/delete/findMany)
//   - lib/services/security-audit.service.ts (findUnique, audit_logs create, login_history)
//   - lib/services/otp.service.ts (otps create/findFirst/update)
//   - lib/services/organization.service.ts (findUnique)
//   - lib/services/notification-emails.service.ts (findUnique)
//   - lib/services/lead-scoring.ts (findUnique)
//   - lib/services/health-score.service.ts (count, user_sessions count, login_history count)
//   - lib/services/google-calendar-user.service.ts (user_preferences findFirst)
//   - lib/services/billing-emails.service.ts (findUnique)
//   - lib/services/admin-notes.service.ts (findUnique)
//
// lib core:
//   - lib/auth.ts (findUnique, login_history create)
//   - lib/auth-helpers.ts (findUnique)
//   - lib/clerk-user-bridge.ts (findUnique, create, update)
//   - lib/dashboard-access.ts (findUnique)
//   - lib/otp-storage.ts (otps create/findFirst/update/deleteMany)
//   - lib/otp-helper.ts (otps findFirst/update)
//   - lib/dual-database.ts (findUnique, audit_logs create)
//   - lib/db-read-replica.ts (findUnique)
//   - lib/analytics-tracker.ts (findUnique)
//   - lib/staff-session.ts (findUnique)
//   - lib/system-alerts.ts (findUnique)
//   - lib/sync-queue.ts (create)
//   - lib/utils/auth-helpers.util.ts (audit_logs create)
//   - lib/utils/voice-agent-db.ts (findUnique)
//   - lib/utils/permissions.ts (findUnique)
//   - lib/utils/calendar-sync.ts (user_preferences findUnique)
//   - lib/utils/api-response.util.ts (audit_logs)
//   - lib/cache/redis-cache.ts (findUnique)
//   - lib/cache/examples.ts (update)
//   - lib/billing/billing.service.ts (findUnique)
//   - lib/config/preferences.config.ts (user_preferences)
//   - lib/cron/audit-log-cleanup.ts (audit_logs deleteMany)
//
// API routes (126 files — major ones):
//   - app/api/auth/login/route.ts (findUnique, findFirst, update)
//   - app/api/auth/verify-signup-otp/route.ts (findUnique, create, update)
//   - app/api/auth/verify-login-otp/route.ts (update, audit_logs)
//   - app/api/auth/verify-otp-and-activate/route.ts (update, audit_logs)
//   - app/api/auth/verify-google-signup-otp/route.ts (findUnique, create)
//   - app/api/auth/verify-email-only/route.ts (audit_logs)
//   - app/api/auth/session-from-otp/route.ts (findUnique, update)
//   - app/api/auth/reset-password/route.ts (findFirst, update, audit_logs)
//   - app/api/auth/forgot-password/route.ts (findFirst, audit_logs)
//   - app/api/auth/check-email/route.ts (findUnique)
//   - app/api/auth/logout/route.ts (user_sessions delete)
//   - app/api/auth/validate-invitation/route.ts (findUnique)
//   - app/api/user/profile/route.ts (findUnique, update)
//   - app/api/user/avatar/route.ts (update)
//   - app/api/user/change-password/route.ts (findUnique, update)
//   - app/api/user/preferences/route.ts (user_preferences findUnique, upsert)
//   - app/api/user/sessions/[id]/route.ts (user_sessions findFirst, delete)
//   - app/api/user/audit-log/route.ts (audit_logs findMany)
//   - app/api/user/delete-account/route.ts (delete)
//   - app/api/user/export-data/route.ts (findUnique + includes)
//   - app/api/user/package-status/route.ts (findUnique)
//   - app/api/user/calendar-status/route.ts (findUnique)
//   - app/api/user/links/route.ts (findUnique)
//   - app/api/user/customers/* (findUnique)
//   - app/api/signup/success/route.ts (update, user_preferences create, audit_logs)
//   - app/api/signup/checkout/route.ts (findUnique, update)
//   - app/api/signup/company-info/route.ts (update, audit_logs)
//   - app/api/onboarding/company-info/route.ts (update)
//   - app/api/clerk/webhook/route.ts (findUnique, create, update)
//   - app/api/stripe/webhook/route.ts (findUnique, findFirst)
//   - app/api/stripe/billing-portal/route.ts (findUnique)
//   - app/api/stripe/invoices/route.ts (findUnique)
//   - app/api/subscriptions/create-checkout/route.ts (findUnique)
//   - app/api/subscription/status/route.ts (findUnique)
//   - app/api/billing/*/route.ts (findUnique)
//   - app/api/organization/*/route.ts (findUnique)
//   - app/api/staff/dashboard/users/route.ts (findMany, count)
//   - app/api/staff/dashboard/overview/route.ts (count, user_sessions count)
//   - app/api/staff/dashboard/analytics/route.ts (findMany, count, user_sessions)
//   - app/api/staff/dashboard/staff/*/route.ts (findMany, create, update)
//   - app/api/staff/users/[id]/*/route.ts (findUnique, update)
//   - app/api/staff/auth/login/route.ts (findUnique, update)
//   - app/api/staff/auth/change-password/route.ts (findUnique, update)
//   - app/api/staff/admin/user-lookup/route.ts (findFirst, audit_logs)
//   - app/api/staff/analytics/*/route.ts (count, findMany)
//   - app/api/integrations/*/route.ts (findUnique)
//   - app/api/settings/*/route.ts (findUnique)
//   - app/api/n8n/*/route.ts (findUnique)
//   - app/api/voice-agent/*/route.ts (findUnique)
//   - app/api/dashboard/*/route.ts (findUnique)
//   - app/api/bookings/*/route.ts (findUnique)
//   - app/api/webhooks/*/route.ts (findUnique)
//   - app/api/analytics/*/route.ts (findUnique)
//   - app/api/csp-report/route.ts (audit_logs)
//   - app/api/conversations/route.ts (findUnique)
//   - app/api/payment/verify-session/route.ts (findUnique, update)
//   - app/api/admin/sync-stripe-subscription/route.ts (findUnique)
//   - app/api/debug/subscription/route.ts (findUnique)
//
// --- prisma.otps.* (4 files) ---
//   - lib/otp-helper.ts
//   - lib/otp-storage.ts
//   - scripts/test-otp-table.ts
//   - tests/signup-package-flow.spec.ts
//
// --- prisma.user_sessions.* (7 files, excl. generated) ---
//   - app/api/auth/logout/route.ts
//   - lib/services/session-manager.ts
//   - lib/services/health-score.service.ts
//   - app/api/user/sessions/[id]/route.ts
//   - app/api/staff/dashboard/overview/route.ts
//   - app/api/staff/dashboard/analytics/route.ts
//   - tests/e2e/signup-session-creation.spec.ts
//
// --- prisma.user_preferences.* (8 files, excl. generated) ---
//   - app/api/signup/success/route.ts
//   - lib/utils/calendar-sync.ts
//   - lib/services/google-calendar-user.service.ts
//   - lib/config/preferences.config.ts
//   - app/api/user/preferences/route.ts
//   - __tests__/api/user/preferences.test.ts
//   - __tests__/api/user-preferences.test.ts
//   - __tests__/api/bookings/create-customer-from-appointment.test.ts
//
// --- prisma.login_history.* (6 files, excl. generated) ---
//   - lib/auth.ts
//   - lib/services/session-manager.ts
//   - lib/services/security-audit.service.ts
//   - lib/services/health-score.service.ts
//   - tests/staff/user-management.spec.ts
//   - tests/staff/user-management-workflows.spec.ts
//
// --- prisma.onboarding_progress.* (2 files, excl. generated) ---
//   - lib/services/webhook-provisioning.service.ts
//   - app/api/staff/analytics/feature-adoption/route.ts
//
// --- prisma.audit_logs.* (45 files, excl. generated) ---
//   - lib/utils/auth-helpers.util.ts
//   - lib/services/security-audit.service.ts
//   - lib/services/otp.service.ts
//   - lib/services/health-score.service.ts
//   - lib/otp-storage.ts
//   - lib/dual-database.ts
//   - lib/cron/audit-log-cleanup.ts
//   - app/api/user/audit-log/route.ts
//   - app/api/auth/*/route.ts (multiple)
//   - app/api/signup/*/route.ts (multiple)
//   - app/api/staff/*/route.ts (multiple)
//   - app/api/csp-report/route.ts
//   - scripts/fix-all-audit-log-ids.ts
//   - scripts/verify-signup-database.ts
//   - (plus ~20 more test and script files)
//
