import { NextRequest, NextResponse } from 'next/server';
import { getDb } from '@/lib/db';
import { fetchRestaurantsFromOverpass } from '@/lib/overpass';
import { resolveOsmImage, resolveUnsplashFallback } from '@/lib/image-resolver';
import { evaluateOpeningStatus, resolveRestaurantTimezone } from '@/lib/opening-hours';
import { expandCuisineTags } from '@/lib/cuisine';
import { buildAddressDisplay } from '@/lib/format-address';
import { hashStringSeed, seededShuffle } from '@/lib/shuffle';
import { ensureRadiusFunction, buildCoreRestaurantConditions } from '@/lib/restaurant-filter';

const OVERPASS_CACHE_TTL_MINUTES = 20;

function withBasePath(url?: string | null): string | undefined {
  if (!url) return undefined;
  return url;
}

function isLegacyExternalFallback(url?: string | null): boolean {
  return !!url && /^https?:\/\/images\.pexels\.com\//i.test(url);
}

function cuisineChipsOverlap(sessionChips: string[], sponsorChips: string[]): boolean {
  if (sessionChips.length === 0 || sponsorChips.length === 0) return true;
  const sessionExpanded = new Set(expandCuisineTags(sessionChips));
  return expandCuisineTags(sponsorChips).some((t) => sessionExpanded.has(t));
}

