/**
 * ایندکس‌ساز جست‌وجو.
 *
 * معماری: یک واسط واحد (SearchBackend) که دو پیاده‌سازی دارد:
 *   - PostgresBackend (پیش‌فرض، همیشه در دسترس؛ جدول search_documents + pg_trgm)
 *   - OpenSearchBackend (در صورت تنظیم OPENSEARCH_URL فعال می‌شود)
 * بنابراین پروژه بدون سرویس جست‌وجوی خارجی هم کاملاً کار می‌کند و در Data Center
 * با یک متغیر محیطی به OpenSearch منتقل می‌شود.
 */
import { eq, and } from 'drizzle-orm';
import { getDb } from '../../database/client.js';
import {
  searchDocuments, articles, organizations, businesses, shops, datasets, cases, reports, guilds,
} from '../../database/schema/index.js';
import { normalizePersian } from '../../common/persian.js';

export type EntityType = 'article' | 'organization' | 'business' | 'shop' | 'dataset' | 'case' | 'report' | 'guild';

type Doc = {
  entityType: EntityType;
  entityId: string;
  title: string;
  summary: string | null;
  text: string;
  facets: Record<string, unknown>;
  url: string;
  publishedAt: Date | null;
  popularity: number;
  hasImage: boolean;
  hasVideo: boolean;
  hasDocument: boolean;
};

async function upsert(doc: Doc) {
  const db = getDb();
  const values = {
    entityType: doc.entityType,
    entityId: doc.entityId,
    title: doc.title,
    summary: doc.summary,
    normalizedText: normalizePersian(`${doc.title} ${doc.summary ?? ''} ${doc.text}`),
    facets: doc.facets,
    url: doc.url,
    publishedAt: doc.publishedAt,
    popularity: doc.popularity,
    hasImage: doc.hasImage,
    hasVideo: doc.hasVideo,
    hasDocument: doc.hasDocument,
    updatedAt: new Date(),
  };
  await db
    .insert(searchDocuments)
    .values(values)
    .onConflictDoUpdate({ target: [searchDocuments.entityType, searchDocuments.entityId], set: values });
}

export async function removeFromIndex(entityType: EntityType, entityId: string) {
  await getDb()
    .delete(searchDocuments)
    .where(and(eq(searchDocuments.entityType, entityType), eq(searchDocuments.entityId, entityId)));
}

