/**
 * Voice Agents Domain Adapter
 *
 * Routes voice-agent and knowledge-base CRUD to Prisma or Convex
 * based on data-source config.
 *
 * Sub-models:
 *   knowledgeBaseSettings, knowledgeBaseDocument, knowledgeBaseFAQ,
 *   knowledgeBaseChunk, voiceAgentPhoneConfig, voiceAgentKBSync,
 *   voiceAgentSettings, conversationLog, conversationLogMessage,
 *   voiceConversationAction
 *
 * Note: 8 elevenlabs-*.service.ts files are pure ElevenLabs API wrappers
 * with zero DB deps -- they are NOT wired through this adapter.
 */

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

// ─────────────────────────────────────────────────────────────────────────────
// Domain config
// ─────────────────────────────────────────────────────────────────────────────

const DOMAIN = "voice_agents" as const;
const TIMESTAMP_FIELDS = ["createdAt", "updatedAt", "lastActiveAt"];

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

// ═══════════════════════════════════════════════════════════════════════════════
// 1. knowledgeBaseSettings
// ═══════════════════════════════════════════════════════════════════════════════

export const knowledgeBaseSettingsAdapter = {
  async findUnique(args: { where: { id?: string; userId?: string } }) {
    if (!isConvex()) {
      return prisma.knowledgeBaseSettings.findUnique(args as any);
    }

    const userId = args.where.userId ?? args.where.id;
    if (!userId) return null;

    const doc = await convexQuery<ConvexDoc>(
      "voice_agents/queries:getKBSettings",
      { userId },
    );
    return doc ? convexToPrismaShape(doc, TIMESTAMP_FIELDS) : null;
  },

  async create(args: { data: Record<string, unknown> }) {
    if (!isConvex()) {
      return prisma.knowledgeBaseSettings.create(args as any);
    }

    const d = args.data;
    const convexId = await convexMutation<string>(
      "voice_agents/mutations:createKBSettings",
      {
        userId: d.userId as string,
        isEnabled: (d.isEnabled as boolean) ?? true,
        onboardingCompleted: (d.onboardingCompleted as boolean) ?? false,
        maxChunksPerQuery: (d.maxChunksPerQuery as number) ?? 5,
        similarityThreshold: (d.similarityThreshold as number) ?? 0.7,
        ...(d.welcomeMessage
          ? { welcomeMessage: d.welcomeMessage as string }
          : {}),
        ...(d.customInstructions
          ? { customInstructions: d.customInstructions as string }
          : {}),
      },
    );

    return {
      id: (d.id as string) || convexId || "",
      userId: d.userId as string,
      isEnabled: (d.isEnabled as boolean) ?? true,
      onboardingCompleted: (d.onboardingCompleted as boolean) ?? false,
      maxChunksPerQuery: (d.maxChunksPerQuery as number) ?? 5,
      similarityThreshold: (d.similarityThreshold as number) ?? 0.7,
      welcomeMessage: (d.welcomeMessage as string) ?? null,
      customInstructions: (d.customInstructions as string) ?? null,
      geminiFileSearchStoreId: null,
      geminiFileSearchStoreName: null,
      createdAt: new Date(),
      updatedAt: new Date(),
    };
  },

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

    const existing = await this.findUnique({
      where: { userId: args.where.userId },
    });
    if (existing) {
      return this.update({
        where: { userId: args.where.userId },
        data: args.update,
      });
    }
    return this.create({ data: args.create });
  },

  async update(args: {
    where: { id?: string; userId?: string };
    data: Record<string, unknown>;
  }) {
    if (!isConvex()) {
      // If where has userId, use findUnique first to get the id for update
      if (args.where.userId && !args.where.id) {
        const existing = await prisma.knowledgeBaseSettings.findUnique({
          where: { userId: args.where.userId },
        });
        if (!existing) return null;
        return prisma.knowledgeBaseSettings.update({
          where: { id: existing.id },
          data: args.data,
        } as any);
      }
      return prisma.knowledgeBaseSettings.update(args as any);
    }

    const userId = args.where.userId ?? args.where.id;
    if (!userId) return null;

    await convexMutation("voice_agents/mutations:updateKBSettings", {
      userId,
      ...args.data,
    });

    return this.findUnique({ where: { userId } });
  },
};

// ═══════════════════════════════════════════════════════════════════════════════
// 2. knowledgeBaseDocument
// ═══════════════════════════════════════════════════════════════════════════════

