---
/**
 * /whats-left — faceted browse shell scoped to the urgency set (expiring <24h OR stock 1–9).
 *
 * SSR seeds top-6 deals + facet counts for DealsBrowserIsland hydration.
 * The 24h expiry boundary may drift slightly between SSR and client mount — client fetch is authoritative.
 */
import { env } from '@/server/env';

import PublicAppShell from '@/layouts/PublicAppShell.astro';
import type { PreloadImageEntry } from '@/components/ui/primitives/Image';
import { DealsBrowserIsland } from '@/features/deals-browser/DealsBrowserIsland';
import type { DealBrowseRow } from '@/components/ui/domain/DealsBrowser/types';
import { he } from '@/lib/i18n/he';
import { en } from '@/lib/i18n/en';
import { resolveCategorySlug, resolveTagSlugs } from '@/server/db/queries/slug-resolve';
import { listActiveFiltered } from '@/server/catalog/public/browse';
import { queryHotDealIds } from '@/server/catalog/public/hot';
import {
  getDealsFacetCounts,
  type DealsFacetCounts,
  HISTO_BUCKETS,
} from '@/server/db/queries/deals-facets';
import { getFilterData } from '@/server/db/queries/filter-data';
import { dehydrateForIsland } from '@/lib/query/ssr';
import { buildFacetKey } from '@/lib/deals/facet-key';
import { makeWhatsLeftCodec } from '@/lib/url/codecs/whatsLeftCodec';
import type { DealsUrlState } from '@/lib/url/codecs/dealsCodec';

const PAGE_SIZE = 12;

type Props = {
  locale: 'he' | 'en';
};

const { locale } = Astro.props as Props;
const dict = locale === 'en' ? en : he;
const BASE_URL = env.PUBLIC_SITE_URL;
const url = new URL(Astro.request.url);
const canonical = `${BASE_URL}/whats-left`;
const currentPath = locale === 'en' ? '/en/whats-left' : '/whats-left';
const localeAlternates = [
  { locale: 'he', url: `${BASE_URL}/whats-left` },
  { locale: 'en', url: `${BASE_URL}/en/whats-left` },
  { locale: 'x-default', url: `${BASE_URL}/whats-left` },
];

const db = Astro.locals.services.db;

let state: DealsUrlState = {
  type: undefined,
  catSlug: undefined,
  tagSlugs: [],
  page: 1,
  sort: 'ending-soon',
  priceMin: 0,
  priceMax: 0,
};
let resolvedCatId: string | undefined;
let resolvedTagIds: string[] = [];
let rows: Awaited<ReturnType<typeof listActiveFiltered>>['rows'] = [];
let initialTotal = 0;
let initialDeals: DealBrowseRow[];
let initialFacets: DealsFacetCounts | undefined;
let priceCeiling = 2000;
let priceFloor = 0;
let hotIds: string[] | undefined;

if (db) {
  try {
    const filterData = await getFilterData(db, locale);
    priceCeiling = filterData.otherFilters.priceCeiling;
    priceFloor = filterData.otherFilters.priceFloor;

    state = makeWhatsLeftCodec({ locale, priceCeiling }).parse({
      pathname: url.pathname,
      search: url.search,
    });

    if (state.catSlug) {
      const cat = await resolveCategorySlug(db, state.catSlug);
      if (!cat) return new Response('Not Found', { status: 404 });
      resolvedCatId = cat.id;
    }

    if (state.tagSlugs.length > 0) {
      const tags = await resolveTagSlugs(db, state.tagSlugs);
      if (!tags) return new Response('Not Found', { status: 404 });
      resolvedTagIds = tags.ids;
    }

    const listPriceMin = state.priceMin > priceFloor ? state.priceMin : undefined;
    const listPriceMax =
      state.priceMax > 0 && state.priceMax < priceCeiling ? state.priceMax : undefined;
    hotIds = await queryHotDealIds(db, '24h', 50);

    const [r, facets] = await Promise.all([
      listActiveFiltered(db, {
        whatsLeft: true,
        type: state.type,
        categoryIds: resolvedCatId ? [resolvedCatId] : undefined,
        tagIds: resolvedTagIds.length > 0 ? resolvedTagIds : undefined,
        priceMin: listPriceMin,
        priceMax: listPriceMax,
        sort: state.sort,
        page: state.page,
        limit: 6,
        offset: (state.page - 1) * PAGE_SIZE,
        hotIds,
        withImages: true,
      }),
      getDealsFacetCounts(db, {
        whatsLeft: true,
        catId: resolvedCatId,
        type: state.type || undefined,
        tagIds: resolvedTagIds.length > 0 ? resolvedTagIds : undefined,
        priceMin: listPriceMin,
        priceMax: listPriceMax,
        tagLimit: 30,
        histoMin: priceFloor,
        histoMax: priceCeiling,
        histoBuckets: HISTO_BUCKETS,
      }).catch(() => undefined),
    ]);

    rows = r.rows;
    initialTotal = r.total;
    initialFacets = facets;
  } catch {
    // Graceful degradation: client fetches on mount
    rows = [];
  }
}

