/**
 * Admin search — server-side fuzzy match across admin routes + entities.
 *
 * Searched surfaces:
 *   1. Admin route manifest (title + path) — in-memory, no DB.
 *   2. Vendors (businessName, id) — ILIKE query.
 *   3. Users — shared smart matcher (email/phone blind-index, uuid exact, name ILIKE).
 *   4. Deals (title ILIKE, id exact).
 *   5. Purchases (id exact).
 *
 * PII rules:
 *   - Email is stored encrypted; lookup uses blind index (HMAC-SHA256).
 *   - Decrypted email is never returned in search results.
 *   - displayName is not PII-encrypted; safe to ILIKE.
 *
 * Returns unified AdminSearchResult[] sorted: routes first, then entities.
 */

import { eq, ilike, sql } from 'drizzle-orm';
import type { DrizzleClient } from '@/server/db/client.js';
import { vendors, users, deals, vendorTranslations, orderLine } from '@/server/db/schema.js';
import { order } from '@platform-modules/commerce-orders';
import { VENDOR_DEFAULT_LOCALE } from '@/server/db/queries/vendors.js';
import { buildUserSearchCondition, isUuid } from '@/server/admin/user-search-match.js';
import { ADMIN_ROUTES } from './_shared/admin-nav.js';
import type { AdminSearchResult } from '@/server/schemas/admin-search.js';

// ─── Helpers ─────────────────────────────────────────────────────────────────

/** Simple fuzzy: query terms must all appear in target (case-insensitive). */
function fuzzyMatch(target: string, q: string): boolean {
  const lower = target.toLowerCase();
  return q
    .toLowerCase()
    .split(/\s+/)
    .every((term) => lower.includes(term));
}

// ─── Route search ─────────────────────────────────────────────────────────────

function searchRoutes(q: string, limit: number): AdminSearchResult[] {
  return ADMIN_ROUTES.filter(
    (r) => fuzzyMatch(r.titleHe, q) || fuzzyMatch(r.titleEn, q) || r.path.includes(q.toLowerCase()),
  )
    .slice(0, limit)
    .map((r) => ({
      type: 'route' as const,
      id: r.path,
      title: r.titleHe,
      subtitle: r.titleEn,
      href: r.path,
    }));
}

// ─── Entity search ────────────────────────────────────────────────────────────

async function searchVendors(
  db: DrizzleClient,
  q: string,
  limit: number,
): Promise<AdminSearchResult[]> {
  const whereClause = isUuid(q) ? eq(vendors.id, q) : ilike(vendors.businessName, `%${q}%`);

  const rows = await db
    .select({
      id: vendors.id,
      businessName: vendors.businessName,
      accountState: vendors.accountState,
    })
    .from(vendors)
    .where(whereClause)
    .limit(limit);

  return rows.map((r) => ({
    type: 'vendor' as const,
    id: r.id,
    title: r.businessName,
    subtitle: r.accountState,
    href: `/admin/vendors/${r.id}`,
  }));
}

async function searchUsers(
  db: DrizzleClient,
  q: string,
  limit: number,
  piiKey: string,
): Promise<AdminSearchResult[]> {
  const rows = await db
    .select({
      id: users.id,
      vendorName: sql<
        string | null
      >`(SELECT vt2.display_name FROM ${vendorTranslations} vt2 INNER JOIN vendors v2 ON v2.id = vt2.vendor_id AND v2.owner_user_id = ${sql.raw('"users"."id"')} WHERE vt2.locale = ${VENDOR_DEFAULT_LOCALE} LIMIT 1)`,
      userDisplayName: users.displayName,
      accountState: users.accountState,
    })
    .from(users)
    .where(await buildUserSearchCondition(q, piiKey))
    .limit(limit);

  return rows.map((r) => ({
    type: 'user' as const,
    id: r.id,
    title: r.vendorName ?? r.userDisplayName ?? r.id,
    subtitle: r.accountState,
    href: `/admin/users/${r.id}`,
  }));
}

async function searchDeals(
  db: DrizzleClient,
  q: string,
  limit: number,
): Promise<AdminSearchResult[]> {
  const whereClause = isUuid(q) ? eq(deals.id, q) : ilike(deals.title, `%${q}%`);

  const rows = await db
    .select({ id: deals.id, title: deals.title, dealState: deals.dealState })
    .from(deals)
    .where(whereClause)
    .limit(limit);

  return rows.map((r) => ({
    type: 'deal' as const,
    id: r.id,
    title: r.title,
    subtitle: r.dealState,
    href: `/admin/deals/${r.id}`,
  }));
}

async function searchPurchases(
  db: DrizzleClient,
  q: string,
  limit: number,
): Promise<AdminSearchResult[]> {
  if (!isUuid(q)) return [];

  const rows = await db
    .select({ id: orderLine.id, paymentStatus: order.status })
    .from(orderLine)
    .innerJoin(order, eq(orderLine.orderId, order.id))
    .where(eq(orderLine.id, q))
    .limit(limit);

  return rows.map((r) => ({
    type: 'purchase' as const,
    id: r.id,
    title: `רכישה ${r.id.slice(0, 8)}`,
    subtitle: r.paymentStatus,
    href: `/admin/purchases/${r.id}`,
  }));
}

// ─── Main ─────────────────────────────────────────────────────────────────────

export interface AdminSearchOptions {
  q: string;
  limit: number;
  /** PII_KEY from env — required for email blind-index lookup. */
  piiKey: string;
}

export async function adminSearch(
  db: DrizzleClient,
  { q, limit, piiKey }: AdminSearchOptions,
): Promise<AdminSearchResult[]> {
  const routeLimit = Math.min(5, limit);
  const entityLimit = Math.max(1, Math.floor((limit - routeLimit) / 4));

  const [routeResults, vendorResults, userResults, dealResults, purchaseResults] =
    await Promise.all([
      Promise.resolve(searchRoutes(q, routeLimit)),
      searchVendors(db, q, entityLimit),
      searchUsers(db, q, entityLimit, piiKey),
      searchDeals(db, q, entityLimit),
      searchPurchases(db, q, entityLimit),
    ]);

  const all = [
    ...routeResults,
    ...vendorResults,
    ...userResults,
    ...dealResults,
    ...purchaseResults,
  ];

  return all.slice(0, limit);
}
