/**
 * Reviews Domain Adapter
 *
 * Routes reviews, review_requests, and review_widget_settings
 * to Convex. These models exist ONLY in Convex (no Prisma models).
 *
 * When DS_REVIEWS=prisma (default), operations throw since there
 * are no Prisma tables. When DS_REVIEWS=convex, uses Convex backend.
 */

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 = "reviews" as const;
const REVIEW_TS_FIELDS = ["createdAt", "updatedAt"];
const REQUEST_TS_FIELDS = [
  "createdAt",
  "updatedAt",
  "expiresAt",
  "completedAt",
  "sentAt",
  "lastReminderAt",
];
const WIDGET_TS_FIELDS = ["createdAt", "updatedAt"];

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

// ─── Reviews ────────────────────────────────────────────────────────────────

export const reviewsAdapter = {
  async findMany(args: {
    where?: Record<string, unknown>;
    skip?: number;
    take?: number;
  }) {
    requireConvex();
    const filters = args.where || {};
    const docs =
      (await convexQuery<ConvexDoc[]>("reviews/queries:getReviews", {
        userId: filters.userId as string,
        platform: filters.platform as string | undefined,
        limit: (args.skip ?? 0) + (args.take ?? 50) + 10,
      })) || [];
    const shaped = convexToPrismaShapeMany<Record<string, unknown>>(
      docs,
      REVIEW_TS_FIELDS,
    );
    return applyPagination(shaped, args.skip, args.take);
  },

  async findFirst(args: { where?: Record<string, unknown> }) {
    requireConvex();
    const docs = await this.findMany({ where: args.where, take: 1 });
    return docs[0] || null;
  },

  async create(args: { data: Record<string, unknown> }) {
    requireConvex();
    const d = args.data;
    const convexId = await convexMutation<string>(
      "reviews/mutations:createReview",
      {
        userId: d.userId as string,
        customerName: d.customerName as string,
        customerEmail: d.customerEmail as string | undefined,
        customerPhone: d.customerPhone as string | undefined,
        platform: d.platform as string | undefined,
        rating: d.rating as number,
        title: d.title as string | undefined,
        content: d.content as string | undefined,
        language: d.language as string | undefined,
        reviewRequestId: d.reviewRequestId as string | undefined,
        questionResponses: d.questionResponses,
      },
    );
    return { ...d, id: d.id || convexId || "" };
  },

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

  async update(args: {
    where: { id: string } | Record<string, unknown>;
    data: Record<string, unknown>;
  }) {
    requireConvex();
    const id = (args.where as { id: string }).id;
    await convexMutation("reviews/mutations:updateReview", {
      id,
      ...args.data,
    });
    // Return merged shape (best-effort)
    const existing = await this.findFirst({ where: { id } });
    return existing ? { ...existing, ...args.data } : { id, ...args.data };
  },

  async updateMany(args: {
    where: Record<string, unknown>;
    data: Record<string, unknown>;
  }): Promise<BatchResult> {
    requireConvex();
    const docs = await this.findMany({ where: args.where });
    let updatedCount = 0;
    for (const doc of docs) {
      try {
        await this.update({
          where: { id: (doc as Record<string, unknown>).id as string },
          data: args.data,
        });
        updatedCount++;
      } catch {
        // Continue on individual failures
      }
    }
    return { count: updatedCount };
  },

  async findManyFeatured(userId: string) {
    requireConvex();
    const docs = await this.findMany({
      where: { userId, isFeatured: true },
    });
    // Sort by featuredOrder ascending
    return (docs as Record<string, unknown>[]).sort(
      (a, b) =>
        ((a.featuredOrder as number) ?? 0) - ((b.featuredOrder as number) ?? 0),
    );
  },

  async aggregate(args: { where?: Record<string, unknown> }) {
    requireConvex();
    const docs = await this.findMany({ where: args.where });
    const records = docs as Record<string, unknown>[];
    if (records.length === 0) {
      return {
        _count: { _all: 0 },
        _avg: { rating: null },
        _max: { featuredOrder: null },
      };
    }
    let totalRating = 0;
    let maxFeaturedOrder = 0;
    for (const r of records) {
      totalRating += (r.rating as number) ?? 0;
      const order = (r.featuredOrder as number) ?? 0;
      if (order > maxFeaturedOrder) maxFeaturedOrder = order;
    }
    return {
      _count: { _all: records.length },
      _avg: { rating: totalRating / records.length },
      _max: { featuredOrder: maxFeaturedOrder },
    };
  },
};

// ─── Review Requests ────────────────────────────────────────────────────────

