import type { Metadata } from "next";
import { headers } from "next/headers";
import { notFound } from "next/navigation";
import prisma from "@/lib/prisma";
import { rateLimit } from "@/lib/rate-limit";
import Link from "next/link";
import Image from "next/image";

export const metadata: Metadata = {
  title: "Search Results",
  description: "Browse escort search results on AdultWorld. Filter by location, services, appearance, and more.",
};
import type { Prisma } from "@prisma/client";
import { avatarUrl, withAvatarUrls } from "@/lib/media";
import MediaImage from "@/components/shared/media-image";
import SaveSearchButton from "@/components/shared/save-search-button";
import ShareSearchButton from "@/components/shared/share-search-button";
import { CompareFloatingBar } from "@/components/shared/compare-button";

function relativeTime(date: Date | null): string {
  if (!date) return "";
  const seconds = Math.floor((Date.now() - date.getTime()) / 1000);
  if (seconds < 60) return "just now";
  if (seconds < 3600) return `${Math.floor(seconds / 60)}m ago`;
  if (seconds < 86400) return `${Math.floor(seconds / 3600)}h ago`;
  if (seconds < 604800) return `${Math.floor(seconds / 86400)}d ago`;
  return "";
}

const PER_PAGE = 48;

export default async function SearchResultsPage({
  searchParams,
}: {
  searchParams: Promise<Record<string, string | string[] | undefined>>;
}) {
  const params = await searchParams;
  const page = Math.max(1, Number(params.page) || 1);

  // R15 A.5: rate-limit the polished UI path. /api/search has its own limit
  // but this server component queries Prisma directly, so without this guard
  // bots could bypass it by hitting /search/results?keyword=… instead.
  const hdrs = await headers();
  const ip =
    hdrs.get("cf-connecting-ip") ||
    hdrs.get("x-real-ip") ||
    hdrs.get("x-forwarded-for")?.split(",")[0]?.trim() ||
    "unknown";
  const rl = await rateLimit(`search-results:${ip}`, 60, 60_000);
  if (!rl.success) {
    notFound();
  }

  const where: Prisma.UserWhereInput = {
    user_type: "escort",
    active: true,
    banned_at: null,
  };

  // Visual Similarity Search — find escorts with similar characteristics
  let similarToUsername: string | null = null;
  const similarToId = params.similar_to ? Number(params.similar_to) : null;
  if (similarToId !== null && Number.isFinite(similarToId) && similarToId > 0) {
    const sourceUser = await prisma.user.findFirst({
      where: { id: similarToId },
      include: {
        characteristic: true,
        city: true,
      },
    });
    if (sourceUser) {
      similarToUsername = sourceUser.username;
      where.id = { not: sourceUser.id };

      const simCharWhere: Prisma.CharacteristicWhereInput = {};
      let hasSim = false;
      if (sourceUser.characteristic?.ethnicity_id) {
        simCharWhere.ethnicity_id = sourceUser.characteristic.ethnicity_id;
        hasSim = true;
      }
      if (sourceUser.characteristic?.age_id) {
        simCharWhere.age_id = sourceUser.characteristic.age_id;
        hasSim = true;
      }
      if (sourceUser.characteristic?.gender_id) {
        simCharWhere.gender_id = sourceUser.characteristic.gender_id;
        hasSim = true;
      }
      if (hasSim) {
        where.characteristic = simCharWhere;
      }
      if (sourceUser.city_id) {
        where.city_id = sourceUser.city_id;
      }
    }
  }

  // Keyword search — searches services (enjoys), bio (status), username, and city name
  if (params.keyword) {
    const kw = String(params.keyword);
    where.OR = [
      { status: { contains: kw, mode: "insensitive" } },
      { enjoyUsers: { some: { enjoy: { name: { contains: kw, mode: "insensitive" } } } } },
      { username: { contains: kw, mode: "insensitive" } },
      { city: { name: { contains: kw, mode: "insensitive" } } },
      { country: { name: { contains: kw, mode: "insensitive" } } },
    ];
  }

  // Location filters (by ID)
  if (params.country) {
    where.country_id = Number(params.country);
  }
  if (params.city) {
    where.city_id = Number(params.city);
  }

  // Characteristic filters - build a single characteristic where clause
  const charWhere: Prisma.CharacteristicWhereInput = {};
  let hasCharFilter = false;

  if (params.gender) {
    charWhere.gender_id = Number(params.gender);
    hasCharFilter = true;
  }
  if (params.orientation) {
    charWhere.orientation_id = Number(params.orientation);
    hasCharFilter = true;
  }
  if (params.age) {
    charWhere.age_id = Number(params.age);
    hasCharFilter = true;
  }
  if (params.ethnicity) {
    charWhere.ethnicity_id = Number(params.ethnicity);
    hasCharFilter = true;
  }
  if (params.nationality) {
    charWhere.nationality_id = Number(params.nationality);
    hasCharFilter = true;
  }
  if (params.hair_color) {
    charWhere.hair_color_id = Number(params.hair_color);
    hasCharFilter = true;
  }
  if (params.eye_color) {
    charWhere.eye_color_id = Number(params.eye_color);
    hasCharFilter = true;
  }
  if (params.height) {
    charWhere.height_id = Number(params.height);
    hasCharFilter = true;
  }
  if (params.weight) {
    charWhere.weight_id = Number(params.weight);
    hasCharFilter = true;
  }
  if (params.smoking) {
    charWhere.smoking_id = Number(params.smoking);
    hasCharFilter = true;
  }
  if (params.travel) {
    charWhere.travel_id = Number(params.travel);
    hasCharFilter = true;
  }
  if (params.breast_size) {
    charWhere.breast_size_id = Number(params.breast_size);
    hasCharFilter = true;
  }
  // Advanced characteristic filters that the form already collects but
  // earlier code dropped silently.
  if (params.hair_length) {
    charWhere.hair_length_id = Number(params.hair_length);
    hasCharFilter = true;
  }
  if (params.hair_public) {
    charWhere.hair_public_id = Number(params.hair_public);
    hasCharFilter = true;
  }
  if (params.breast_state) {
    charWhere.breast_state_id = Number(params.breast_state);
    hasCharFilter = true;
  }
  if (params.cup_size) {
    charWhere.cup_size_id = Number(params.cup_size);
    hasCharFilter = true;
  }
  if (params.calling) {
    charWhere.calling_id = Number(params.calling);
    hasCharFilter = true;
  }

  if (hasCharFilter) {
    where.characteristic = charWhere;
  }

  // Top-level filters that the form serialises but the prior query
  // never applied. Verified-only and has-photos in particular were
  // headline UI options that did nothing.
  if (params.verified_only === "1" || params.verified_only === "true") {
    where.is_verified = true;
  }
  if (params.has_photos === "1" || params.has_photos === "true") {
    where.photos = { some: {} };
  }
  // R14 A.4: time-bounded discovery. `period=week` AND `period=today` filter
  // to escorts who joined within the window. Other filters still apply.
  if (params.period === "week") {
    const since = new Date(Date.now() - 7 * 24 * 60 * 60 * 1000);
    where.created_at = { gte: since };
  } else if (params.period === "today") {
    const since = new Date(Date.now() - 24 * 60 * 60 * 1000);
    where.created_at = { gte: since };
  }
  // R14 A.4: also wire the existing `verified=true` and `vip=true` chip
  // params (QuickFilters used them but the results page only honored
  // `verified_only`).
  if (params.verified === "true") {
    where.is_verified = true;
  }
  if (params.vip === "true") {
    where.is_vip = true;
  }
  if (params.available === "true") {
    where.status = "available";
  }
  if (params.new === "true") {
    const since = new Date(Date.now() - 30 * 24 * 60 * 60 * 1000);
    where.created_at = { gte: since };
  }
  // Pivot-based filters (language, piercing, tattoo) live on side tables.
  if (params.language) {
    const langId = Number(params.language);
    if (Number.isFinite(langId) && langId > 0) {
      where.languageLinks = { some: { id: langId } };
    }
  }
  if (params.piercing) {
    const pId = Number(params.piercing);
    if (Number.isFinite(pId) && pId > 0) {
      where.piercings = { some: { id: pId } };
    }
  }
  if (params.tattoo) {
    const tId = Number(params.tattoo);
    if (Number.isFinite(tId) && tId > 0) {
      where.tattoos = { some: { id: tId } };
    }
  }

  // Services filter
  if (params.services) {
    const serviceIds = String(params.services)
      .split(",")
      .map(Number)
      .filter((n) => n > 0);
    if (serviceIds.length > 0) {
      where.enjoyUsers = {
        some: {
          enjoy_id: { in: serviceIds },
        },
      };
    }
  }

  const [escortsRaw, total] = await Promise.all([
    prisma.user.findMany({
      where,
      select: {
        id: true,
        id_aw: true,
        username: true,
        profile_photo: true,
        is_vip: true,
        is_verified: true,
        lastonline_at: true,
        country: { select: { name: true } },
        city: { select: { name: true } },
        characteristic: {
          select: {
            gender: { select: { name: true } },
            age: { select: { name: true } },
          },
        },
      },
      orderBy: [{ is_top: "desc" }, { is_vip: "desc" }, { lastonline_at: "desc" }],
      skip: (page - 1) * PER_PAGE,
      take: PER_PAGE,
    }),
    prisma.user.count({ where }),
  ]);
  const escorts = await withAvatarUrls(escortsRaw);

  const totalPages = Math.ceil(total / PER_PAGE);

  // Build filter summary from param keys
  const filterLabels: Record<string, string> = {
    keyword: "Keyword",
    gender: "Gender",
    orientation: "Orientation",
    age: "Age",
    country: "Country",
    city: "City",
    ethnicity: "Ethnicity",
    nationality: "Nationality",
    hair_color: "Hair Color",
    eye_color: "Eye Color",
    height: "Height",
    weight: "Weight",
    smoking: "Smoking",
    travel: "Travel",
    breast_size: "Breast Size",
    services: "Services",
  };

  const activeFilters = Object.entries(params)
    .filter(([k, v]) => v && k !== "page" && filterLabels[k])
    .map(([k]) => filterLabels[k]);

  // Build pagination URL helper
  function pageUrl(p: number) {
    const sp = new URLSearchParams();
    Object.entries(params).forEach(([k, v]) => {
      if (v && k !== "page") sp.set(k, String(v));
    });
    if (p > 1) sp.set("page", String(p));
    return `/search/results?${sp.toString()}`;
  }

  return (
    <div className="space-y-6">
      <div className="flex items-center justify-between">
        <div>
          <h1 className="text-2xl font-bold">
            {similarToUsername ? `Similar to ${similarToUsername}` : "Search Results"}
          </h1>
          <p className="text-text-muted mt-1">
            {total} result{total !== 1 ? "s" : ""} found
            {totalPages > 1 && ` - Page ${page} of ${totalPages}`}
          </p>
        </div>
        <div className="flex items-center gap-2">
          <ShareSearchButton />
          <SaveSearchButton
            searchParams={Object.fromEntries(
              Object.entries(params)
                .filter(([, v]) => typeof v === "string" && v !== "")
                .map(([k, v]) => [k, String(v)])
            )}
          />
          <Link
            href="/search"
            className="bg-surface-light hover:bg-surface text-text-muted px-4 py-2 rounded-lg transition-colors text-sm"
          >
            Modify Search
          </Link>
        </div>
      </div>

      {/* Active Filters */}
      {activeFilters.length > 0 && (
        <div className="flex flex-wrap gap-2">
          {activeFilters.map((f, i) => (
            <span
              key={i}
              className="bg-surface-light px-3 py-1 rounded-full text-sm text-text-muted"
            >
              {f}
            </span>
          ))}
        </div>
      )}

      {/* Results Grid */}
      <div className="grid grid-cols-2 md:grid-cols-3 lg:grid-cols-4 xl:grid-cols-6 gap-4">
        {escorts.map((escort, idx) => (
          <Link
            key={escort.id}
            href={`/view/${escort.id_aw ?? escort.id}`}
            className="bg-surface rounded-lg overflow-hidden hover:ring-1 hover:ring-primary transition-all group"
          >
            <div className="aspect-[3/4] bg-surface-light relative">
              {/* R14 A.7: next/image migration. The first 6 cards (first row
                  on desktop, ~3 rows on mobile) get `priority` so the LCP
                  candidate is a hinted image. Past the first row, lazy.
                  MediaImage falls through candidates on 404 and renders
                  `fallback` if every one fails (or there are none). */}
              <MediaImage
                srcs={escort.avatarMediaUrls.length > 0 ? escort.avatarMediaUrls : (escort.profile_photo ? [avatarUrl(escort.profile_photo, escort.id_aw)] : [])}
                alt={escort.username ?? ""}
                fill
                sizes="(max-width:640px) 50vw, (max-width:1024px) 33vw, 16vw"
                priority={idx < 6}
                loading={idx < 6 ? "eager" : "lazy"}
                className="object-cover group-hover:scale-105 transition-transform"
                fallback={
                  <div className="w-full h-full flex items-center justify-center text-text-muted">
                    <svg
                      className="w-12 h-12"
                      fill="none"
                      stroke="currentColor"
                      viewBox="0 0 24 24"
                    >
                      <path
                        strokeLinecap="round"
                        strokeLinejoin="round"
                        strokeWidth={1.5}
                        d="M16 7a4 4 0 11-8 0 4 4 0 018 0zM12 14a7 7 0 00-7 7h14a7 7 0 00-7-7z"
                      />
                    </svg>
                  </div>
                }
              />
              {/* Badges */}
              <div className="absolute top-2 left-2 flex gap-1">
                {escort.is_vip && (
                  <span className="bg-yellow-500 text-black text-xs font-bold px-1.5 py-0.5 rounded">
                    VIP
                  </span>
                )}
                {escort.is_verified && (
                  <span className="bg-green-500 text-white text-xs font-bold px-1.5 py-0.5 rounded">
                    Verified
                  </span>
                )}
              </div>
            </div>
            <div className="p-3">
              <p className="font-semibold truncate">
                {escort.username ?? "No name"}
              </p>
              <p className="text-text-muted text-xs truncate">
                {escort.characteristic?.gender?.name}
                {escort.characteristic?.age
                  ? ` - ${escort.characteristic.age.name}`
                  : ""}
              </p>
              <p className="text-text-muted text-xs truncate">
                {escort.city?.name}
                {escort.city && escort.country ? ", " : ""}
                {escort.country?.name}
              </p>
              {(() => {
                const rt = relativeTime(escort.lastonline_at);
                if (!rt) return null;
                const isRecent =
                  escort.lastonline_at &&
                  Date.now() - escort.lastonline_at.getTime() < 3600000;
                return (
                  <p className="text-xs flex items-center gap-1 mt-0.5">
                    <span
                      className={`inline-block w-1.5 h-1.5 rounded-full ${
                        isRecent ? "bg-green-500" : "bg-gray-500"
                      }`}
                    />
                    <span className="text-text-muted">Active {rt}</span>
                  </p>
                );
              })()}
            </div>
          </Link>
        ))}
      </div>

      {escorts.length === 0 && (
        <div className="text-center py-16 max-w-md mx-auto">
          <svg className="w-20 h-20 mx-auto mb-6 text-text-muted/30" fill="none" stroke="currentColor" viewBox="0 0 24 24">
            <path strokeLinecap="round" strokeLinejoin="round" strokeWidth={1} d="M21 21l-6-6m2-5a7 7 0 11-14 0 7 7 0 0114 0z" />
          </svg>
          <h3 className="text-xl font-semibold text-text mb-2">No results found</h3>
          <p className="text-text-muted mb-6">
            We couldn&apos;t find any escorts matching your filters. Try broadening your search criteria for more results.
          </p>
          <div className="flex flex-col sm:flex-row gap-3 justify-center mb-8">
            <Link
              href="/search"
              className="bg-primary hover:bg-primary-dark text-white px-6 py-2.5 rounded-lg font-medium transition-colors"
            >
              Modify Filters
            </Link>
            <Link
              href="/escorts"
              className="bg-surface-light hover:bg-surface text-text-muted px-6 py-2.5 rounded-lg transition-colors"
            >
              Clear All Filters
            </Link>
          </div>
          <div className="border-t border-surface-light pt-6">
            <p className="text-sm text-text-muted mb-3">Popular searches</p>
            <div className="flex flex-wrap gap-2 justify-center">
              {[
                { label: "Available Now", href: "/escorts/available-now" },
                { label: "Newest Escorts", href: "/escorts?sort=newest" },
                { label: "Most Popular", href: "/escorts?sort=popular" },
                { label: "Nearby", href: "/escorts/nearby" },
              ].map((item) => (
                <Link
                  key={item.href}
                  href={item.href}
                  className="bg-surface hover:bg-surface-light border border-surface-light text-text-muted hover:text-white px-4 py-2 rounded-full text-sm transition-colors"
                >
                  {item.label}
                </Link>
              ))}
            </div>
          </div>
        </div>
      )}

      {/* Pagination */}
      {totalPages > 1 && (
        <div className="flex items-center justify-center gap-2 pt-4">
          {page > 1 && (
            <Link
              href={pageUrl(page - 1)}
              className="bg-surface-light hover:bg-surface text-text-muted px-3 py-2 rounded-lg text-sm transition-colors"
            >
              Previous
            </Link>
          )}
          {Array.from({ length: totalPages }, (_, i) => i + 1)
            .filter(
              (p) =>
                p === 1 ||
                p === totalPages ||
                (p >= page - 2 && p <= page + 2)
            )
            .reduce<number[]>((acc, p) => {
              const last = acc[acc.length - 1];
              if (last !== undefined && p - last > 1) {
                acc.push(-1); // gap marker
              }
              acc.push(p);
              return acc;
            }, [])
            .map((p, i) =>
              p === -1 ? (
                <span key={`gap-${i}`} className="text-text-muted px-1">
                  ...
                </span>
              ) : (
                <Link
                  key={p}
                  href={pageUrl(p)}
                  className={`px-3 py-2 rounded-lg text-sm transition-colors ${
                    p === page
                      ? "bg-primary text-white"
                      : "bg-surface-light hover:bg-surface text-text-muted"
                  }`}
                >
                  {p}
                </Link>
              )
            )}
          {page < totalPages && (
            <Link
              href={pageUrl(page + 1)}
              className="bg-surface-light hover:bg-surface text-text-muted px-3 py-2 rounded-lg text-sm transition-colors"
            >
              Next
            </Link>
          )}
        </div>
      )}
      <CompareFloatingBar />
    </div>
  );
}
