import { t as validateIdentifier } from "./validate-VPnKoIzW.mjs";
import { sql } from "kysely";

//#region src/api/handlers/seo.ts
/**
* SEO Handlers
*
* Business logic for sitemap generation and robots.txt.
*/
/** Maximum entries per sitemap (per spec) */
const SITEMAP_MAX_ENTRIES = 5e4;
/** Matches a trailing timezone designator (`Z` or `±HH`, `±HHMM`, `±HH:MM`). */
const TZ_SUFFIX_RE = /([zZ]|[+-]\d{2}(:?\d{2})?)$/;
/**
* Normalize a stored timestamp to W3C Datetime (ISO 8601) for sitemaps.
*
* `updated_at` is not guaranteed to be ISO: the column default is
* `datetime('now')` on SQLite and `CURRENT_TIMESTAMP` on Postgres, both of
* which store a space-separated `YYYY-MM-DD HH:MM:SS` string (and imported
* content can carry other shapes). The sitemap `<lastmod>` field requires
* W3C Datetime, and Google Search Console rejects the space-separated form
* as "Invalid date". Normalize defensively, assuming UTC when no offset is
* present (matches SQLite's `datetime('now')`). Valid date strings are
* normalized to UTC ISO 8601; unparseable values are returned as-is.
*/
function toW3CDate(value) {
	if (!value) return value;
	let normalized = value.trim();
	if (normalized.includes(" ") && !normalized.includes("T")) normalized = normalized.replace(" ", "T");
	if (!TZ_SUFFIX_RE.test(normalized)) normalized += "Z";
	const parsed = Date.parse(normalized);
	return Number.isNaN(parsed) ? value : new Date(parsed).toISOString();
}
/**
* Collect all published, indexable content across SEO-enabled collections
* for sitemap generation, grouped by collection.
*
* Only includes content from collections with `has_seo = 1`.
* Excludes content with `seo_no_index = 1` in the `_emdash_seo` table.
*
* Returns raw data grouped per collection. The caller (route) is
* responsible for building absolute URLs — this handler does NOT
* assume a URL structure.
*/
async function handleSitemapData(db, collectionSlug) {
	try {
		let query = db.selectFrom("_emdash_collections").select(["slug", "url_pattern"]).where("has_seo", "=", 1);
		if (collectionSlug) query = query.where("slug", "=", collectionSlug);
		const collections = await query.execute();
		const result = [];
		for (const col of collections) {
			try {
				validateIdentifier(col.slug, "collection slug");
			} catch {
				console.warn(`[SITEMAP] Skipping collection with invalid slug: ${col.slug}`);
				continue;
			}
			const tableName = `ec_${col.slug}`;
			try {
				const rows = await sql`
					SELECT c.slug, c.id, c.updated_at, c.locale, c.translation_group, s.seo_image
					FROM ${sql.ref(tableName)} c
					LEFT JOIN _emdash_seo s
						ON s.collection = ${col.slug}
						AND s.content_id = c.id
					WHERE c.status = 'published'
					AND c.deleted_at IS NULL
					AND (s.seo_no_index IS NULL OR s.seo_no_index = 0)
					ORDER BY c.updated_at DESC
					LIMIT ${SITEMAP_MAX_ENTRIES}
				`.execute(db);
				if (rows.rows.length === 0) continue;
				const entries = [];
				for (const row of rows.rows) entries.push({
					id: row.id,
					slug: row.slug,
					updatedAt: toW3CDate(row.updated_at),
					locale: row.locale,
					translationGroup: row.translation_group,
					image: row.seo_image ?? null
				});
				result.push({
					collection: col.slug,
					urlPattern: col.url_pattern,
					lastmod: toW3CDate(rows.rows[0].updated_at),
					entries
				});
			} catch (err) {
				console.warn(`[SITEMAP] Failed to query collection "${col.slug}":`, err);
				continue;
			}
		}
		return {
			success: true,
			data: { collections: result }
		};
	} catch (error) {
		console.error("[SITEMAP_ERROR]", error);
		return {
			success: false,
			error: {
				code: "SITEMAP_ERROR",
				message: "Failed to generate sitemap data"
			}
		};
	}
}

//#endregion
export { handleSitemapData as t };
//# sourceMappingURL=seo-89-53DDs.mjs.map