export async function GET(req: NextRequest, { params }: { params: Promise<{ code: string }> }) {
  const { code } = await params;
  const db = getDb();
  ensureRadiusFunction(db);
  const session = db.prepare('SELECT * FROM sessions WHERE code = ?').get(code) as any;
  if (!session) return NextResponse.json({ error: 'Session nicht gefunden' }, { status: 404 });

  const expandParam = Number(req.nextUrl.searchParams.get('expand') || 0);
  const sessionExpand = Number(session.expand_level || 0);
  const expandLevel = Math.max(expandParam, sessionExpand);
  const expandKm = Number.isFinite(expandLevel) && expandLevel > 0 ? Math.min(expandLevel, 3) * 5 : 0;
  const effectiveRadiusKm = (session.radius_km || 3) + expandKm;

  if (session.lat && session.lon) {
    const cacheCheck = db.prepare(`
      SELECT COUNT(*) as cnt FROM restaurants
      WHERE lat BETWEEN ? AND ? AND lon BETWEEN ? AND ?
        AND updated_at > datetime('now', '-${OVERPASS_CACHE_TTL_MINUTES} minutes')
    `).get(
      session.lat - 0.2, session.lat + 0.2, session.lon - 0.2, session.lon + 0.2
    ) as { cnt: number };

    if (cacheCheck.cnt === 0 || expandKm > 0) {
      try {
        const fresh = await fetchRestaurantsFromOverpass(session.lat, session.lon, effectiveRadiusKm * 1000);
        const insertStmt = db.prepare(`
          INSERT INTO restaurants
            (osm_id, name, lat, lon, address, postal_code, suburb, cuisine, price_rating, is_fast_food, phone, website,
             opening_hours_raw, diet_vegan, diet_vegetarian, diet_halal, diet_gluten_free)
          VALUES (@osmId, @name, @lat, @lon, @address, @postalCode, @suburb, @cuisine, @priceRating, @isFastFood, @phone, @website,
             @openingHoursRaw, @dietVegan, @dietVegetarian, @dietHalal, @dietGlutenFree)
          ON CONFLICT(osm_id) DO UPDATE SET
            name = excluded.name, cuisine = excluded.cuisine, price_rating = excluded.price_rating,
            address = excluded.address, postal_code = excluded.postal_code, suburb = excluded.suburb,
            phone = excluded.phone, website = excluded.website, opening_hours_raw = excluded.opening_hours_raw,
            diet_vegan = excluded.diet_vegan, diet_vegetarian = excluded.diet_vegetarian,
            diet_halal = excluded.diet_halal, diet_gluten_free = excluded.diet_gluten_free,
            updated_at = datetime('now')
        `);
        for (const r of fresh) {
          insertStmt.run({
            ...r,
            isFastFood: r.isFastFood ? 1 : 0,
            dietVegan: r.dietVegan ?? null,
            dietVegetarian: r.dietVegetarian ?? null,
            dietHalal: r.dietHalal ?? null,
            dietGlutenFree: r.dietGlutenFree ?? null,
          });
        }

        const needingOsmImage = fresh.filter((r) => r.imageTag || r.wikidataId || r.wikimediaCommons);
        await Promise.all(
          needingOsmImage.map(async (r) => {
            const row = db.prepare(
              'SELECT id, osm_image_url, image_source_used FROM restaurants WHERE osm_id = ?'
            ).get(r.osmId) as any;
            if (!row || row.osm_image_url || row.image_source_used) return;
            const imageUrl = await resolveOsmImage(r);
            db.prepare(
              'UPDATE restaurants SET osm_image_url = ?, image_source_used = ? WHERE id = ?'
            ).run(imageUrl, imageUrl ? 'osm' : 'none', row.id);
          })
        );
      } catch (e) {
        console.error('Overpass-Fetch fehlgeschlagen, nutze Cache:', e);
      }
    }
  }

  const { conditions, args } = buildCoreRestaurantConditions(session, effectiveRadiusKm);
  let query = 'SELECT * FROM restaurants WHERE 1=1' + conditions;

  const cuisineTags: string[] = session.cuisine_tags ? JSON.parse(session.cuisine_tags) : [];
  const cuisineAliases = expandCuisineTags(cuisineTags);

  query += ` AND id NOT IN (SELECT restaurant_id FROM vetos WHERE session_id = ?)`;
  args.push(session.id);

  query += ` AND id NOT IN (
    SELECT restaurant_id FROM sponsored_restaurants
    WHERE restaurant_id IS NOT NULL
      AND active = 1
      AND (active_from IS NULL OR active_from <= datetime('now'))
      AND (active_until IS NULL OR active_until >= datetime('now'))
  )`;

  const limit = expandKm > 0 ? 120 : 80;
  query += ` LIMIT ${limit}`;

  let restaurants = db.prepare(query).all(...args) as any[];

  // "Gibt es mehr als den aktuellen Stapel?" - der Client nutzt das, um den
  // Button "Weitere Karten anfordern" nur zu zeigen, wenn tatsaechlich noch
  // weitere Restaurants im Umkreis liegen (heuristisch: mehr Treffer als die
  // Kartenbegrenzung 40/60).
  const hasMore = restaurants.length > (expandKm > 0 ? 60 : 40);

  if (session.open_now_only) {
    restaurants = restaurants.filter((r) => {
      if (!r.opening_hours_raw) return true;
      const tz = resolveRestaurantTimezone(r.lat, r.lon);
      const status = evaluateOpeningStatus(r.opening_hours_raw, new Date(), tz);
      return status.isOpen !== false;
    });
  }

  restaurants = restaurants.slice(0, expandKm > 0 ? 60 : 40);

  const staleFallback = restaurants.filter((r) => !r.osm_image_url && !r.admin_image_url && isLegacyExternalFallback(r.fallback_image_url));
  const clearStmt = db.prepare("UPDATE restaurants SET fallback_image_url = NULL, image_source_used = NULL WHERE id = ?");
  for (const r of staleFallback) {
    r.fallback_image_url = null;
    r.image_source_used = null;
    clearStmt.run(r.id);
  }

  const missingImage = restaurants.filter((r) => !r.osm_image_url && !r.admin_image_url && !r.fallback_image_url);
  const updateFallbackStmt = db.prepare('UPDATE restaurants SET fallback_image_url = ?, image_source_used = ? WHERE id = ?');
  await Promise.all(
    missingImage.map(async (r) => {
      const url = await resolveUnsplashFallback(
        r.id,
        r.cuisine,
        cuisineAliases.length ? cuisineAliases : cuisineTags,
      );
      if (url) {
        r.fallback_image_url = url;
        r.image_source_used = 'fallback';
        updateFallbackStmt.run(url, 'fallback', r.id);
      }
    })
  );

  // BUGFIX: phone/website kommen jetzt per LEFT JOIN von der verknuepften
  // restaurants-Zeile (r.phone/r.website), statt fest auf null verdrahtet zu
  // sein - das betraf ALLE Sponsor-Karten, nicht nur verknuepfte. Ausserdem
  // opening_hours_raw jetzt mit COALESCE: eigener Sponsor-Wert hat Vorrang,
  // faellt aber auf den verknuepften Restaurant-Wert zurueck, wenn der Admin
  // das Feld leer gelassen hat.
  const sponsorQuery = `
    SELECT sp.*,
           r.phone AS linked_phone,
           r.website AS linked_website,
           r.opening_hours_raw AS linked_opening_hours_raw
    FROM sponsored_restaurants sp
    LEFT JOIN restaurants r ON r.id = sp.restaurant_id
    WHERE sp.active = 1
      AND (sp.active_from IS NULL OR sp.active_from <= datetime('now'))
      AND (sp.active_until IS NULL OR sp.active_until >= datetime('now'))
      AND (sp.city IS NULL OR sp.city LIKE ?)
    ORDER BY sp.priority DESC
  `;
  const sponsors = db.prepare(sponsorQuery).all(`%${session.city}%`) as any[];

  const matchingSponsors = sponsors.filter((s) => {
    const sponsorTags: string[] = s.filter_cuisine_tags ? JSON.parse(s.filter_cuisine_tags) : [];
    return cuisineChipsOverlap(cuisineTags, sponsorTags);
  });

  const sponsoredCards = matchingSponsors.map((s) => ({
    id: `sponsor-${s.id}`,
    name: s.name,
    lat: s.lat,
    lon: s.lon,
    address: buildAddressDisplay(s.address, s.postal_code, s.city),
    city: s.city,
    postal_code: s.postal_code,
    cuisine: s.filter_cuisine_tags ? JSON.parse(s.filter_cuisine_tags).join(', ') : null,
    price_rating: null,
    is_fast_food: 0,
    phone: s.linked_phone ?? null,
    website: s.linked_website ?? null,
    opening_hours_raw: s.opening_hours_raw ?? s.linked_opening_hours_raw ?? null,
    osm_image_url: null,
    admin_image_url: withBasePath(s.image_url),
    fallback_image_url: null,
    sponsored: true,
    discount_text: s.discount_text,
    disclaimer_text: s.disclaimer_text,
    priority: s.priority,
    reservation_url: s.reservation_url,
    lieferando_url: s.lieferando_url,
  }));

  let combined: any[] = [...restaurants];
  if (expandKm === 0) {
    const SPACING = 4;
    sponsoredCards.forEach((sponsorCard, i) => {
      const insertPos = Math.min(i * SPACING, combined.length);
      combined.splice(insertPos, 0, sponsorCard);
    });
  }

  // Deterministisches Mischen pro Teilnehmer: alle sehen dieselben Karten,
  // aber in unterschiedlicher Reihenfolge. Der Seed kommt als Query-Param
  // (participantId/anonymousId, siehe session-client). Ohne Seed bleibt die
  // bisherige Reihenfolge (Sponsoren an festen Slots) erhalten.
  const seedParam = req.nextUrl.searchParams.get('seed');
  if (seedParam) {
    combined = seededShuffle(combined, hashStringSeed(seedParam));
  }

  const normalized = combined.map((r) => ({
    ...r,
    city: r.city ?? session.city,
    osm_image_url: withBasePath(r.osm_image_url),
    admin_image_url: withBasePath(r.admin_image_url),
    fallback_image_url: withBasePath(r.fallback_image_url),
  }));

  return NextResponse.json({ restaurants: normalized, hasMore });
}