initialDeals = rows.map((d) => ({
  id: d.id,
  title: d.title,
  originalPrice: d.maxDiscountPercent
    ? Math.round((parseFloat(d.minPrice ?? '0') / (1 - d.maxDiscountPercent / 100)) * 100) / 100
    : parseFloat(d.minPrice ?? '0'),
  discountedPrice: parseFloat(d.minPrice ?? '0'),
  discountPercent: d.maxDiscountPercent ?? null,
  imageUrl: d.primaryImageUrl ?? null,
  vendorId: d.vendorId,
  vendorName: d.vendorBusinessName ?? '',
  vendorAvatarUrl: d.vendorLogoUrl ?? null,
  city: '',
  dealType: d.dealType,
  windowEnd: d.windowEnd?.toISOString() ?? null,
  stockRemaining: d.stockRemaining ?? 0,
  stockTotal: d.stockRemaining ?? 0,
  isHotDeal: hotIds?.includes(d.id) ?? false,
  soldCount: 0,
  minPrice: d.minPrice ?? null,
  maxPrice: d.maxPrice ?? null,
  maxDiscountPercent: d.maxDiscountPercent ?? null,
  axesCount: d.axesCount,
  defaultSkuId: d.defaultSkuId,
  qtyTierTop: d.qtyTierTop ?? null,
  skuCount: d.skuCount ?? 1,
  heSlug: d.heSlug ?? undefined,
}));

const facetKeyPriceMin = state.priceMin > priceFloor ? state.priceMin : undefined;
const facetKeyPriceMax =
  state.priceMax > 0 && state.priceMax < priceCeiling ? state.priceMax : undefined;

const { dehydratedState } = await dehydrateForIsland(
  initialFacets
    ? [
        async (qc) => {
          qc.setQueryData(
            buildFacetKey({
              catSlug: state.catSlug,
              type: state.type ?? '',
              tagSlugs: state.tagSlugs,
              priceMin: facetKeyPriceMin,
              priceMax: facetKeyPriceMax,
              preset: 'whats-left',
            }),
            initialFacets,
          );
        },
      ]
    : [],
);

const WHATS_LEFT_FILTER_PARAM_KEYS = [
  'type',
  'cat',
  'tags',
  'priceMin',
  'priceMax',
  'sort',
  'page',
] as const;
const noindex = WHATS_LEFT_FILTER_PARAM_KEYS.some((key) => Astro.url.searchParams.has(key));

const itemListJsonLd: Record<string, unknown> = {
  '@context': 'https://schema.org',
  '@type': 'ItemList',
  name: dict.seo_pages.whats_left_title,
  url: canonical,
  numberOfItems: initialTotal,
  itemListElement: initialDeals.map((deal, index) => {
    const dealUrl = deal.heSlug
      ? locale === 'en'
        ? `${BASE_URL}/en/deals/${deal.heSlug}`
        : `${BASE_URL}/deals/${deal.heSlug}`
      : `${BASE_URL}/deals`;
    return {
      '@type': 'ListItem',
      position: index + 1,
      url: dealUrl,
      name: deal.title,
    };
  }),
};

Astro.response.headers.set(
  'Cache-Control',
  noindex
    ? 'public, max-age=0, s-maxage=30, stale-while-revalidate=3600'
    : 'public, max-age=0, s-maxage=300, stale-while-revalidate=3600',
);

const preloadImages: PreloadImageEntry[] = initialDeals
  .filter((d) => d.imageUrl)
  .slice(0, 6)
  .map((d, idx) => ({
    src: d.imageUrl as string,
    variant: 'card',
    fetchpriority: idx === 0 ? 'high' : undefined,
  }));
---

<PublicAppShell
  title={dict.seo_pages.whats_left_title}
  description={dict.seo_pages.whats_left_desc}
  canonical={canonical}
  {locale}
  noindex={noindex}
  jsonLd={itemListJsonLd}
  {localeAlternates}
  currentPath={currentPath}
  preloadImages={preloadImages}
  ogImage={`${BASE_URL}/brand/og-default.webp`}
  geoRegion="IL"
  geoPosition="31.0461;34.8516"
>
  <main id="main">
    <h1 class="sr-only">{dict.whats_left_page.heading}</h1>
    <DealsBrowserIsland
      client:idle
      locale={locale}
      initialUrlState={state}
      initialDeals={initialDeals}
      initialTotal={initialTotal}
      codecKind="whats-left"
      apiPreset="whats-left"
      defaultSort="ending-soon"
      emptyState={dict.whats_left_page.empty}
      gridLabel={dict.whats_left_page.grid_label}
      priceCeiling={priceCeiling}
      priceFloor={priceFloor}
      dehydratedState={dehydratedState}
    />
  </main>
</PublicAppShell>