export const knowledgeBaseDocumentAdapter = {
  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.knowledgeBaseDocument.findMany(args as any);
    }

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

    const docs =
      (await convexQuery<ConvexDoc[]>("voice_agents/queries:getKBDocuments", {
        userId: userId || "",
        ...(category ? { category } : {}),
        ...(status ? { status } : {}),
      })) || [];

    // Filter soft-deleted if deletedAt: null is specified
    let filtered = docs;
    if (where.deletedAt === null) {
      filtered = docs.filter((d) => !d.deletedAt);
    }

    const paginated = applyPagination(filtered, args.skip, args.take);
    return convexToPrismaShapeMany(paginated, [
      ...TIMESTAMP_FIELDS,
      "processedAt",
      "deletedAt",
      "geminiUploadedAt",
    ]);
  },

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

    const where = (args.where || {}) as Record<string, unknown>;
    const userId = where.userId as string | undefined;
    const docs =
      (await convexQuery<ConvexDoc[]>("voice_agents/queries:getKBDocuments", {
        userId: userId || "",
      })) || [];

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

    return match
      ? convexToPrismaShape(match, [
          ...TIMESTAMP_FIELDS,
          "processedAt",
          "deletedAt",
          "geminiUploadedAt",
        ])
      : null;
  },

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

    const where = (args?.where || {}) as Record<string, unknown>;
    const userId = where.userId as string | undefined;
    const docs =
      (await convexQuery<ConvexDoc[]>("voice_agents/queries:getKBDocuments", {
        userId: userId || "",
        ...(where.status ? { status: where.status as string } : {}),
      })) || [];

    let filtered = docs;
    if (where.deletedAt === null) {
      filtered = docs.filter((d) => !d.deletedAt);
    }

    return filtered.length;
  },

  async create(args: { data: Record<string, unknown> }) {
    if (!isConvex()) {
      return prisma.knowledgeBaseDocument.create(args as any);
    }

    const d = args.data;
    const convexId = await convexMutation<string>(
      "voice_agents/mutations:createKBDocument",
      {
        userId: d.userId as string,
        filename: d.filename as string,
        originalName: d.originalName as string,
        mimeType: d.mimeType as string,
        fileSize: d.fileSize as number,
        storagePath: d.storagePath as string,
        ...(d.title ? { title: d.title as string } : {}),
        ...(d.description ? { description: d.description as string } : {}),
        category: (d.category as string) || "general",
      },
    );

    return {
      id: (d.id as string) || convexId || "",
      userId: d.userId as string,
      filename: d.filename as string,
      originalName: d.originalName as string,
      mimeType: d.mimeType as string,
      fileSize: d.fileSize as number,
      storagePath: d.storagePath as string,
      title: (d.title as string) ?? null,
      description: (d.description as string) ?? null,
      category: (d.category as string) || "general",
      status: (d.status as string) || "PENDING",
      processedAt: null,
      processingError: null,
      chunkCount: (d.chunkCount as number) ?? 0,
      embeddingModel: null,
      createdAt: new Date(),
      updatedAt: new Date(),
      deletedAt: null,
      geminiFileName: null,
      geminiUploadedAt: null,
    };
  },

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

    await convexMutation("voice_agents/mutations:updateKBDocument", {
      id: args.where.id,
      ...args.data,
    });

    return this.findFirst({ where: { id: args.where.id } });
  },

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

    const where = args.where;
    const userId = where.userId as string | undefined;
    const docs =
      (await convexQuery<ConvexDoc[]>("voice_agents/queries:getKBDocuments", {
        userId: userId || "",
      })) || [];

    const matching = docs.filter((doc) => {
      for (const [k, v] of Object.entries(where)) {
        if (k === "id") {
          if ((doc.prismaId || doc._id) !== v) return false;
        } else if (doc[k] !== v) {
          return false;
        }
      }
      return true;
    });

    for (const doc of matching) {
      await convexMutation("voice_agents/mutations:updateKBDocument", {
        id: doc._id,
        ...args.data,
      });
    }

    return { count: matching.length };
  },

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

    const where = args.where;
    const userId = where.userId as string | undefined;
    const docs =
      (await convexQuery<ConvexDoc[]>("voice_agents/queries:getKBDocuments", {
        userId: userId || "",
      })) || [];

    const matching = docs.filter((doc) => {
      for (const [k, v] of Object.entries(where)) {
        if (k === "id") {
          if ((doc.prismaId || doc._id) !== v) return false;
        } else if (doc[k] !== v) {
          return false;
        }
      }
      return true;
    });

    for (const doc of matching) {
      await convexMutation("voice_agents/mutations:deleteKBDocument", {
        id: doc._id,
      });
    }

    return { count: matching.length };
  },
};

