/**
 * Edge Manifest Generator
 *
 * Produces the same JSON format as mizan-django. One Edge Worker.
 * Two backend languages. Same manifest.
 */

import type { EdgeManifest } from './types'
import { getAllFunctions, getContextGroups, getContextParamNames } from './registry'

// Both camelCase and snake_case forms included for cross-language matching.
// Wire format is snake_case (protocol rule); camelCase is the TS-local convention.
const USER_SCOPED_PARAMS = new Set(['userId', 'user', 'ownerId', 'accountId', 'user_id', 'owner_id', 'account_id'])

export function generateManifest(baseUrl = '/api/mizan'): EdgeManifest {
    const groups = getContextGroups()
    const allFunctions = getAllFunctions()
    const manifest: EdgeManifest = { version: 1, contexts: {}, mutations: {} }

    // Contexts
    for (const [ctxName, fnNames] of Object.entries(groups)) {
        const paramNames = new Set<string>()
        const functions: Array<{ name: string; path: 'rpc' | 'view'; route?: string; methods?: string[] }> = []
        const pageRoutes: string[] = []

        for (const fnName of fnNames) {
            const entry = allFunctions.get(fnName)
            if (!entry) continue

            for (const p of entry.params) paramNames.add(p.name)

            const fnEntry: any = { name: fnName, path: entry.viewPath ? 'view' : 'rpc' }
            if (entry.route) {
                fnEntry.route = entry.route
                fnEntry.methods = entry.methods || ['GET']
                pageRoutes.push(entry.route)
            }
            if (entry.rev !== undefined && entry.rev !== 0) fnEntry.rev = entry.rev
            if (entry.cache !== undefined && entry.cache !== true) fnEntry.cache = entry.cache
            functions.push(fnEntry)
        }

        const sortedParams = [...paramNames].sort()
        const userScoped = [...paramNames].some(p => USER_SCOPED_PARAMS.has(p))

        const ctxEntry: EdgeManifest['contexts'][string] = {
            functions,
            endpoints: [`${baseUrl}/ctx/${ctxName}/`],
            params: sortedParams,
            user_scoped: userScoped,
            render_strategy: userScoped ? 'dynamic_cached' : 'psr',
        }

        if (pageRoutes.length > 0) {
            ctxEntry.page_routes = pageRoutes
        }

        manifest.contexts[ctxName] = ctxEntry
    }

    // Mutations
    for (const [fnName, entry] of allFunctions) {
        if (!entry.affects) continue

        const affectedContexts = [...new Set(entry.affects.map(a => a.name))]

        // Auto-scoped params
        const fnParamNames = new Set(entry.params.map(p => p.name))
        const autoScoped: string[] = []
        for (const ctxName of affectedContexts) {
            const ctxParams = getContextParamNames(ctxName)
            for (const p of fnParamNames) {
                if (ctxParams.has(p) && !autoScoped.includes(p)) {
                    autoScoped.push(p)
                }
            }
        }

        const mutation: EdgeManifest['mutations'][string] = {
            affects: affectedContexts,
        }
        if (autoScoped.length > 0) mutation.auto_scoped_params = autoScoped.sort()
        if (entry.private) mutation.private = true
        if (entry.route) {
            mutation.route = entry.route
            mutation.methods = entry.methods || ['POST']
        }

        manifest.mutations[fnName] = mutation
    }

    return manifest
}
