import prisma from "../db.server";
import { assertTenantScope } from "../lib/security/tenant-context";

export interface RetrievedProductDTO {
  id: string;
  shopId: string;
  title: string;
  handle: string;
  description?: string | null;
  productType?: string | null;
  vendor?: string | null;
  tags?: string[];
  price: number;
  compareAtPrice?: number | null;
  currency: string;
  availableForSale: boolean;
  totalInventory?: number;
  variants: Array<{
    id: string;
    title: string;
    price: number;
    compareAtPrice?: number | null;
    availableForSale: boolean;
    inventoryQuantity?: number;
  }>;
}

export interface CatalogRetrievalResult {
  shopId: string;
  wasExecuted: boolean;
  query: string;
  totalFound: number;
  products: RetrievedProductDTO[];
  noMatchesFound: boolean;
}

export class CatalogRetrievalService {
  /**
   * Authoritative product retrieval strictly scoped to shopId.
   * Returns empty array if no matches; explicitly flags noMatchesFound.
   */
  static async getRelevantProducts({
    shopId,
    query,
    maxResults = 5,
    maxPrice,
    category,
  }: {
    shopId: string;
    query?: string;
    maxResults?: number;
    maxPrice?: number;
    category?: string;
  }): Promise<CatalogRetrievalResult> {
    if (!shopId) {
      throw new Error("CatalogRetrievalService: shopId is required.");
    }

    const trimmedQuery = (query || "").trim().toLowerCase();
    const whereClause: any = { shopId };

    if (maxPrice !== undefined && !isNaN(maxPrice) && maxPrice > 0) {
      whereClause.minPrice = { lte: maxPrice };
    }

    if (trimmedQuery && trimmedQuery.length > 0) {
      whereClause.OR = [
        { title: { contains: trimmedQuery } },
        { description: { contains: trimmedQuery } },
        { handle: { contains: trimmedQuery } },
      ];
    }

    const records = await prisma.productCache.findMany({
      where: whereClause,
      take: Math.min(maxResults, 10),
      orderBy: { updatedAt: "desc" },
    });

    // Enforce tenant scoping verification on all returned rows
    records.forEach((r) => assertTenantScope(r.shopId, shopId));

    const products: RetrievedProductDTO[] = records.map((p) => {
      const rawVariants = Array.isArray(p.variants) ? (p.variants as any[]) : [];
      const parsedVariants = rawVariants.map((v) => ({
        id: String(v.shopifyVariantId || v.id || ""),
        title: v.title || "Default Variant",
        price: parseFloat(v.price || "0"),
        compareAtPrice: v.compareAtPrice ? parseFloat(v.compareAtPrice) : null,
        availableForSale: v.available !== false,
        inventoryQuantity: v.inventoryQuantity ?? 10,
      }));

      const isAvailable = parsedVariants.some((v) => v.availableForSale);
      const totalInv = parsedVariants.reduce((sum, v) => sum + (v.inventoryQuantity || 0), 0);

      return {
        id: p.id,
        shopId: p.shopId,
        title: p.title,
        handle: p.handle,
        description: p.description,
        price: parseFloat(p.minPrice.toString()),
        currency: p.currency || "USD",
        availableForSale: isAvailable,
        totalInventory: totalInv,
        variants: parsedVariants,
      };
    });

    const wasExecuted = Boolean(trimmedQuery || maxPrice || category);

    return {
      shopId,
      wasExecuted,
      query: trimmedQuery,
      totalFound: products.length,
      products,
      noMatchesFound: wasExecuted && products.length === 0,
    };
  }
}