// ═══════════════════════════════════════════════════════════════════════════════
// 3. knowledgeBaseFAQ
// ═══════════════════════════════════════════════════════════════════════════════

export const knowledgeBaseFAQAdapter = {
  async findMany(args: {
    where?: Record<string, unknown>;
    orderBy?: Record<string, string> | Array<Record<string, string>>;
    take?: number;
    skip?: number;
  }) {
    if (!isConvex()) {
      return prisma.knowledgeBaseFAQ.findMany(args as any);
    }

    const where = (args.where || {}) as Record<string, unknown>;
    const userId = where.userId as string | undefined;
    const docs =
      (await convexQuery<ConvexDoc[]>("voice_agents/queries:getKBFAQs", {
        userId: userId || "",
        ...(where.category ? { category: where.category as string } : {}),
        ...(where.isActive !== undefined
          ? { isActive: where.isActive as boolean }
          : {}),
      })) || [];

    // Handle OR filter (used in search)
    let filtered = docs;
    if (where.OR && Array.isArray(where.OR)) {
      filtered = docs.filter((doc) =>
        (where.OR as Array<Record<string, unknown>>).some((clause) =>
          Object.entries(clause).every(([field, condition]) => {
            if (typeof condition === "object" && condition !== null) {
              const cond = condition as { contains?: string; mode?: string };
              if (cond.contains) {
                const val = (doc[field] as string) || "";
                return cond.mode === "insensitive"
                  ? val.toLowerCase().includes(cond.contains.toLowerCase())
                  : val.includes(cond.contains);
              }
            }
            return doc[field] === condition;
          }),
        ),
      );
    }

    const paginated = applyPagination(filtered, args.skip, args.take);
    return convexToPrismaShapeMany(paginated, TIMESTAMP_FIELDS);
  },

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

    const where = (args.where || {}) as Record<string, unknown>;
    const userId = where.userId as string | undefined;
    const docs =
      (await convexQuery<ConvexDoc[]>("voice_agents/queries:getKBFAQs", {
        userId: userId || "",
      })) || [];

    const match = docs.find((doc) => {
      for (const [k, v] of Object.entries(where)) {
        if (k === "OR") continue; // Skip complex filters for findFirst
        const docVal = k === "id" ? doc.prismaId || doc._id : doc[k];
        if (docVal !== v) return false;
      }
      return true;
    });

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

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

    const where = (args?.where || {}) as Record<string, unknown>;
    const userId = where.userId as string | undefined;
    const docs =
      (await convexQuery<ConvexDoc[]>("voice_agents/queries:getKBFAQs", {
        userId: userId || "",
        ...(where.isActive !== undefined
          ? { isActive: where.isActive as boolean }
          : {}),
      })) || [];

    return docs.length;
  },

  async create(args: { data: Record<string, unknown> }) {
    if (!isConvex()) {
      return prisma.knowledgeBaseFAQ.create(args as any);
    }

    const d = args.data;
    const convexId = await convexMutation<string>(
      "voice_agents/mutations:createKBFAQ",
      {
        userId: d.userId as string,
        questionEn: d.questionEn as string,
        answerEn: d.answerEn as string,
        ...(d.questionAr ? { questionAr: d.questionAr as string } : {}),
        ...(d.answerAr ? { answerAr: d.answerAr as string } : {}),
        category: (d.category as string) || "general",
        priority: (d.priority as number) ?? 0,
        isActive: (d.isActive as boolean) ?? true,
      },
    );

    return {
      id: (d.id as string) || convexId || "",
      userId: d.userId as string,
      questionEn: d.questionEn as string,
      questionAr: (d.questionAr as string) ?? null,
      answerEn: d.answerEn as string,
      answerAr: (d.answerAr as string) ?? null,
      category: (d.category as string) || "general",
      priority: (d.priority as number) ?? 0,
      isActive: (d.isActive as boolean) ?? true,
      embedding: null,
      createdAt: new Date(),
      updatedAt: new Date(),
    };
  },

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

    const where = args.where;
    const userId = where.userId as string | undefined;
    const docs =
      (await convexQuery<ConvexDoc[]>("voice_agents/queries:getKBFAQs", {
        userId: userId || "",
      })) || [];

    const matching = docs.filter((doc) => {
      for (const [k, v] of Object.entries(where)) {
        if (k === "id") {
          if ((doc.prismaId || doc._id) !== v) return false;
        } else if (doc[k] !== v) {
          return false;
        }
      }
      return true;
    });

    for (const doc of matching) {
      await convexMutation("voice_agents/mutations:updateKBFAQ", {
        id: doc._id,
        ...args.data,
      });
    }

    return { count: matching.length };
  },

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

    const where = args.where;
    const userId = where.userId as string | undefined;
    const docs =
      (await convexQuery<ConvexDoc[]>("voice_agents/queries:getKBFAQs", {
        userId: userId || "",
      })) || [];

    const matching = docs.filter((doc) => {
      for (const [k, v] of Object.entries(where)) {
        if (k === "id") {
          if ((doc.prismaId || doc._id) !== v) return false;
        } else if (doc[k] !== v) {
          return false;
        }
      }
      return true;
    });

    for (const doc of matching) {
      await convexMutation("voice_agents/mutations:deleteKBFAQ", {
        id: doc._id,
      });
    }

    return { count: matching.length };
  },
};

