/**
 * Customers Domain Adapter
 *
 * Routes customer CRUD to Prisma or Convex based on data-source config.
 * Maintains Prisma-compatible method signatures so callers need minimal changes.
 *
 * Prisma model fields: id, userId, organizationId?, name, phone, email?, notes?,
 *                      source?, createdAt, updatedAt
 * Convex indexes: by_userId, by_userId_phone, by_userId_name, by_phone,
 *                 by_createdAt, by_organizationId, by_organizationId_phone,
 *                 by_organizationId_name
 *
 * Usage sites:
 *   - app/api/user/customers/route.ts (findMany, count, create, findUnique)
 *   - app/api/user/customers/[id]/route.ts (findFirst, findUnique, update, delete)
 *   - app/api/analytics/metrics/route.ts (count)
 *   - app/api/bookings/route.ts (findUnique, create)
 *   - app/api/webhooks/n8n/lead/route.ts (upsert)
 *   - app/api/webhooks/n8n/booking/route.ts (upsert)
 *   - lib/services/review-auto-trigger.service.ts (findFirst)
 *   - lib/services/whatsapp-ai-engine.service.ts (findFirst, create)
 *   - lib/actions/google-contacts.actions.ts (findUnique, create)
 */

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";

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

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

// ─── findMany ────────────────────────────────────────────────────────────

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

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

  if (!userId) {
    // Convex requires userId for efficient querying
    return [];
  }

  // Use Convex getCustomers query with pagination
  const limit = (args.take || 100) + (args.skip || 0);
  const result = await convexQuery<{ customers: ConvexDoc[]; total: number }>(
    "customers/queries:getCustomers",
    { userId, limit, page: 1 },
  );

  const docs = result?.customers || [];
  const paginated = applyPagination(docs, args.skip, args.take);
  return convexToPrismaShapeMany(paginated, TIMESTAMP_FIELDS);
}

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

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

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

  if (!userId) return null;

  // Fetch user's customers and filter locally
  const result = await convexQuery<{ customers: ConvexDoc[]; total: number }>(
    "customers/queries:getCustomers",
    { userId, limit: 10000, page: 1 },
  );

  const docs = result?.customers || [];

  // Match by all where conditions
  const match = docs.find((doc) => {
    for (const [key, value] of Object.entries(where)) {
      if (key === "id") {
        const docId = (doc.prismaId as string) || doc._id;
        if (docId !== value) return false;
      } else {
        if (doc[key] !== value) return false;
      }
    }
    return true;
  });

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

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

export async function findUnique(args: {
  where: { id?: string; userId_phone?: { userId: string; phone: string } };
  select?: Record<string, boolean>;
}) {
  if (!isConvex()) {
    return prisma.customers.findUnique(args as any);
  }

  // Unique by id
  if (args.where.id) {
    return findFirst({ where: { id: args.where.id } as any });
  }

  // Unique by userId + phone composite
  if (args.where.userId_phone) {
    const { userId, phone } = args.where.userId_phone;
    return findFirst({
      where: { userId, phone },
    });
  }

  return null;
}

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

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

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

  if (!userId) return 0;

  const result = await convexQuery<{ total: number }>(
    "customers/queries:getCustomers",
    { userId, limit: 1, page: 1 },
  );

  return result?.total ?? 0;
}

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

export async function create(args: {
  data: {
    id?: string;
    userId: string;
    organizationId?: string | null;
    name: string;
    phone: string;
    email?: string | null;
    notes?: string | null;
    source?: string | null;
    createdAt?: Date;
    updatedAt?: Date;
  };
}) {
  if (!isConvex()) {
    return prisma.customers.create(args as any);
  }

  const d = args.data;
  const convexId = await convexMutation<string>(
    "customers/mutations:createCustomer",
    {
      userId: d.userId,
      name: d.name,
      phone: d.phone,
      ...(d.organizationId ? { organizationId: d.organizationId } : {}),
      ...(d.email ? { email: d.email } : {}),
      ...(d.notes ? { notes: d.notes } : {}),
      ...(d.source ? { source: d.source } : {}),
    },
  );

  // Return Prisma-compatible shape
  return {
    id: d.id || convexId || "",
    userId: d.userId,
    organizationId: d.organizationId ?? null,
    name: d.name,
    phone: d.phone,
    email: d.email ?? null,
    notes: d.notes ?? null,
    source: d.source ?? null,
    createdAt: d.createdAt ?? new Date(),
    updatedAt: d.updatedAt ?? new Date(),
  };
}

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

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

  // We need the userId to pass to the Convex mutation for ownership check.
  // First, find the existing record to get userId.
  const existing = await findFirst({ where: { id: args.where.id } as any });
  if (!existing) return null;

  const userId = (existing as Record<string, unknown>).userId as string;

  await convexMutation("customers/mutations:updateCustomer", {
    id: args.where.id,
    userId,
    ...(args.data.name !== undefined ? { name: args.data.name } : {}),
    ...(args.data.phone !== undefined ? { phone: args.data.phone } : {}),
    ...(args.data.email !== undefined ? { email: args.data.email } : {}),
    ...(args.data.notes !== undefined ? { notes: args.data.notes } : {}),
    ...(args.data.source !== undefined ? { source: args.data.source } : {}),
  });

  // Return merged shape (best-effort since Convex mutation returns the doc)
  return { ...existing, ...args.data, updatedAt: new Date() };
}

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

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

  // We need the userId for ownership verification
  const existing = await findFirst({ where: { id: args.where.id } as any });
  if (!existing) return { id: args.where.id };

  const userId = (existing as Record<string, unknown>).userId as string;

  await convexMutation("customers/mutations:deleteCustomer", {
    id: args.where.id,
    userId,
  });

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

export { deleteOne as delete };

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

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

  const where = args.where;
  const userId = where.userId as string;

  if (!userId) return { count: 0 };

  // Fetch all matching customers
  const result = await convexQuery<{ customers: ConvexDoc[]; total: number }>(
    "customers/queries:getCustomers",
    { userId, limit: 10000, page: 1 },
  );

  let matching = result?.customers || [];

  // Apply additional filters
  const inIds = (where.id as { in?: string[] })?.in;
  if (inIds) {
    const idSet = new Set(inIds);
    matching = matching.filter((d) =>
      idSet.has((d.prismaId as string) || d._id),
    );
  }

  // Delete each
  let deletedCount = 0;
  for (const doc of matching) {
    await convexMutation("customers/mutations:deleteCustomer", {
      id: doc._id,
      userId,
    });
    deletedCount++;
  }

  return { count: deletedCount };
}

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

export async function upsert(args: {
  where: { userId_phone: { userId: string; phone: string } };
  create: {
    userId: string;
    name: string;
    phone: string;
    email?: string;
    source?: string;
    [key: string]: unknown;
  };
  update: Record<string, unknown>;
}) {
  if (!isConvex()) {
    return prisma.customers.upsert(args as any);
  }

  const { userId, phone } = args.where.userId_phone;

  // Check if customer exists
  const existing = await findFirst({ where: { userId, phone } });

  if (existing) {
    // Update the existing record
    const existingRecord = existing as Record<string, unknown>;
    return update({
      where: { id: existingRecord.id as string },
      data: args.update,
    });
  }

  // Create new
  return create({
    data: {
      userId: args.create.userId,
      name: args.create.name,
      phone: args.create.phone,
      email: (args.create.email as string) || undefined,
      source: (args.create.source as string) || undefined,
    },
  });
}

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

export const customersAdapter = {
  findMany,
  findFirst,
  findUnique,
  count,
  create,
  update,
  delete: deleteOne,
  deleteMany,
  upsert,
};
