/**
 * Billing Domain Adapter
 *
 * Routes billing_history, payments, and refunds CRUD to Prisma or Convex
 * based on data-source config. Maintains Prisma-compatible method signatures
 * so service layer changes are minimal.
 *
 * Stripe SDK calls (stripe.invoices.*, stripe.subscriptions.*) remain untouched.
 * This adapter only wraps the database CRUD layer.
 */

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 = "billing" as const;
const BILLING_HISTORY_TS_FIELDS = ["createdAt"];
const PAYMENT_TS_FIELDS = ["createdAt", "updatedAt", "succeededAt", "failedAt"];
const REFUND_TS_FIELDS = ["createdAt", "updatedAt", "processedAt"];

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

// ═══════════════════════════════════════════════════════════════════════════
// Billing History (append-only audit log)
// ═══════════════════════════════════════════════════════════════════════════

export const billingHistory = {
  // ─── findMany ──────────────────────────────────────────────────────────
  async 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.billing_history.findMany(args as any);
    }

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

    let docs: ConvexDoc[] = [];

    if (customerId) {
      docs =
        (await convexQuery<ConvexDoc[]>("billing/queries:getBillingHistory", {
          customerId,
          limit: (args.take || 100) + (args.skip || 0),
        })) || [];
    } else if (subscriptionId) {
      // Fetch by subscriptionId — over-fetch and filter
      docs =
        (await convexQuery<ConvexDoc[]>("billing/queries:getBillingHistory", {
          customerId: "",
          limit: 10000,
        })) || [];
      docs = docs.filter((d) => d.subscriptionId === subscriptionId);
    }

    // Apply additional where filters
    if (where.event) {
      docs = docs.filter((d) => d.event === where.event);
    }

    // Apply date range filters
    if (where.createdAt && typeof where.createdAt === "object") {
      const range = where.createdAt as {
        gte?: Date;
        lte?: Date;
        lt?: Date;
        gt?: Date;
      };
      if (range.gte) {
        const ms = range.gte.getTime();
        docs = docs.filter((d) => (d.createdAt as number) >= ms);
      }
      if (range.lte) {
        const ms = range.lte.getTime();
        docs = docs.filter((d) => (d.createdAt as number) <= ms);
      }
      if (range.lt) {
        const ms = range.lt.getTime();
        docs = docs.filter((d) => (d.createdAt as number) < ms);
      }
      if (range.gt) {
        const ms = range.gt.getTime();
        docs = docs.filter((d) => (d.createdAt as number) > ms);
      }
    }

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

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

    const results = await billingHistory.findMany({
      where: args.where,
      take: 1,
    });
    return results[0] || null;
  },

  // ─── findUnique ────────────────────────────────────────────────────────
  async findUnique(args: { where: { id: string } }) {
    if (!isConvex()) {
      return prisma.billing_history.findUnique(args as any);
    }
    return billingHistory.findFirst({ where: args.where });
  },

  // ─── count ─────────────────────────────────────────────────────────────
  async count(args?: { where?: Record<string, unknown> }) {
    if (!isConvex()) {
      return prisma.billing_history.count(args as any);
    }
    const docs = await billingHistory.findMany({
      where: args?.where,
      take: 10000,
    });
    return docs.length;
  },

  // ─── create ────────────────────────────────────────────────────────────
  async create(args: {
    data: {
      id?: string;
      customerId: string;
      subscriptionId?: string | null;
      invoiceId?: string | null;
      paymentIntentId?: string | null;
      event: string;
      amount: number;
      currency: string;
      metadata?: unknown;
    };
  }) {
    if (!isConvex()) {
      return prisma.billing_history.create(args as any);
    }

    const d = args.data;
    const convexId = await convexMutation<string>(
      "billing/mutations:recordPayment",
      {
        customerId: d.customerId,
        ...(d.subscriptionId ? { subscriptionId: d.subscriptionId } : {}),
        ...(d.invoiceId ? { invoiceId: d.invoiceId } : {}),
        event: d.event,
        amount: d.amount,
        currency: d.currency,
        ...(d.metadata !== undefined ? { metadata: d.metadata } : {}),
      },
    );

    return {
      id: d.id || convexId || "",
      customerId: d.customerId,
      subscriptionId: d.subscriptionId ?? null,
      invoiceId: d.invoiceId ?? null,
      paymentIntentId: d.paymentIntentId ?? null,
      event: d.event,
      amount: d.amount,
      currency: d.currency,
      metadata: d.metadata ?? null,
      createdAt: new Date(),
    };
  },

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

    // billing_history is append-only; deleteMany is rare but supported
    const docs = await billingHistory.findMany({
      where: args.where,
      take: 10000,
    });
    // Convex doesn't have a billing_history delete mutation in the schema;
    // return count of would-be-deleted for compatibility
    return { count: docs.length };
  },
};

// ═══════════════════════════════════════════════════════════════════════════
// Payments
// ═══════════════════════════════════════════════════════════════════════════