// ═══════════════════════════════════════════════════════════════════════════════
// 4. knowledgeBaseChunk
// ═══════════════════════════════════════════════════════════════════════════════

export const knowledgeBaseChunkAdapter = {
  async findMany(args: {
    where?: Record<string, unknown>;
    include?: Record<string, unknown>;
    take?: number;
    skip?: number;
  }) {
    if (!isConvex()) {
      return prisma.knowledgeBaseChunk.findMany(args as any);
    }

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

    // Handle nested KnowledgeBaseDocument filter
    const nestedDoc = where.KnowledgeBaseDocument as
      | { userId?: string; status?: string }
      | undefined;

    const docs =
      (await convexQuery<ConvexDoc[]>("voice_agents/queries:getKBChunks", {
        ...(documentId ? { documentId } : {}),
        ...(nestedDoc?.userId ? { userId: nestedDoc.userId } : {}),
      })) || [];

    // Filter by content search if specified
    let filtered = docs;
    if (where.content && typeof where.content === "object") {
      const contentFilter = where.content as {
        contains?: string;
        mode?: string;
      };
      if (contentFilter.contains) {
        const searchTerm =
          contentFilter.mode === "insensitive"
            ? contentFilter.contains.toLowerCase()
            : contentFilter.contains;
        filtered = docs.filter((d) => {
          const content = (d.content as string) || "";
          return contentFilter.mode === "insensitive"
            ? content.toLowerCase().includes(searchTerm)
            : content.includes(searchTerm);
        });
      }
    }

    const paginated = applyPagination(filtered, args.skip, args.take);
    return convexToPrismaShapeMany(paginated, ["createdAt"]);
  },

  async createMany(args: { data: Array<Record<string, unknown>> }) {
    if (!isConvex()) {
      return prisma.knowledgeBaseChunk.createMany(args as any);
    }

    let created = 0;
    for (const chunk of args.data) {
      await convexMutation("voice_agents/mutations:createKBChunk", {
        documentId: chunk.documentId as string,
        content: chunk.content as string,
        chunkIndex: chunk.chunkIndex as number,
        ...(chunk.embedding !== undefined
          ? { embedding: chunk.embedding }
          : {}),
        ...(chunk.metadata !== undefined ? { metadata: chunk.metadata } : {}),
      });
      created++;
    }

    return { count: created };
  },
};

// ═══════════════════════════════════════════════════════════════════════════════
// 5. voiceAgentPhoneConfig
// ═══════════════════════════════════════════════════════════════════════════════