export const reviewRequestsAdapter = {
  async findMany(args: {
    where?: Record<string, unknown>;
    skip?: number;
    take?: number;
  }) {
    requireConvex();
    const filters = args.where || {};
    const docs =
      (await convexQuery<ConvexDoc[]>("reviews/queries:getReviewRequests", {
        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,
      REQUEST_TS_FIELDS,
    );
    return applyPagination(shaped, args.skip, args.take);
  },

  async findFirst(args: { where?: Record<string, unknown> }) {
    requireConvex();
    const docs = await this.findMany({ where: args.where, take: 1 });
    return docs[0] || null;
  },

  async create(args: { data: Record<string, unknown> }) {
    requireConvex();
    const d = args.data;
    const result = await convexMutation<{ id: string; token: string }>(
      "reviews/mutations:createReviewRequest",
      {
        userId: d.userId as string,
        customerName: d.customerName as string,
        customerEmail: d.customerEmail as string | undefined,
        customerPhone: d.customerPhone as string | undefined,
        channel: d.channel as string | undefined,
        message: d.message as string | undefined,
        language: d.language as string | undefined,
        bookingId: d.bookingId as string | undefined,
        customerId: d.customerId as string | undefined,
        questions: d.questions,
      },
    );
    return { ...d, id: result?.id || "", token: result?.token || "" };
  },

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

  async update(args: {
    where: { id: string } | Record<string, unknown>;
    data: Record<string, unknown>;
  }) {
    requireConvex();
    const id = (args.where as { id: string }).id;
    await convexMutation("reviews/mutations:updateReviewRequest", {
      id,
      ...args.data,
    });
    const existing = await this.findFirst({ where: { id } });
    return existing ? { ...existing, ...args.data } : { id, ...args.data };
  },

  /**
   * Find a review request by its unique token, filtering by allowed statuses
   * and optionally requiring not-expired.
   */
  async findByToken(args: {
    token: string;
    statuses: string[];
    notExpired?: boolean;
    select?: Record<string, boolean>;
  }) {
    requireConvex();
    const doc = await convexQuery<ConvexDoc | null>(
      "reviews/queries:getReviewRequestByToken",
      {
        token: args.token,
        statuses: args.statuses,
        notExpired: args.notExpired ?? false,
      },
    );
    if (!doc) return null;
    const shaped = convexToPrismaShape<Record<string, unknown>>(
      doc,
      REQUEST_TS_FIELDS,
    );
    // If select is specified, filter to only those fields
    if (args.select) {
      const filtered: Record<string, unknown> = {};
      for (const key of Object.keys(args.select)) {
        if (args.select[key]) filtered[key] = shaped[key];
      }
      return filtered;
    }
    return shaped;
  },
};

// ─── Review Widget Settings ─────────────────────────────────────────────────

export const reviewWidgetSettingsAdapter = {
  async findFirst(args: { where?: Record<string, unknown> }) {
    requireConvex();
    const filters = args.where || {};
    const doc = await convexQuery<ConvexDoc | null>(
      "reviews/queries:getWidgetSettings",
      {
        userId: filters.userId as string,
      },
    );
    return doc
      ? convexToPrismaShape<Record<string, unknown>>(doc, WIDGET_TS_FIELDS)
      : null;
  },

  async upsert(args: {
    where: Record<string, unknown>;
    create: Record<string, unknown>;
    update: Record<string, unknown>;
  }) {
    requireConvex();
    const d = { ...args.create, ...args.update };
    const convexId = await convexMutation<string>(
      "reviews/mutations:updateWidgetSettings",
      {
        userId: (args.where.userId || d.userId) as string,
        businessName: d.businessName as string | undefined,
        businessNameAr: d.businessNameAr as string | undefined,
        logoUrl: d.logoUrl as string | undefined,
        primaryColor: d.primaryColor as string | undefined,
        backgroundColor: d.backgroundColor as string | undefined,
        textColor: d.textColor as string | undefined,
        thankYouMessage: d.thankYouMessage as string | undefined,
        thankYouMessageAr: d.thankYouMessageAr as string | undefined,
        showPoweredBy: d.showPoweredBy as boolean | undefined,
        customCss: d.customCss as string | undefined,
      },
    );
    return { ...d, id: convexId || "" };
  },
};

// ─── Review Question Templates (Prisma passthrough) ────────────────────────

export const reviewQuestionTemplatesAdapter = {
  async findUnique(args: {
    where: Record<string, unknown>;
    select?: Record<string, boolean>;
  }) {
    return prisma.review_question_templates.findUnique(args as never);
  },

  async findMany(args: {
    where?: Record<string, unknown>;
    orderBy?: Record<string, unknown>;
  }) {
    return prisma.review_question_templates.findMany(args as never);
  },

  async create(args: { data: Record<string, unknown> }) {
    return prisma.review_question_templates.create(args as never);
  },

  async update(args: {
    where: Record<string, unknown>;
    data: Record<string, unknown>;
  }) {
    return prisma.review_question_templates.update(args as never);
  },

  async delete(args: { where: Record<string, unknown> }) {
    return prisma.review_question_templates.delete(args as never);
  },
};
