// Copyright (c) Microsoft Corporation. All rights reserved.
// Licensed under the MIT License. See License.txt in the project root for license information.
import { UsageFlags, } from "@azure-tools/typespec-client-generator-core";
import { NoTarget } from "@typespec/compiler";
import { resolveOptions } from "../options.js";
import { InputOperationParameterKind } from "../type/input-operation-parameter-kind.js";
import { RequestLocation } from "../type/request-location.js";
import { fromSdkType } from "./converter.js";
import { reportDiagnostic } from "./lib.js";
import { Logger } from "./logger.js";
import { navigateModels } from "./model.js";
import { fromSdkServiceMethod, getParameterDefaultValue } from "./operation-converter.js";
import { processServiceAuthentication } from "./service-authentication.js";
export function createModel(sdkContext) {
    const sdkPackage = sdkContext.sdkPackage;
    const sdkTypeMap = {
        types: new Map(),
        models: new Map(),
        enums: new Map(),
    };
    navigateModels(sdkContext, sdkTypeMap);
    const sdkApiVersionEnums = sdkPackage.enums.filter((e) => e.usage === UsageFlags.ApiVersionEnum);
    const rootClients = sdkPackage.clients.filter((c) => c.initialization.access === "public");
    if (rootClients.length === 0) {
        reportDiagnostic(sdkContext.program, { code: "no-root-client", format: {}, target: NoTarget });
        return {};
    }
    const rootApiVersions = sdkApiVersionEnums.length > 0
        ? sdkApiVersionEnums[0].values.map((v) => v.value).flat()
        : rootClients[0].apiVersions;
    const inputClients = [];
    fromSdkClients(rootClients, inputClients, []);
    const clientModel = {
        Name: sdkPackage.rootNamespace,
        ApiVersions: rootApiVersions,
        Enums: Array.from(sdkTypeMap.enums.values()),
        Models: Array.from(sdkTypeMap.models.values()),
        Clients: inputClients,
        Auth: processServiceAuthentication(sdkContext, sdkPackage),
    };
    return clientModel;
    function fromSdkClients(clients, inputClients, parentClientNames) {
        for (const client of clients) {
            const inputClient = emitClient(client, parentClientNames);
            inputClients.push(inputClient);
            const subClients = client.methods
                .filter((m) => m.kind === "clientaccessor")
                .map((m) => m.response);
            parentClientNames.push(inputClient.Name);
            fromSdkClients(subClients, inputClients, parentClientNames);
            parentClientNames.pop();
        }
    }
    function emitClient(client, parentNames) {
        const endpointParameter = client.initialization.properties.find((p) => p.kind === "endpoint");
        const uri = getMethodUri(endpointParameter);
        const clientParameters = fromSdkEndpointParameter(endpointParameter);
        return {
            Name: getClientName(client, parentNames),
            Summary: client.summary,
            Doc: client.doc,
            Operations: client.methods
                .filter((m) => m.kind !== "clientaccessor")
                .map((m) => fromSdkServiceMethod(m, uri, rootApiVersions, sdkContext, sdkTypeMap)),
            Protocol: {},
            Parent: parentNames.length > 0 ? parentNames[parentNames.length - 1] : undefined,
            Parameters: clientParameters,
            Decorators: client.decorators,
        };
    }
    function getClientName(client, parentClientNames) {
        const clientName = client.name;
        if (parentClientNames.length === 0)
            return clientName;
        if (parentClientNames.length >= 2)
            return `${parentClientNames.slice(parentClientNames.length - 1).join("")}${clientName}`;
        if (clientName === "Models" &&
            resolveOptions(sdkContext.emitContext)["model-namespace"] !== false) {
            Logger.getInstance().warn(`Invalid client name "${clientName}"`);
            return "ModelsOps";
        }
        return clientName;
    }
    function fromSdkEndpointParameter(p) {
        if (p.type.kind === "union") {
            return fromSdkEndpointType(p.type.variantTypes[0]);
        }
        else {
            return fromSdkEndpointType(p.type);
        }
    }
    function fromSdkEndpointType(type) {
        // TODO: support free-style endpoint url with multiple parameters
        const endpointExpr = type.serverUrl
            .replace("https://", "")
            .replace("http://", "")
            .split("/")[0];
        if (!/^\{\w+\}$/.test(endpointExpr))
            throw new Error(`Unsupported server url "${type.serverUrl}"`);
        const endpointVariableName = endpointExpr.substring(1, endpointExpr.length - 1);
        const parameters = [];
        for (const parameter of type.templateArguments) {
            const isEndpoint = parameter.name === endpointVariableName;
            const parameterType = isEndpoint
                ? {
                    kind: "url",
                    name: "url",
                    crossLanguageDefinitionId: "TypeSpec.url",
                }
                : fromSdkType(parameter.type, sdkContext, sdkTypeMap); // TODO: consolidate with converter.fromSdkEndpointType
            parameters.push({
                Name: parameter.name,
                NameInRequest: parameter.serializedName,
                Summary: parameter.summary,
                Doc: parameter.doc,
                // TODO: we should do the magic in generator
                Type: parameterType,
                Location: RequestLocation.Uri,
                IsApiVersion: parameter.isApiVersionParam,
                IsResourceParameter: false,
                IsContentType: false,
                IsRequired: !parameter.optional,
                IsEndpoint: isEndpoint,
                SkipUrlEncoding: false,
                Explode: false,
                Kind: InputOperationParameterKind.Client,
                DefaultValue: getParameterDefaultValue(parameter.clientDefaultValue, parameterType),
            });
        }
        return parameters;
    }
}
function getMethodUri(p) {
    if (!p)
        return "";
    if (p.type.kind === "endpoint" && p.type.templateArguments.length > 0)
        return p.type.serverUrl;
    if (p.type.kind === "union" && p.type.variantTypes.length > 0)
        return p.type.variantTypes[0].serverUrl;
    return `{${p.name}}`;
}
//# sourceMappingURL=client-model-builder.js.map