export const voiceAgentPhoneConfigAdapter = {
  async findUnique(args: { where: { id?: string; userId?: string } }) {
    if (!isConvex()) {
      return prisma.voiceAgentPhoneConfig.findUnique(args as any);
    }

    const userId = args.where.userId ?? args.where.id;
    if (!userId) return null;

    const doc = await convexQuery<ConvexDoc>(
      "voice_agents/queries:getPhoneConfig",
      { userId },
    );
    return doc ? convexToPrismaShape(doc, TIMESTAMP_FIELDS) : null;
  },

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

    const existing = await this.findUnique({
      where: { userId: args.where.userId },
    });
    if (existing) {
      await convexMutation("voice_agents/mutations:updatePhoneConfig", {
        id:
          (existing as Record<string, unknown>)._convexId ||
          (existing as Record<string, unknown>).id,
        ...args.update,
      });
      return this.findUnique({ where: { userId: args.where.userId } });
    }
    return this.create({ data: args.create });
  },

  async create(args: { data: Record<string, unknown> }) {
    if (!isConvex()) {
      return prisma.voiceAgentPhoneConfig.create(args as any);
    }

    const d = args.data;
    const convexId = await convexMutation<string>(
      "voice_agents/mutations:createPhoneConfig",
      {
        userId: d.userId as string,
        connectionType: (d.connectionType as string) || "custom_sip",
        isActive: (d.isActive as boolean) ?? false,
        ...(d.phoneNumber ? { phoneNumber: d.phoneNumber as string } : {}),
        ...(d.elevenlabsPhoneId
          ? { elevenlabsPhoneId: d.elevenlabsPhoneId as string }
          : {}),
        ...(d.twilioPhoneNumber
          ? { twilioPhoneNumber: d.twilioPhoneNumber as string }
          : {}),
        ...(d.twilioAccountSid
          ? { twilioAccountSid: d.twilioAccountSid as string }
          : {}),
        ...(d.twilioAuthCredential
          ? { twilioAuthToken: d.twilioAuthCredential as string }
          : {}),
        ...(d.sipUri ? { sipUri: d.sipUri as string } : {}),
        ...(d.sipUsername ? { sipUsername: d.sipUsername as string } : {}),
        ...(d.sipCredential ? { sipPassword: d.sipCredential as string } : {}),
        ...(d.sipRegistrar ? { sipRegistrar: d.sipRegistrar as string } : {}),
        ...(d.sipTransport ? { sipTransport: d.sipTransport as string } : {}),
        ...(d.metadata !== undefined ? { metadata: d.metadata } : {}),
      },
    );

    return {
      id: convexId || "",
      userId: d.userId as string,
      connectionType: (d.connectionType as string) || "custom_sip",
      phoneNumber: (d.phoneNumber as string) ?? null,
      isActive: (d.isActive as boolean) ?? false,
      elevenlabsPhoneId: (d.elevenlabsPhoneId as string) ?? null,
      twilioPhoneNumber: (d.twilioPhoneNumber as string) ?? null,
      twilioAccountSid: (d.twilioAccountSid as string) ?? null,
      sipUri: (d.sipUri as string) ?? null,
      sipUsername: (d.sipUsername as string) ?? null,
      sipRegistrar: (d.sipRegistrar as string) ?? null,
      sipTransport: (d.sipTransport as string) ?? "udp",
      metadata: d.metadata ?? null,
      createdAt: new Date(),
      updatedAt: new Date(),
    };
  },

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

    const userId = args.where.userId as string | undefined;
    if (!userId) return { count: 0 };

    const existing = await this.findUnique({ where: { userId } });
    if (!existing) return { count: 0 };

    await convexMutation("voice_agents/mutations:deletePhoneConfig", {
      id: (existing as Record<string, unknown>).id,
    });

    return { count: 1 };
  },
};

