// Copyright Metatype OÜ, licensed under the Mozilla Public License Version 2.0.
// SPDX-License-Identifier: MPL-2.0
import "./_dnt.polyfills.js";
import * as t from "./types.js";
import { core } from "./gen/typegraph_core_2.js";
import { caller, fromFileUrl } from "./deps/mod.js";
import { serializeFromParentInjection, serializeGenericInjection, serializeStaticInjection, } from "./utils/injection_utils.js";
import { wit_utils, } from "./wit.js";
import { getPolicyChain } from "./types.js";
import { Manager } from "./tg_manage.js";
import { log } from "./io.js";
import { hasCliEnv } from "./envs/cli.js";
import process from "process";
export class ApplyFromArg {
    constructor(name, type) {
        Object.defineProperty(this, "name", {
            enumerable: true,
            configurable: true,
            writable: true,
            value: name
        });
        Object.defineProperty(this, "type", {
            enumerable: true,
            configurable: true,
            writable: true,
            value: type
        });
    }
}
export class ApplyFromStatic {
    // deno-lint-ignore no-explicit-any
    constructor(value) {
        Object.defineProperty(this, "value", {
            enumerable: true,
            configurable: true,
            writable: true,
            value: value
        });
    }
}
export class ApplyFromSecret {
    constructor(key) {
        Object.defineProperty(this, "key", {
            enumerable: true,
            configurable: true,
            writable: true,
            value: key
        });
    }
}
export class ApplyFromContext {
    constructor(key, type) {
        Object.defineProperty(this, "key", {
            enumerable: true,
            configurable: true,
            writable: true,
            value: key
        });
        Object.defineProperty(this, "type", {
            enumerable: true,
            configurable: true,
            writable: true,
            value: type
        });
    }
}
export class ApplyFromParent {
    constructor(typeName) {
        Object.defineProperty(this, "typeName", {
            enumerable: true,
            configurable: true,
            writable: true,
            value: typeName
        });
    }
}
const InjectionSource = {
    asArg: (name, type) => new ApplyFromArg(name ?? null, type?._id ?? null),
    // deno-lint-ignore no-explicit-any
    set: (value) => new ApplyFromStatic(value),
    fromSecret: (key) => new ApplyFromSecret(key),
    fromContext: (key, type) => new ApplyFromContext(key, type?._id ?? null),
    fromParent: (typeName) => new ApplyFromParent(typeName),
};
export class InheritDef {
    constructor() {
        Object.defineProperty(this, "payload", {
            enumerable: true,
            configurable: true,
            writable: true,
            value: void 0
        });
    }
    /** inject static value */
    set(value) {
        this.payload = serializeStaticInjection(value);
        return this;
    }
    /** inject a value (generic) */
    inject(value) {
        this.payload = serializeGenericInjection("dynamic", value);
        return this;
    }
    /** inject from context */
    fromContext(value) {
        this.payload = serializeGenericInjection("context", value);
        return this;
    }
    /** inject from secret */
    fromSecret(value) {
        this.payload = serializeGenericInjection("secret", value);
        return this;
    }
    /** inject from parent */
    fromParent(value) {
        this.payload = serializeFromParentInjection(value);
        return this;
    }
    /** inject from random */
    fromRandom() {
        this.payload = serializeGenericInjection("random", null);
        return this;
    }
}
export class RawAuth {
    constructor(jsonStr) {
        Object.defineProperty(this, "jsonStr", {
            enumerable: true,
            configurable: true,
            writable: true,
            value: jsonStr
        });
    }
}
let counter = 0;
export async function typegraph(nameOrArgs, maybeBuilder) {
    ++counter;
    const args = typeof nameOrArgs === "string"
        ? { name: nameOrArgs }
        : nameOrArgs;
    const { name, dynamic, cors, prefix, rate, secrets } = args;
    const builder = "builder" in args
        ? args.builder
        : maybeBuilder;
    const file = caller();
    if (!file) {
        throw new Error("Could not determine caller file");
    }
    // node/deno compat tick until MET-236 is landed
    const simpleFile = file.replace(/:[0-9]+$/, "").replace(/^file:\/\//, "");
    const path = fromFileUrl(`file://${simpleFile}`);
    const defaultCorsFields = {
        allowCredentials: true,
        allowHeaders: [],
        allowMethods: [],
        allowOrigin: [],
        exposeHeaders: [],
        maxAgeSec: undefined,
    };
    const defaultRateFields = {
        localExcess: 0,
    };
    const tgParams = {
        prefix,
        secrets: secrets ?? [],
        cors: cors ? { ...defaultCorsFields, ...cors } : defaultCorsFields,
        rate: rate ? { ...defaultRateFields, ...rate } : undefined,
    };
    core.initTypegraph({ name, dynamic, path, ...tgParams });
    const g = {
        expose: (exports, defaultPolicy) => {
            core.expose(Object.entries(exports).map(([name, fn]) => [name, fn._id]), defaultPolicy ? getPolicyChain(defaultPolicy) : []);
        },
        inherit: () => {
            return new InheritDef();
        },
        rest: (graphql) => {
            return wit_utils.addGraphqlEndpoint(graphql);
        },
        auth: (value) => {
            if (value instanceof RawAuth) {
                return wit_utils.addRawAuth(value.jsonStr);
            }
            return wit_utils.addAuth(value);
        },
        ref: (name) => {
            return genRef(name);
        },
        configureRandomInjection: (params) => {
            return core.setSeed(params.seed);
        },
        ...InjectionSource,
    };
    try {
        builder(g);
        // deno-lint-ignore no-explicit-any
    }
    catch (err) {
        if (err.payload && !err.cause) {
            err.cause = err.payload;
        }
        if (("cause" in err) && ("stack" in err.cause)) {
            console.error(`Error in typegraph '${name}':`);
            for (const msg of err.cause.stack) {
                console.error(`- ${msg}`);
            }
            process.exit(1);
        }
        throw err;
    }
    const ret = {
        serialize(config) {
            try {
                const [tgJson, ref_artifacts] = core.serializeTypegraph(config);
                const result = {
                    tgJson: tgJson,
                    ref_artifacts: ref_artifacts,
                };
                return result;
                // deno-lint-ignore no-explicit-any
            }
            catch (err) {
                const stack = err?.payload?.stack;
                if (stack) {
                    // FIXME: jco generated code throws new Error(object) => prints [Object object]
                    throw new Error(stack.join("\n"));
                }
                throw err;
            }
        },
        name,
    };
    if (hasCliEnv()) {
        const manager = new Manager(ret);
        await manager.run();
        // TODO solve hanging process (stdin??)
        setTimeout(() => {
            if (counter === 0) {
                log.debug("exiting");
                process.exit(0);
            }
        }, 1000);
    }
    --counter;
    return ret;
}
/** generate a type reference (by name) */
export function genRef(name) {
    const value = core.refb(name, null);
    if (typeof value == "object") {
        throw new Error(JSON.stringify(value));
    }
    return new t.Typedef(value);
}
