import { Prisma } from '@prisma/client';
import { type SearchParams } from '../../domain/repositories/search.repository';
import { parseFilterBy } from './search-filter-parser';

const FTS_COLUMNS = `coalesce(p."title", '') || ' ' || coalesce(p."description", '') || ' ' || coalesce(p."address", '') || ' ' || coalesce(p."district", '') || ' ' || coalesce(p."city", '')`;

/** Build WHERE conditions from search parameters. */
export function buildSearchConditions(params: SearchParams): Prisma.Sql[] {
  const conditions: Prisma.Sql[] = [Prisma.sql`l."status" = 'ACTIVE'`];
  const parsed = parseFilterBy(params.filterBy ?? '');

  if (parsed.propertyType) {
    conditions.push(Prisma.sql`p."propertyType" = ${parsed.propertyType}`);
  }
  if (parsed.transactionType) {
    conditions.push(Prisma.sql`l."transactionType" = ${parsed.transactionType}`);
  }
  if (parsed.priceMin !== undefined && parsed.priceMax !== undefined) {
    conditions.push(Prisma.sql`l."priceVND" BETWEEN ${BigInt(parsed.priceMin)} AND ${BigInt(parsed.priceMax)}`);
  } else if (parsed.priceMin !== undefined) {
    conditions.push(Prisma.sql`l."priceVND" >= ${BigInt(parsed.priceMin)}`);
  } else if (parsed.priceMax !== undefined) {
    conditions.push(Prisma.sql`l."priceVND" <= ${BigInt(parsed.priceMax)}`);
  }
  if (parsed.areaMin !== undefined && parsed.areaMax !== undefined) {
    conditions.push(Prisma.sql`p."areaM2" BETWEEN ${parsed.areaMin} AND ${parsed.areaMax}`);
  } else if (parsed.areaMin !== undefined) {
    conditions.push(Prisma.sql`p."areaM2" >= ${parsed.areaMin}`);
  } else if (parsed.areaMax !== undefined) {
    conditions.push(Prisma.sql`p."areaM2" <= ${parsed.areaMax}`);
  }
  if (parsed.bedrooms !== undefined) {
    conditions.push(Prisma.sql`p."bedrooms" >= ${parsed.bedrooms}`);
  }
  if (parsed.district) {
    conditions.push(Prisma.sql`p."district" = ${parsed.district}`);
  }
  if (parsed.city) {
    conditions.push(Prisma.sql`p."city" = ${parsed.city}`);
  }

  if (params.geoPoint && params.geoRadiusKm) {
    const radiusMeters = params.geoRadiusKm * 1000;
    conditions.push(
      Prisma.sql`ST_DWithin(
        p."location"::geography,
        ST_SetSRID(ST_MakePoint(${params.geoPoint.lng}, ${params.geoPoint.lat}), 4326)::geography,
        ${radiusMeters}
      )`,
    );
  }

  if (hasText(params)) {
    conditions.push(
      Prisma.sql`(
        to_tsvector('simple', ${Prisma.raw(FTS_COLUMNS)})
        @@ plainto_tsquery('simple', ${params.query!})
      )`,
    );
  }

  return conditions;
}

/** Build ORDER BY clause from search parameters. */
export function buildOrderClause(params: SearchParams): Prisma.Sql {
  if (params.geoPoint && (params.sortBy === 'distance' || (!params.sortBy && params.geoRadiusKm))) {
    return Prisma.sql`ORDER BY ST_Distance(
      p."location"::geography,
      ST_SetSRID(ST_MakePoint(${params.geoPoint.lng}, ${params.geoPoint.lat}), 4326)::geography
    ) ASC`;
  }

  switch (params.sortBy) {
    case 'price_asc':
      return Prisma.sql`ORDER BY l."priceVND" ASC`;
    case 'price_desc':
      return Prisma.sql`ORDER BY l."priceVND" DESC`;
    case 'date_desc':
      return Prisma.sql`ORDER BY l."publishedAt" DESC NULLS LAST`;
    case 'relevance':
    default:
      if (hasText(params)) {
        return Prisma.sql`ORDER BY ts_rank(
          to_tsvector('simple', ${Prisma.raw(FTS_COLUMNS)}),
          plainto_tsquery('simple', ${params.query!})
        ) DESC, l."publishedAt" DESC NULLS LAST`;
      }
      return Prisma.sql`ORDER BY l."publishedAt" DESC NULLS LAST`;
  }
}

function hasText(params: SearchParams): boolean {
  return !!(params.query && params.query !== '*');
}