// ═══════════════════════════════════════════════════════════════════════════════
// 6. voiceAgentKBSync
// ═══════════════════════════════════════════════════════════════════════════════

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

    const where = (args.where || {}) as Record<string, unknown>;
    const userId = where.userId as string | undefined;
    const docs =
      (await convexQuery<ConvexDoc[]>("voice_agents/queries:getKBSyncRecords", {
        userId: userId || "",
      })) || [];

    let filtered = docs;
    if (where.syncStatus) {
      filtered = docs.filter((d) => d.syncStatus === where.syncStatus);
    }
    if (where.documentId) {
      filtered = filtered.filter((d) => d.documentId === where.documentId);
    }

    const paginated = applyPagination(filtered, args.skip, args.take);
    return convexToPrismaShapeMany(paginated, [
      ...TIMESTAMP_FIELDS,
      "lastSyncAt",
    ]);
  },

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

    const results = await this.findMany({ where: args.where, take: 1 });
    return (results as unknown[])[0] ?? null;
  },

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

    if (args.where.userId_documentId) {
      return this.findFirst({
        where: {
          userId: args.where.userId_documentId.userId,
          documentId: args.where.userId_documentId.documentId,
        },
      });
    }

    return this.findFirst({ where: { id: args.where.id } });
  },

  async create(args: { data: Record<string, unknown> }) {
    if (!isConvex()) {
      return prisma.voiceAgentKBSync.create(args as any);
    }

    const d = args.data;
    const convexId = await convexMutation<string>(
      "voice_agents/mutations:createKBSync",
      {
        userId: d.userId as string,
        documentId: d.documentId as string,
        syncStatus: (d.syncStatus as string) || "pending",
        ...(d.elevenlabsDocumentId
          ? { elevenlabsDocumentId: d.elevenlabsDocumentId as string }
          : {}),
      },
    );

    return {
      id: convexId || "",
      userId: d.userId as string,
      documentId: d.documentId as string,
      elevenlabsDocumentId: (d.elevenlabsDocumentId as string) ?? null,
      syncStatus: (d.syncStatus as string) || "pending",
      lastSyncAt: null,
      syncError: null,
      createdAt: new Date(),
      updatedAt: new Date(),
    };
  },

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

    await convexMutation("voice_agents/mutations:updateKBSync", {
      id: args.where.id,
      ...args.data,
    });

    return this.findFirst({ where: { id: args.where.id } });
  },

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

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

// ═══════════════════════════════════════════════════════════════════════════════
// 7. voiceAgentSettings
// ═══════════════════════════════════════════════════════════════════════════════

export const voiceAgentSettingsAdapter = {
  async findUnique(args: { where: { id?: string; userId?: string } }) {
    if (!isConvex()) {
      return prisma.voiceAgentSettings.findUnique(args as any);
    }

    const userId = args.where.userId ?? args.where.id;
    if (!userId) return null;

    const doc = await convexQuery<ConvexDoc>(
      "voice_agents/queries:getVoiceAgentSettings",
      { userId },
    );
    return doc ? convexToPrismaShape(doc, TIMESTAMP_FIELDS) : null;
  },

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

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

  async create(args: { data: Record<string, unknown> }) {
    if (!isConvex()) {
      return prisma.voiceAgentSettings.create(args as any);
    }

    const d = args.data;
    const convexId = await convexMutation<string>(
      "voice_agents/mutations:createVoiceAgentSettings",
      {
        userId: d.userId as string,
        domainAllowlist: (d.domainAllowlist as string[]) || [],
        signedUrlEnabled: (d.signedUrlEnabled as boolean) ?? false,
        ...(d.successEvaluationCriteria !== undefined
          ? { successEvaluationCriteria: d.successEvaluationCriteria }
          : {}),
        ...(d.dataCollectionSchema !== undefined
          ? { dataCollectionSchema: d.dataCollectionSchema }
          : {}),
        ...(d.postCallWebhookUrl
          ? { postCallWebhookUrl: d.postCallWebhookUrl as string }
          : {}),
        ...(d.pronunciationDictId
          ? { pronunciationDictId: d.pronunciationDictId as string }
          : {}),
        ...(d.pronunciationRules !== undefined
          ? { pronunciationRules: d.pronunciationRules }
          : {}),
        ...(d.activeWorkflowId
          ? { activeWorkflowId: d.activeWorkflowId as string }
          : {}),
        ...(d.workflowTemplate
          ? { workflowTemplate: d.workflowTemplate as string }
          : {}),
        ...(d.workflowCustomization !== undefined
          ? { workflowCustomization: d.workflowCustomization }
          : {}),
      },
    );

    return {
      id: convexId || "",
      userId: d.userId as string,
      successEvaluationCriteria: d.successEvaluationCriteria ?? null,
      dataCollectionSchema: d.dataCollectionSchema ?? null,
      postCallWebhookUrl: (d.postCallWebhookUrl as string) ?? null,
      pronunciationDictId: (d.pronunciationDictId as string) ?? null,
      pronunciationRules: d.pronunciationRules ?? null,
      domainAllowlist: (d.domainAllowlist as string[]) || [],
      signedUrlEnabled: (d.signedUrlEnabled as boolean) ?? false,
      activeWorkflowId: (d.activeWorkflowId as string) ?? null,
      workflowTemplate: (d.workflowTemplate as string) ?? null,
      workflowCustomization: d.workflowCustomization ?? null,
      createdAt: new Date(),
      updatedAt: new Date(),
    };
  },

  async update(args: {
    where: { id?: string; userId?: string };
    data: Record<string, unknown>;
  }) {
    if (!isConvex()) {
      if (args.where.userId && !args.where.id) {
        const existing = await prisma.voiceAgentSettings.findUnique({
          where: { userId: args.where.userId },
        });
        if (!existing) return null;
        return prisma.voiceAgentSettings.update({
          where: { id: existing.id },
          data: args.data,
        } as any);
      }
      return prisma.voiceAgentSettings.update(args as any);
    }

    const userId = args.where.userId ?? args.where.id;
    if (!userId) return null;

    // Get existing to find Convex _id
    const existing = await this.findUnique({ where: { userId } });
    if (!existing) return null;

    await convexMutation("voice_agents/mutations:updateSettings", {
      id: (existing as Record<string, unknown>).id,
      ...args.data,
    });

    return this.findUnique({ where: { userId } });
  },
};

