import { getAgent, getAgentMap } from "../agents.js";
import { allBuiltInShapes } from '../builtin-schema.js';
import { dereferenceObjectSchema, dereferenceSchema, getQuery, normalizePropertyRef, normalizeRefExpression, parsePropertyRef, parseRef, refItemToNamespacedShapeName } from "../refs.js";
import { getShape } from "../util/shapes.js";
import { createAgent } from "./agent.js";
import { createQuery } from './query.js';
import { createShape } from './shape.js';
export class ProjectSchemaImpl {
    #projectSchemaJson;
    #layers;
    #shapeCache;
    #queryCache;
    #agentCache;
    constructor(projectSchemaJson, layers) {
        this.#projectSchemaJson = projectSchemaJson;
        this.#layers = layers;
        this.#shapeCache = new Map();
        this.#queryCache = new Map();
        this.#agentCache = new Map();
    }
    getShape(rawShapeRef, options) {
        const shapeRef = normalizeRefExpression(this.#projectSchemaJson, rawShapeRef);
        let shape = this.#shapeCache.get(shapeRef);
        if (!shape) {
            const refItem = parseRef(this.#projectSchemaJson, rawShapeRef);
            if (refItem) {
                const namespacedName = refItemToNamespacedShapeName(this.#projectSchemaJson, refItem);
                let shapeJson = getShape(this.#projectSchemaJson, namespacedName);
                if (!shapeJson && options?.includeAllBuiltIns) {
                    shapeJson = getShape({ shapes: allBuiltInShapes }, namespacedName);
                }
                if (shapeJson) {
                    shape = createShape(this, refItem, shapeJson);
                    this.#shapeCache.set(shapeRef, shape);
                }
                else if (refItem.layerId !== 'local') {
                    const layerSchema = this.#layers[refItem.layerId]?.schema;
                    if (layerSchema) {
                        shapeJson = getShape(layerSchema, namespacedName);
                        if (shapeJson) {
                            shape = createShape(this, refItem, shapeJson);
                            this.#shapeCache.set(shapeRef, shape);
                        }
                    }
                }
            }
        }
        return shape;
    }
    getQuery(rawQueryRef) {
        const queryRef = normalizePropertyRef(rawQueryRef);
        let query = this.#queryCache.get(queryRef);
        if (!query) {
            const queryEntry = getQuery(this.#projectSchemaJson, queryRef);
            if (queryEntry) {
                query = createQuery(this, queryEntry);
                this.#queryCache.set(queryRef, query);
            }
            else {
                const parsedRef = parsePropertyRef(queryRef);
                if (parsedRef && parsedRef.layerId !== 'local') {
                    const layerSchema = this.#layers[parsedRef.layerId]?.schema;
                    if (layerSchema) {
                        const queryEntry = getQuery(layerSchema, parsedRef.propertyName);
                        if (queryEntry) {
                            query = createQuery(this, { ...queryEntry, ref: parsedRef });
                            this.#queryCache.set(queryRef, query);
                        }
                    }
                }
            }
        }
        return query;
    }
    ensureQuery(ref) {
        const query = this.getQuery(ref);
        if (!query) {
            throw new Error(`Query "${ref}" not found`);
        }
        return query;
    }
    getLocalQueries() {
        return Object.keys(this.#projectSchemaJson.queries).map((queryName) => this.ensureQuery(`Query.${queryName}`));
    }
    getLocalMutations() {
        return Object.keys(this.#projectSchemaJson.mutations).map((queryName) => this.ensureQuery(`Mutation.${queryName}`));
    }
    getAgents() {
        const agents = getAgentMap(this.#projectSchemaJson);
        return agents ? Object.keys(agents).map((name) => this.ensureAgent(name)) : [];
    }
    ensureAgent(name) {
        const agent = this.getAgent(name);
        if (!agent) {
            throw new Error(`Agent "${name}" not found`);
        }
        return agent;
    }
    getAgent(name) {
        let agent = this.#agentCache.get(name);
        if (!agent) {
            const agentJson = getAgent(this.#projectSchemaJson, name);
            if (agentJson) {
                agent = createAgent(this, name, agentJson);
                this.#agentCache.set(name, agent);
            }
        }
        return agent;
    }
    dereferenceSchema(propertySchema) {
        return dereferenceSchema(this.#projectSchemaJson, propertySchema);
    }
    dereferenceObjectSchema(propertySchema) {
        return dereferenceObjectSchema(this.#projectSchemaJson, propertySchema);
    }
    get json() {
        return this.#projectSchemaJson;
    }
}