/** ایندکس‌گذاری یک موجودیت مشخص؛ پس از هر تغییر وضعیت مهم صدا زده می‌شود */
export async function indexEntity(entityType: EntityType, entityId: string) {
  const db = getDb();

  if (entityType === 'article') {
    const [a] = await db.select().from(articles).where(eq(articles.id, entityId));
    if (!a || a.deletedAt || a.status !== 'published') return removeFromIndex('article', entityId);
    return upsert({
      entityType: 'article', entityId: a.id, title: a.title, summary: a.summary,
      text: `${a.body} ${(a.topicKeys ?? []).join(' ')} ${(a.tagKeys ?? []).join(' ')} ${a.categoryKey ?? ''}`,
      facets: {
        contentType: a.contentType, categoryKey: a.categoryKey, topicKeys: a.topicKeys, tagKeys: a.tagKeys,
        organizationId: a.organizationId, businessId: a.businessId, regionId: a.regionId, authorId: a.authorId,
        isBreaking: a.isBreaking,
      },
      url: `/news/${a.slug}`, publishedAt: a.publishedAt,
      popularity: a.viewCount + a.discussionCount * 3 + a.saveCount * 5,
      hasImage: Boolean(a.coverUrl), hasVideo: Boolean(a.videoUrl),
      hasDocument: (a.sourceRefs ?? []).length > 0,
    });
  }

  if (entityType === 'organization') {
    const [o] = await db.select().from(organizations).where(eq(organizations.id, entityId));
    if (!o || !o.isActive) return removeFromIndex('organization', entityId);
    return upsert({
      entityType: 'organization', entityId: o.id, title: o.nameFa, summary: o.description,
      text: `${o.shortName ?? ''} ${o.address ?? ''} ${o.typeKey}`,
      facets: { typeKey: o.typeKey, regionId: o.regionId, verificationStatus: o.verificationStatus },
      url: `/organizations/${o.slug}`, publishedAt: o.createdAt, popularity: 0,
      hasImage: Boolean(o.logoUrl), hasVideo: false, hasDocument: false,
    });
  }

  if (entityType === 'business') {
    const [b] = await db.select().from(businesses).where(eq(businesses.id, entityId));
    if (!b || !b.isActive) return removeFromIndex('business', entityId);
    return upsert({
      entityType: 'business', entityId: b.id, title: b.nameFa, summary: b.description,
      text: `${b.categoryKey} ${b.phone ?? ''}`,
      facets: { categoryKey: b.categoryKey, guildId: b.guildId, regionId: b.regionId, verificationStatus: b.verificationStatus },
      url: `/businesses/${b.slug}`, publishedAt: b.createdAt, popularity: 0,
      hasImage: Boolean(b.logoUrl), hasVideo: false, hasDocument: false,
    });
  }

  if (entityType === 'shop') {
    const [s] = await db.select().from(shops).where(eq(shops.id, entityId));
    if (!s) return removeFromIndex('shop', entityId);
    return upsert({
      entityType: 'shop', entityId: s.id, title: s.nameFa, summary: s.address,
      text: `${s.address ?? ''} ${s.phone ?? ''}`,
      facets: { businessId: s.businessId, regionId: s.regionId },
      url: `/businesses/shop/${s.id}`, publishedAt: s.createdAt, popularity: 0,
      hasImage: (s.images ?? []).length > 0, hasVideo: false, hasDocument: false,
    });
  }

  if (entityType === 'dataset') {
    const [d] = await db.select().from(datasets).where(eq(datasets.id, entityId));
    if (!d || d.status !== 'published') return removeFromIndex('dataset', entityId);
    return upsert({
      entityType: 'dataset', entityId: d.id, title: d.titleFa, summary: d.description,
      text: `${d.unit} ${d.publisher} ${d.sourceTitle} ${d.geographicScope ?? ''}`,
      facets: { organizationId: d.organizationId, publisher: d.publisher, chartHint: d.chartHint },
      url: `/statistics/${d.slug}`, publishedAt: d.lastUpdatedAt ?? d.createdAt, popularity: 0,
      hasImage: false, hasVideo: false, hasDocument: Boolean(d.sourceUrl),
    });
  }

  if (entityType === 'case') {
    const [c] = await db.select().from(cases).where(eq(cases.id, entityId));
    if (!c || c.visibility !== 'public') return removeFromIndex('case', entityId);
    const [r] = await db.select().from(reports).where(eq(reports.id, c.reportId));
    return upsert({
      entityType: 'case', entityId: c.id, title: c.title, summary: r?.body?.slice(0, 220) ?? null,
      text: `${r?.body ?? ''} ${r?.categoryKey ?? ''} ${c.caseCode}`,
      facets: {
        organizationId: c.organizationId, status: c.status, priority: c.priority,
        categoryKey: r?.categoryKey, regionId: r?.regionId, kind: r?.kind,
      },
      url: `/reports/${c.caseCode}`, publishedAt: c.createdAt, popularity: 0,
      hasImage: false, hasVideo: false, hasDocument: false,
    });
  }

  if (entityType === 'guild') {
    const [g] = await db.select().from(guilds).where(eq(guilds.id, entityId));
    if (!g) return removeFromIndex('guild', entityId);
    return upsert({
      entityType: 'guild', entityId: g.id, title: g.nameFa, summary: g.description,
      text: g.kind, facets: { kind: g.kind, regionId: g.regionId },
      url: `/guilds/${g.slug}`, publishedAt: g.createdAt, popularity: 0,
      hasImage: false, hasVideo: false, hasDocument: false,
    });
  }
}

/** بازسازی کامل ایندکس (پس از seed یا تغییرات گسترده) */
export async function reindexAll() {
  const db = getDb();
  const counts: Record<string, number> = {};
  const tasks: [EntityType, { id: string }[]][] = [
    ['article', await db.select({ id: articles.id }).from(articles)],
    ['organization', await db.select({ id: organizations.id }).from(organizations)],
    ['business', await db.select({ id: businesses.id }).from(businesses)],
    ['shop', await db.select({ id: shops.id }).from(shops)],
    ['dataset', await db.select({ id: datasets.id }).from(datasets)],
    ['case', await db.select({ id: cases.id }).from(cases)],
    ['guild', await db.select({ id: guilds.id }).from(guilds)],
  ];
  for (const [type, rows] of tasks) {
    for (const row of rows) await indexEntity(type, row.id);
    counts[type] = rows.length;
  }
  return counts;
}