// ═══════════════════════════════════════════════════════════════════════════════
// 8. conversationLog
// ═══════════════════════════════════════════════════════════════════════════════

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

    const where = (args.where || {}) as Record<string, unknown>;
    const userId = where.userId as string | undefined;
    const platform = where.platform as string | undefined;
    const status = where.status as string | undefined;
    const limit = (args.take || 50) + (args.skip || 0);

    const docs =
      (await convexQuery<ConvexDoc[]>(
        "voice_agents/queries:getConversationLogs",
        {
          userId: userId || "",
          ...(platform ? { platform } : {}),
          ...(status ? { status } : {}),
          limit,
        },
      )) || [];

    const paginated = applyPagination(docs, args.skip, args.take);
    return convexToPrismaShapeMany(paginated, [
      ...TIMESTAMP_FIELDS,
      "lastMessageAt",
    ]);
  },

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

    const where = (args.where || {}) as Record<string, unknown>;
    const userId = where.userId as string | undefined;
    const docs =
      (await convexQuery<ConvexDoc[]>(
        "voice_agents/queries:getConversationLogs",
        { userId: userId || "", limit: 200 },
      )) || [];

    const match = docs.find((doc) => {
      for (const [k, v] of Object.entries(where)) {
        if (k === "id") {
          if ((doc.prismaId || doc._id) !== v) return false;
        } else if (doc[k] !== v) {
          return false;
        }
      }
      return true;
    });

    return match
      ? convexToPrismaShape(match, [...TIMESTAMP_FIELDS, "lastMessageAt"])
      : null;
  },

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

    const where = (args?.where || {}) as Record<string, unknown>;
    const userId = where.userId as string | undefined;
    const docs =
      (await convexQuery<ConvexDoc[]>(
        "voice_agents/queries:getConversationLogs",
        { userId: userId || "", limit: 10000 },
      )) || [];

    return docs.length;
  },

  async create(args: { data: Record<string, unknown> }) {
    if (!isConvex()) {
      return prisma.conversationLog.create(args as any);
    }

    const d = args.data;
    const convexId = await convexMutation<string>(
      "voice_agents/mutations:createConversationLog",
      {
        userId: d.userId as string,
        platform: d.platform as string,
        ...(d.customerPhone
          ? { customerPhone: d.customerPhone as string }
          : {}),
        ...(d.customerName ? { customerName: d.customerName as string } : {}),
        ...(d.metadata !== undefined ? { metadata: d.metadata } : {}),
      },
    );

    return {
      id: (d.id as string) || convexId || "",
      userId: d.userId as string,
      platform: d.platform as string,
      customerPhone: (d.customerPhone as string) ?? null,
      customerName: (d.customerName as string) ?? null,
      messages: d.messages ?? [],
      metadata: d.metadata ?? null,
      status: (d.status as string) || "active",
      lastMessageAt: null,
      lastMessagePreview: null,
      unreadCount: 0,
      messageCount: 0,
      createdAt: new Date(),
      updatedAt: new Date(),
    };
  },

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

    await convexMutation("voice_agents/mutations:updateConversationLog", {
      id: args.where.id,
      ...args.data,
    });

    return this.findFirst({ where: { id: args.where.id } });
  },
};

