"use strict";
var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
    function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
    return new (P || (P = Promise))(function (resolve, reject) {
        function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
        function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
        function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
        step((generator = generator.apply(thisArg, _arguments || [])).next());
    });
};
Object.defineProperty(exports, "__esModule", { value: true });
exports.ProtobuftsPlugin = void 0;
const plugin_framework_1 = require("@protobuf-ts/plugin-framework");
const file_1 = require("./file");
const twirp_1 = require("./gen/twirp");
const gateway_1 = require("./gen/gateway");
const local_type_name_1 = require("./local-type-name");
const interpreter_1 = require("./interpreter");
const open_api_1 = require("./gen/open-api");
const index_file_1 = require("./gen/index-file");
class ProtobuftsPlugin extends plugin_framework_1.PluginBase {
    constructor() {
        super(...arguments);
        this.parameters = {
            ts_proto: {
                description: "Use the ts-proto compiler (protobuf-ts by default)",
            },
            gateway: {
                description: "Generates the twirp gateway",
            },
            index_file: {
                description: "Generates an index.ts file that exports all the types",
            },
            emit_default_values: {
                description: "Json encode and decode will emit default values",
            },
            openapi_twirp: {
                description: "Generates an OpenAPI spec for twirp handlers",
            },
            openapi_gateway: {
                description: "Generates an OpenAPI spec for gateway handlers",
            },
            standalone: {
                description: "Generates client and server in 2 separate files",
            },
            client_only: {
                description: "Only client will be generated (overrides 'standalone')",
            },
            server_only: {
                description: "Only server will be generated (overrides 'standalone')",
            },
            camel_case: {
                description: "Generates with method names in camel case.",
            },
        };
        // we support proto3-optionals, so we let protoc know
        this.getSupportedFeatures = () => [
            plugin_framework_1.CodeGeneratorResponse_Feature.PROTO3_OPTIONAL,
        ];
    }
    generate(request) {
        var _a, _b, _c, _d;
        return __awaiter(this, void 0, void 0, function* () {
            const params = this.parseOptions(this.parameters, request.parameter), registry = plugin_framework_1.DescriptorRegistry.createFrom(request), symbols = new plugin_framework_1.SymbolTable(), interpreter = new interpreter_1.Interpreter(registry);
            const ctx = {
                lib: params.ts_proto ? "ts-proto" : "protobuf-ts",
                emitDefaultValues: params.emit_default_values,
                symbols,
                registry,
                interpreter,
                camelCase: params.camel_case,
            };
            const files = [];
            for (let fileDescriptor of registry.allFiles()) {
                const messageFileOut = new file_1.File(`${(_a = fileDescriptor.name) === null || _a === void 0 ? void 0 : _a.replace(".proto", "").toLowerCase()}`);
                registry.visitTypes(fileDescriptor, (descriptor) => {
                    // we are not interested in synthetic types like map entry messages
                    if (registry.isSyntheticElement(descriptor))
                        return;
                    ctx.symbols.register(local_type_name_1.createLocalTypeName(descriptor, registry), descriptor, messageFileOut);
                });
                // Generate a combined client and server bundle if no code gen
                // options are passed.
                if (!params.standalone && !params.client_only && !params.server_only) {
                    const twirpFileOut = new file_1.File(`${(_b = fileDescriptor.name) === null || _b === void 0 ? void 0 : _b.replace(".proto", "").toLowerCase()}.twirp.ts`);
                    const twirpFileContent = yield twirp_1.generateTwirp(ctx, fileDescriptor);
                    twirpFileOut.setContent(twirpFileContent);
                    files.push(twirpFileOut);
                }
                if (params.server_only && params.client_only) {
                    throw new Error("Only one of server_only or client_only can be passed.");
                }
                if (params.server_only || params.standalone) {
                    const serverFileOut = new file_1.File(`${(_c = fileDescriptor.name) === null || _c === void 0 ? void 0 : _c.replace(".proto", "").toLowerCase()}.twirp.ts`);
                    const serverContent = yield twirp_1.generateTwirpServer(ctx, fileDescriptor);
                    serverFileOut.setContent(serverContent);
                    files.push(serverFileOut);
                }
                if (params.client_only || params.standalone) {
                    const clientFileOut = new file_1.File(`${(_d = fileDescriptor.name) === null || _d === void 0 ? void 0 : _d.replace(".proto", "").toLowerCase()}.twirp-client.ts`);
                    const clientContent = yield twirp_1.generateTwirpClient(ctx, fileDescriptor);
                    clientFileOut.setContent(clientContent);
                    files.push(clientFileOut);
                }
            }
            // Gateway generation
            if (params.gateway) {
                const gatewayFileOut = new file_1.File(`gateway.twirp.ts`);
                const gatewayContent = yield gateway_1.genGateway(ctx, registry.allFiles());
                gatewayFileOut.setContent(gatewayContent);
                files.push(gatewayFileOut);
            }
            // Create index file
            if (params.index_file) {
                files.push(index_file_1.genIndexFile(registry, [...files]));
            }
            // Open API
            const docs = [];
            if (params.openapi_twirp) {
                docs.push(...(yield open_api_1.genOpenAPI(ctx, registry.allFiles(), open_api_1.OpenAPIType.TWIRP)));
            }
            if (params.openapi_gateway) {
                docs.push(...(yield open_api_1.genOpenAPI(ctx, registry.allFiles(), open_api_1.OpenAPIType.GATEWAY)));
            }
            docs.forEach((doc) => {
                const file = new file_1.File(`${doc.fileName}`);
                file.setContent(doc.content);
                files.push(file);
            });
            return files;
        });
    }
}
exports.ProtobuftsPlugin = ProtobuftsPlugin;
new ProtobuftsPlugin()
    .run()
    .then(() => {
    process.exit(0);
})
    .catch((e) => {
    process.stderr.write("FAILED!");
    process.stderr.write(e.message);
    process.stderr.write(e.stack);
    process.exit(1);
});
