/**
 * Referrals Domain Adapter
 *
 * Routes referral operations to Convex. The referrals table exists
 * in the Convex schema (convex/schema/reviews.ts) alongside reviews.
 *
 * Referrals share the "reviews" data-source domain since they are
 * defined in the same Convex schema module.
 */

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

const DOMAIN = "reviews" as const; // referrals live in the reviews schema module
const REFERRAL_TS_FIELDS = [
  "createdAt",
  "updatedAt",
  "expiresAt",
  "lastConvertedAt",
  "lastRewardAt",
];

function requireConvex(): void {
  if (getDataSource(DOMAIN) !== "convex") {
    throw new Error(
      "Referrals domain requires DS_REVIEWS=convex (no Prisma model exists)",
    );
  }
}

export const referralsAdapter = {
  async create(args: { data: Record<string, unknown> }) {
    requireConvex();
    const d = args.data;
    const convexId = await convexMutation<string>(
      "reviews/referral_mutations:createReferral",
      {
        userId: d.userId as string,
        code: d.code as string,
        name: d.name as string,
        description: (d.description as string | undefined) ?? undefined,
        rewardType: d.rewardType as string,
        rewardValue: d.rewardValue as number,
        rewardCurrency: d.rewardCurrency as string,
        maxUses: (d.maxUses as number | undefined) ?? undefined,
        expiresAt: dateToConvexTimestamp(
          d.expiresAt as Date | string | null | undefined,
        ),
        uses: (d.uses as number) ?? 0,
        conversions: (d.conversions as number) ?? 0,
        rewardsIssued: (d.rewardsIssued as number) ?? 0,
        status: (d.status as string) ?? "active",
      },
    );
    return {
      ...d,
      id: (d.id as string) || convexId || "",
      createdAt: d.createdAt instanceof Date ? d.createdAt : new Date(),
      updatedAt: d.updatedAt instanceof Date ? d.updatedAt : new Date(),
    };
  },

  async findMany(args: {
    where?: Record<string, unknown>;
    orderBy?: Record<string, string>;
    skip?: number;
    take?: number;
    select?: Record<string, boolean>;
  }) {
    requireConvex();
    const filters = args.where || {};
    const docs =
      (await convexQuery<ConvexDoc[]>("reviews/referral_queries:getReferrals", {
        userId: filters.userId as string,
        status: filters.status as string | undefined,
        limit: (args.skip ?? 0) + (args.take ?? 50) + 10,
      })) || [];
    const shaped = convexToPrismaShapeMany<Record<string, unknown>>(
      docs,
      REFERRAL_TS_FIELDS,
    );

    // Client-side search filtering (Convex doesn't support LIKE/contains)
    let filtered = shaped;
    if (filters.OR && Array.isArray(filters.OR)) {
      const searchTerms = filters.OR as Array<Record<string, unknown>>;
      filtered = shaped.filter((item) =>
        searchTerms.some((term) => {
          for (const [field, condition] of Object.entries(term)) {
            if (
              condition &&
              typeof condition === "object" &&
              "contains" in (condition as Record<string, unknown>)
            ) {
              const search = (
                (condition as Record<string, unknown>).contains as string
              ).toLowerCase();
              const value = item[field];
              if (
                typeof value === "string" &&
                value.toLowerCase().includes(search)
              ) {
                return true;
              }
            }
          }
          return false;
        }),
      );
    }

    // Client-side sorting
    if (args.orderBy) {
      const [sortField, sortOrder] = Object.entries(args.orderBy)[0];
      filtered.sort((a, b) => {
        const aVal = a[sortField];
        const bVal = b[sortField];
        if (aVal == null && bVal == null) return 0;
        if (aVal == null) return 1;
        if (bVal == null) return -1;
        const cmp = aVal < bVal ? -1 : aVal > bVal ? 1 : 0;
        return sortOrder === "asc" ? cmp : -cmp;
      });
    }

    return applyPagination(filtered, args.skip, args.take);
  },

  async findFirst(args: { where?: Record<string, unknown> }) {
    requireConvex();
    const filters = args.where || {};

    // If searching by code, use the dedicated query
    if (filters.code) {
      const doc = await convexQuery<ConvexDoc | null>(
        "reviews/referral_queries:getReferralByCode",
        {
          code: filters.code as string,
          status: filters.status as string | undefined,
        },
      );
      if (!doc) return null;
      return convexToPrismaShape<Record<string, unknown>>(
        doc,
        REFERRAL_TS_FIELDS,
      );
    }

    // Otherwise fall back to findMany with take=1
    const docs = await this.findMany({ where: args.where, take: 1 });
    return docs[0] || null;
  },

  async findUnique(args: { where: Record<string, unknown> }) {
    requireConvex();
    const filters = args.where;

    if (filters.id) {
      const doc = await convexQuery<ConvexDoc | null>(
        "reviews/referral_queries:getReferralById",
        { id: filters.id as string },
      );
      if (!doc) return null;
      return convexToPrismaShape<Record<string, unknown>>(
        doc,
        REFERRAL_TS_FIELDS,
      );
    }

    // Fallback to findFirst
    return this.findFirst(args);
  },

  async update(args: {
    where: Record<string, unknown>;
    data: Record<string, unknown>;
  }) {
    requireConvex();
    const d = args.data;

    // Handle Prisma-style increment operations
    const patch: Record<string, unknown> = {};
    for (const [key, value] of Object.entries(d)) {
      if (
        value &&
        typeof value === "object" &&
        "increment" in (value as Record<string, unknown>)
      ) {
        patch[`${key}_increment`] = (
          value as Record<string, unknown>
        ).increment;
      } else if (
        key === "expiresAt" ||
        key === "lastConvertedAt" ||
        key === "lastRewardAt"
      ) {
        patch[key] = dateToConvexTimestamp(
          value as Date | string | null | undefined,
        );
      } else {
        patch[key] = value;
      }
    }

    const convexId = await convexMutation<string>(
      "reviews/referral_mutations:updateReferral",
      {
        id: args.where.id as string,
        ...patch,
      },
    );
    return { id: convexId || args.where.id, ...d };
  },

  async delete(args: { where: Record<string, unknown> }) {
    requireConvex();
    await convexMutation<boolean>("reviews/referral_mutations:deleteReferral", {
      id: args.where.id as string,
    });
    return { id: args.where.id };
  },

  async count(args?: { where?: Record<string, unknown> }) {
    requireConvex();
    const docs = await this.findMany({ where: args?.where });
    return docs.length;
  },
};
