import { Controller, Get, Query, ServiceUnavailableException } from "@nestjs/common";
import { Public } from "./common/decorators/public.decorator";
import { PrismaService } from "./common/prisma/prisma.service";
import { defaultCurrency, maximumUploadImages } from "./common/service-fields";
import { Throttle } from "@nestjs/throttler";
import { normalizeCitySearch, searchSyrianCities } from "./common/syrian-cities";

@Controller()
export class PublicConfigController {
  constructor(private readonly prisma: PrismaService) {}
  private citiesCache = new Map<string, { expires: number; data: unknown[] }>();

  @Public() @Get("settings/public")
  async settings() {
    const [currency, maxUploadImages, rows] = await Promise.all([
      defaultCurrency(this.prisma),
      maximumUploadImages(this.prisma),
      this.prisma.appSetting.findMany({ where: { key: { in: ["maintenanceMode", "registrationEnabled", "chatEnabled", "supportEmail", "supportPhone"] } } }),
    ]);
    const configured = Object.fromEntries(rows.map(row => [row.key, row.value]));
    return {
      currency,
      maxUploadImages,
      maintenanceMode: configured.maintenanceMode === true,
      registrationEnabled: configured.registrationEnabled !== false,
      chatEnabled: configured.chatEnabled !== false,
      supportEmail: typeof configured.supportEmail === "string" ? configured.supportEmail : "support@mawjod.app",
      supportPhone: typeof configured.supportPhone === "string" ? configured.supportPhone : "",
    };
  }

  @Public() @Get("locations/cities")
  @Throttle({ default: { limit: 20, ttl: 60_000, blockDuration: 60_000 } })
  async cities(@Query("search") search = "") {
    if (search.trim().length < 2) return [];
    const term = search.trim().slice(0, 100), cacheKey = normalizeCitySearch(term), cached = this.citiesCache.get(cacheKey);
    if (cached && cached.expires > Date.now()) return cached.data;
    const localMatches = searchSyrianCities(term, 10);
    if (localMatches.length) {
      this.citiesCache.set(cacheKey, { expires: Date.now() + 86_400_000, data: localMatches });
      return localMatches;
    }
    try {
      const key = process.env.GEOCODING_API_KEY;
      const url = new URL(`https://${key ? "customer-" : ""}geocoding-api.open-meteo.com/v1/search`);
      url.search = new URLSearchParams({ name: term, count: "10", language: "ar", countryCode: "SY", ...(key ? { apikey: key } : {}) }).toString();
      const response = await fetch(url, { signal: AbortSignal.timeout(8000) });
      if (!response.ok) throw new Error("geocoder_unavailable");
      const result = await response.json() as { results?: { id: number; name: string; country?: string; country_code?: string; admin1?: string; latitude: number; longitude: number }[] };
      const remoteMatches = (result.results ?? [])
        .filter(city => city.country_code === "SY")
        .map(city => ({ id: String(city.id), name: city.name, country: city.country, region: city.admin1, latitude: city.latitude, longitude: city.longitude }));
      const seen = new Set<string>();
      const data = remoteMatches.filter(city => {
        const key = `${normalizeCitySearch(city.name)}:${normalizeCitySearch(city.region ?? "")}`;
        if (seen.has(key)) return false;
        seen.add(key);
        return true;
      }).slice(0, 10);
      if (this.citiesCache.size > 500) this.citiesCache.clear();
      this.citiesCache.set(cacheKey, { expires: Date.now() + 86_400_000, data }); return data;
    } catch {
      throw new ServiceUnavailableException("city_search_unavailable");
    }
  }
}