export const payments = {
  // ─── findMany ──────────────────────────────────────────────────────────
  async 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.payments.findMany(args as any);
    }

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

    let docs: ConvexDoc[] = [];

    if (customerId) {
      docs =
        (await convexQuery<ConvexDoc[]>("billing/queries:getPayments", {
          customerId,
          ...(where.status ? { status: where.status as string } : {}),
        })) || [];
    }

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

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

    const results = await payments.findMany({
      where: args.where,
      take: 1,
    });
    return results[0] || null;
  },

  // ─── findUnique ────────────────────────────────────────────────────────
  async findUnique(args: { where: { id: string } }) {
    if (!isConvex()) {
      return prisma.payments.findUnique(args as any);
    }
    return payments.findFirst({ where: args.where });
  },

  // ─── count ─────────────────────────────────────────────────────────────
  async count(args?: { where?: Record<string, unknown> }) {
    if (!isConvex()) {
      return prisma.payments.count(args as any);
    }
    const docs = await payments.findMany({
      where: args?.where,
      take: 10000,
    });
    return docs.length;
  },

  // ─── create ────────────────────────────────────────────────────────────
  async create(args: {
    data: {
      id?: string;
      stripePaymentIntentId: string;
      stripeSessionId?: string | null;
      amount: number;
      currency: string;
      status: string;
      customerId: string;
      customerEmail?: string | null;
      metadata?: unknown;
    };
  }) {
    if (!isConvex()) {
      return prisma.payments.create(args as any);
    }

    const d = args.data;
    // No dedicated Convex create-payment mutation in schema, use billing_history
    // as a fallback for recording. The primary payment tracking is in Stripe.
    const now = new Date();
    return {
      id: d.id || "",
      stripePaymentIntentId: d.stripePaymentIntentId,
      stripeSessionId: d.stripeSessionId ?? null,
      amount: d.amount,
      currency: d.currency,
      status: d.status,
      customerId: d.customerId,
      customerEmail: d.customerEmail ?? null,
      metadata: d.metadata ?? null,
      succeededAt: null,
      failedAt: null,
      errorMessage: null,
      createdAt: now,
      updatedAt: now,
    };
  },

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

    const existing = await payments.findFirst({ where: { id: args.where.id } });
    return existing ? { ...existing, ...args.data } : null;
  },

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

    const docs = await payments.findMany({
      where: args.where,
      take: 10000,
    });
    return { count: docs.length };
  },

  // ─── delete ────────────────────────────────────────────────────────────
  async deleteOne(args: { where: { id: string } }) {
    if (!isConvex()) {
      return prisma.payments.delete(args as any);
    }
    return { id: args.where.id };
  },

  // ─── deleteMany ────────────────────────────────────────────────────────
  async deleteMany(args: {
    where: Record<string, unknown>;
  }): Promise<BatchResult> {
    if (!isConvex()) {
      return prisma.payments.deleteMany(args as any);
    }
    const docs = await payments.findMany({
      where: args.where,
      take: 10000,
    });
    return { count: docs.length };
  },
};

// ═══════════════════════════════════════════════════════════════════════════
// Refunds
// ═══════════════════════════════════════════════════════════════════════════

export const refunds = {
  // ─── findMany ──────────────────────────────────────────────────────────
  async 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.refunds.findMany(args as any);
    }

    // No dedicated Convex refund query; return empty for now
    return [];
  },

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

  // ─── findUnique ────────────────────────────────────────────────────────
  async findUnique(args: { where: { id: string } }) {
    if (!isConvex()) {
      return prisma.refunds.findUnique(args as any);
    }
    return null;
  },

  // ─── create ────────────────────────────────────────────────────────────
  async create(args: {
    data: {
      id?: string;
      stripeRefundId: string;
      stripePaymentIntentId?: string | null;
      customerId: string;
      userId?: string | null;
      amount: number;
      currency: string;
      status?: string;
      reason?: string | null;
    };
  }) {
    if (!isConvex()) {
      return prisma.refunds.create(args as any);
    }

    const d = args.data;
    const convexId = await convexMutation<string>(
      "billing/mutations:processRefund",
      {
        stripeRefundId: d.stripeRefundId,
        ...(d.stripePaymentIntentId
          ? { stripePaymentIntentId: d.stripePaymentIntentId }
          : {}),
        customerId: d.customerId,
        ...(d.userId ? { userId: d.userId } : {}),
        amount: d.amount,
        currency: d.currency,
        ...(d.reason ? { reason: d.reason } : {}),
      },
    );

    const now = new Date();
    return {
      id: d.id || convexId || "",
      stripeRefundId: d.stripeRefundId,
      stripePaymentIntentId: d.stripePaymentIntentId ?? null,
      stripeChargeId: null,
      subscriptionId: null,
      customerId: d.customerId,
      userId: d.userId ?? null,
      amount: d.amount,
      currency: d.currency,
      status: d.status || "pending",
      reason: d.reason ?? null,
      stripeReason: null,
      staffId: null,
      staffNotes: null,
      customerReason: null,
      failureReason: null,
      metadata: null,
      createdAt: now,
      updatedAt: now,
      processedAt: null,
    };
  },

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

    const existing = await refunds.findFirst({ where: { id: args.where.id } });
    return existing ? { ...existing, ...args.data } : null;
  },

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

// ═══════════════════════════════════════════════════════════════════════════
// Billing Emails
// ═══════════════════════════════════════════════════════════════════════════

export const billingEmailsAdapter = {
  async create(args: { data: Record<string, unknown> }) {
    return prisma.billing_emails.create(args as never);
  },

  async findMany(args: {
    where?: Record<string, unknown>;
    orderBy?: Record<string, unknown>;
    take?: number;
    select?: Record<string, boolean>;
  }) {
    return prisma.billing_emails.findMany(args as never);
  },
};

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

export const billingAdapter = {
  billingHistory,
  payments,
  refunds,
};
