const { apiSsrBaseURL, ssrPublishCheckCollections } = require("../config")

/**
 * api request via server, cache result in context.ssrCache
 * should be elimated in client code via tree shaking
 *
 * @param {string} cacheKey
 * @param {string} endpoint
 * @param {string} query
 * @param {ApiOptions} options
 * @returns {ApiResult<any>}
 */
function ssrRequest(cacheKey, endpoint, query, options) {
    let url = endpoint + (query ? "?" + query : "")

    // track which collections/entries contribute to this SSR render
    // endpoint may contain path segments (e.g. "content/abc123") or query strings
    const collectionName = endpoint.split("?")[0].split("/")[0]

    if (ssrPublishCheckCollections?.includes(endpoint)) {
        // @ts-ignore
        let validUntil = context.ssrCacheValidUntil

        // check in db for publish date to invalidate cache
        const _optionsPublishSearch = Object.assign(
            {},
            { filter: options?.filter },
            {
                selector: { publication: 1 },
                projection: null,
            }
        )
        const publishSearch = context.db.find(endpoint, _optionsPublishSearch)
        publishSearch?.forEach((item) => {
            const publicationFrom = item.publication?.from ? new Date(item.publication.from.unixMilli()) : null
            const publicationTo = item.publication?.to ? new Date(item.publication.to.unixMilli()) : null

            if (publicationFrom && publicationFrom > new Date()) {
                // entry has a publish date that is further in in the future than current, set global validUntil
                if (validUntil == null || validUntil > publicationFrom) {
                    validUntil = publicationFrom
                }
            }
            if (publicationTo && publicationTo > new Date()) {
                // entry has a unpublish date that is further in in the future than current, set global validUntil
                if (validUntil == null || validUntil > publicationTo) {
                    validUntil = publicationTo
                }
            }
        })
        // @ts-ignore
        context.ssrCacheValidUntil = validUntil
    }

    // console.log("############ FETCHING ", apiSsrBaseURL + url)

    const response = context.http.fetch(apiSsrBaseURL + url, {
        method: options.method,
        headers: options.headers,
    })

    // console.log(JSON.stringify(response.headers, null, 2))

    const json = response.body.json()
    const count = parseInt(response.headers["X-Results-Count"] || "0")

    // json is go data structure and incompatible with js, so we need to convert it
    const r = { data: JSON.parse(JSON.stringify(json)), count: count }

    // track dependencies: "col:id" for single-entry, "col:*" for list queries
    // @ts-ignore – dynamic property set by get_read.js
    if (context.ssrDeps) {
        let entryId = null

        if (!Array.isArray(r.data) && r.data && r.data.id) {
            // direct ID lookup (COLLECTION/ID) – API returned single object
            entryId = r.data.id
        } else if (options?.limit === 1 && Array.isArray(r.data) && r.data.length === 1 && r.data[0] && r.data[0].id) {
            // filter-based detail query with limit:1
            entryId = r.data[0].id
        }

        if (entryId) {
            // @ts-ignore
            context.ssrDeps[collectionName + ":" + entryId] = true
        } else {
            // list query – any change to this collection affects this page
            // @ts-ignore
            context.ssrDeps[collectionName + ":*"] = true
        }

        // --- EXTENSION: Track dependencies from lookup and aggregate ---
        // Both `lookup` and `aggregate` parameters can inject data from other collections.
        // We must invalidate the SSR cache if any of those referenced collections change.
        if (options && options.lookup) {
            /** @type {any[]} */
            let lookups = []
            if (typeof options.lookup === "string") {
                const trimmed = options.lookup.trim()
                if (trimmed.startsWith("[") || trimmed.startsWith("{")) {
                    try {
                        const parsed = JSON.parse(trimmed)
                        lookups = Array.isArray(parsed) ? parsed : [parsed]
                    } catch (e) {
                        lookups = options.lookup.split(",")
                    }
                } else {
                    lookups = options.lookup.split(",")
                }
            } else if (Array.isArray(options.lookup)) {
                lookups = options.lookup
            } else if (typeof options.lookup === "object" && options.lookup !== null) {
                lookups = [options.lookup]
            }

            for (const l of lookups) {
                if (typeof l === "object" && l !== null && l.collection) {
                    // @ts-ignore
                    context.ssrDeps[l.collection + ":*"] = true
                } else if (typeof l === "string") {
                    // format: "fieldPath:collectionName"
                    const parts = l.split(":")
                    if (parts.length > 1) {
                        const targetCollection = parts[parts.length - 1]
                        // @ts-ignore
                        context.ssrDeps[targetCollection + ":*"] = true
                    }
                }
            }
        }
        const rawAggregate = (options && options.aggregate) || (options && options.params && options.params.aggregate)
        if (rawAggregate) {
            /** @type {any[]} */
            let aggregates = []
            if (typeof rawAggregate === "string") {
                const trimmed = rawAggregate.trim()
                if (trimmed.startsWith("[") || trimmed.startsWith("{")) {
                    try {
                        const parsed = JSON.parse(trimmed)
                        aggregates = Array.isArray(parsed) ? parsed : [parsed]
                    } catch (e) {
                        aggregates = rawAggregate.split(",")
                    }
                } else {
                    aggregates = rawAggregate.split(",")
                }
            } else if (Array.isArray(rawAggregate)) {
                aggregates = rawAggregate
            } else if (typeof rawAggregate === "object" && rawAggregate !== null) {
                aggregates = [rawAggregate]
            }

            for (const a of aggregates) {
                if (typeof a === "object" && a !== null && a.collection) {
                    // @ts-ignore
                    context.ssrDeps[a.collection + ":*"] = true
                } else if (typeof a === "string") {
                    const parts = a.split(":")
                    if (parts.length > 0) {
                        const targetCollection = parts[0]
                        // @ts-ignore
                        context.ssrDeps[targetCollection + ":*"] = true
                    }
                }
            }
        }
        // --- END EXTENSION ---
    }

    // @ts-ignore
    context.ssrCache[cacheKey] = r

    return r
}

module.exports = {
    ssrRequest,
}