// ═══════════════════════════════════════════════════════════════════════════════
// 9. conversationLogMessage
// ═══════════════════════════════════════════════════════════════════════════════

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

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

    const docs =
      (await convexQuery<ConvexDoc[]>(
        "voice_agents/queries:getConversationMessages",
        {
          conversationId: conversationId || "",
          limit: (args.take || 100) + (args.skip || 0),
        },
      )) || [];

    const paginated = applyPagination(docs, args.skip, args.take);
    return convexToPrismaShapeMany(paginated, ["createdAt", "timestamp"]);
  },

  async create(args: { data: Record<string, unknown> }) {
    if (!isConvex()) {
      return prisma.conversationLogMessage.create(args as any);
    }

    const d = args.data;
    const convexId = await convexMutation<string>(
      "voice_agents/mutations:addMessage",
      {
        conversationId: d.conversationId as string,
        sender: d.sender as string,
        content: d.content as string,
        ...(d.metadata !== undefined ? { metadata: d.metadata } : {}),
      },
    );

    return {
      id: (d.id as string) || convexId || "",
      conversationId: d.conversationId as string,
      sender: d.sender as string,
      content: d.content as string,
      timestamp: d.timestamp ?? new Date(),
      metadata: d.metadata ?? null,
      createdAt: new Date(),
    };
  },
};

// ═══════════════════════════════════════════════════════════════════════════════
// 10. voiceConversationAction
// ═══════════════════════════════════════════════════════════════════════════════

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

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

    const docs =
      (await convexQuery<ConvexDoc[]>(
        "voice_agents/queries:getConversationActions",
        {
          ...(conversationId ? { conversationId } : {}),
          ...(status ? { status } : {}),
          ...(assignedTo ? { assignedTo } : {}),
        },
      )) || [];

    const paginated = applyPagination(docs, args.skip, args.take);
    return convexToPrismaShapeMany(paginated, [
      ...TIMESTAMP_FIELDS,
      "dueAt",
      "completedAt",
    ]);
  },

  async create(args: { data: Record<string, unknown> }) {
    if (!isConvex()) {
      return prisma.voiceConversationAction.create(args as any);
    }

    const d = args.data;
    const convexId = await convexMutation<string>(
      "voice_agents/mutations:createConversationAction",
      {
        conversationId: d.conversationId as string,
        type: d.type as string,
        title: d.title as string,
        ...(d.status ? { status: d.status as string } : {}),
        ...(d.description ? { description: d.description as string } : {}),
        ...(d.assignedTo ? { assignedTo: d.assignedTo as string } : {}),
        ...(d.metadata !== undefined ? { metadata: d.metadata } : {}),
      },
    );

    return {
      id: convexId || "",
      conversationId: d.conversationId as string,
      type: d.type as string,
      status: (d.status as string) || "PENDING",
      title: d.title as string,
      description: (d.description as string) ?? null,
      dueAt: d.dueAt ?? null,
      assignedTo: (d.assignedTo as string) ?? null,
      completedAt: null,
      completedBy: null,
      metadata: d.metadata ?? null,
      createdAt: new Date(),
      updatedAt: new Date(),
    };
  },

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

    await convexMutation("voice_agents/mutations:updateConversationAction", {
      id: args.where.id,
      ...args.data,
    });

    const docs =
      (await convexQuery<ConvexDoc[]>(
        "voice_agents/queries:getConversationActions",
        {},
      )) || [];

    const match = docs.find(
      (d) => d._id === args.where.id || d.prismaId === args.where.id,
    );
    return match
      ? convexToPrismaShape(match, [
          ...TIMESTAMP_FIELDS,
          "dueAt",
          "completedAt",
        ])
      : null;
  },
};

// ─── Namespace export ────────────────────────────────────────────────────────

export const voiceAgentsAdapter = {
  knowledgeBaseSettings: knowledgeBaseSettingsAdapter,
  knowledgeBaseDocument: knowledgeBaseDocumentAdapter,
  knowledgeBaseFAQ: knowledgeBaseFAQAdapter,
  knowledgeBaseChunk: knowledgeBaseChunkAdapter,
  voiceAgentPhoneConfig: voiceAgentPhoneConfigAdapter,
  voiceAgentKBSync: voiceAgentKBSyncAdapter,
  voiceAgentSettings: voiceAgentSettingsAdapter,
  conversationLog: conversationLogAdapter,
  conversationLogMessage: conversationLogMessageAdapter,
  voiceConversationAction: voiceConversationActionAdapter,
};
