/*!
 * Copyright (C) Microsoft Corporation. All rights reserved.
 */
import { getPlayerServiceConfig } from '../initializePlayerServices.js';
const apiHubResourceMap = {
    local: 'https://apihub.azure.com',
    dev: 'https://apihub.azure.com',
    test: 'https://apihub.azure.com',
    preprod: 'https://apihub.azure.com',
    preview: 'https://apihub.azure.com',
    prod: 'https://apihub.azure.com',
    gccmoderate: 'https://power-apis-usgov001-public.azure-apihub.us',
    gcchigh: 'https://power-apis-usgov001-sovereign.azure-apihub.us',
    dod: 'https://power-apis-usdod-001.azure-apihub.us',
    mooncake: 'https://china.azure-apihub.cn/',
    ex: 'https://azure-apihub.eaglex.ic.gov',
    rx: 'https://azure-apihub.microsoft.scloud',
};
function getApiHubResource(region) {
    return apiHubResourceMap[region];
}
export function isEncoded(str) {
    try {
        return decodeURIComponent(str) !== str;
    }
    catch {
        return true;
    }
}
export function isDoubleEncoded(str) {
    try {
        const decodedOnce = decodeURIComponent(str);
        const decodedTwice = decodeURIComponent(decodedOnce);
        return str !== decodedOnce && decodedOnce !== decodedTwice;
    }
    catch {
        return false;
    }
}
export function ensureEncoded(str) {
    if (str.includes(',')) {
        const parts = str.split(',');
        return parts
            .map((part) => {
            if (isDoubleEncoded(part)) {
                return part;
            }
            else if (isEncoded(part)) {
                return encodeURIComponent(part);
            }
            else {
                return encodeURIComponent(encodeURIComponent(part));
            }
        })
            .join(',');
    }
    if (isDoubleEncoded(str)) {
        return str;
    }
    else if (isEncoded(str)) {
        return encodeURIComponent(str);
    }
    else {
        return encodeURIComponent(encodeURIComponent(str));
    }
}
/**
 * Returns the baseUrl of the highest-numbered production CDPTabular1 revision, or null if none exists.
 */
function getLatestCdpTabularBaseUrl(connector) {
    const cdpTabular1 = connector.properties.interfaces?.CDPTabular1;
    if (!cdpTabular1) {
        return null;
    }
    const prodRevisions = Object.entries(cdpTabular1.revisions)
        .filter(([, rev]) => rev.status === 'Production')
        .map(([version, rev]) => ({ version: parseInt(version, 10), rev }))
        .sort((a, b) => b.version - a.version);
    return prodRevisions.length > 0 ? prodRevisions[0].rev.baseUrl : null;
}
function getRuntimeRequestUrl(connector, connection) {
    const baseUrl = getLatestCdpTabularBaseUrl(connector);
    if (baseUrl === null) {
        throw new Error("No production revisions found for the 'CDPTabular1' interface in the connector's interfaces.");
    }
    const urlString = connector.properties.primaryRuntimeUrl.replace(/\/+$/, '');
    const connectionNameTrimmed = connection.name.replace(/\/+$/, '');
    const revisionTrimmed = baseUrl.replace(/^\/+|\/+$/g, '');
    return revisionTrimmed
        ? `${urlString}/${connectionNameTrimmed}/${revisionTrimmed}`
        : `${urlString}/${connectionNameTrimmed}`;
}
export async function getResourceMetadataAsync(connection, connector, dataset, resourceName, resourceType, logger) {
    const scenario = logger.trackScenario('ApimService.GetResourceMetadataAsync', {
        connectionId: connection.id,
        apiId: connector.id,
        apiName: connector.name,
        dataset,
        resourceName,
        resourceType,
    });
    // eslint-disable-next-line @typescript-eslint/no-explicit-any
    let additionalScenarioData = {};
    try {
        const { httpClient, region } = getPlayerServiceConfig();
        const authResource = getApiHubResource(region);
        let url = getRuntimeRequestUrl(connector, connection);
        const safeDataset = ensureEncoded(dataset);
        const safeResourceName = ensureEncoded(resourceName);
        url += `/$metadata.json/datasets/${safeDataset}/${resourceType}/${safeResourceName}`;
        additionalScenarioData = { url, authResource };
        const response = await httpClient.get(url, {
            authResource,
        });
        if (response.status === 200) {
            scenario.complete(additionalScenarioData);
        }
        else {
            additionalScenarioData.response = response.data;
            additionalScenarioData.status = response.status;
            scenario.completeWithError(additionalScenarioData);
        }
        return response.data;
    }
    catch (error) {
        scenario.failure({ error, ...additionalScenarioData });
        throw error;
    }
}
export async function getDatasetsAsync(connection, connector, logger) {
    const { httpClient, region } = getPlayerServiceConfig();
    const authResource = getApiHubResource(region);
    const scenario = logger.trackScenario('ApimService.GetDatasetsAsync', {
        connectionId: connection.id,
        connectionName: connection.name,
        connectionType: connection.type,
        apiName: connector.name,
        apiId: connector.id,
    });
    // eslint-disable-next-line @typescript-eslint/no-explicit-any
    let additionalScenarioData = {};
    try {
        let url = getRuntimeRequestUrl(connector, connection);
        url += `/datasets`;
        additionalScenarioData = { url, authResource };
        const response = await httpClient.get(url, {
            authResource,
        });
        if (response.status === 200) {
            scenario.complete();
        }
        else {
            additionalScenarioData.response = response.data;
            additionalScenarioData.status = response.status;
            scenario.completeWithError(additionalScenarioData);
        }
        return response.data;
    }
    catch (error) {
        scenario.failure({ error, ...additionalScenarioData });
        let errorMessage = `Failed to get datasets for connection '${connection.name}' and connector '${connector.name}'`;
        errorMessage += `: ${JSON.stringify(error)}`;
        throw new Error(errorMessage);
    }
}
export async function getTablesAsync(connection, connector, dataset, logger) {
    if (!connection || !connector || !dataset) {
        throw new Error('Connection, connector, and dataset are required parameters');
    }
    const scenario = logger.trackScenario('ApimService.GetTablesAsync', {
        connectionId: connection.id,
        connectionName: connection.name,
        apiId: connector.id,
        apiName: connector.name,
        dataset,
    });
    // eslint-disable-next-line @typescript-eslint/no-explicit-any
    let additionalScenarioData = {};
    try {
        const { httpClient, region } = getPlayerServiceConfig();
        const authResource = getApiHubResource(region);
        let url = getRuntimeRequestUrl(connector, connection);
        const tablePath = connector.name.toLowerCase() === 'shared_sharepointonline' ? 'allTables' : 'tables';
        const safeDataset = ensureEncoded(dataset);
        url += `/datasets/${safeDataset}/${tablePath}`;
        additionalScenarioData = { url, authResource };
        const response = await httpClient.get(url, {
            authResource,
        });
        if (response.status === 200) {
            scenario.complete(additionalScenarioData);
        }
        else {
            additionalScenarioData.response = response.data;
            additionalScenarioData.status = response.status;
            scenario.completeWithError(additionalScenarioData);
        }
        return response.data;
    }
    catch (error) {
        scenario.failure({ error, ...additionalScenarioData });
        let errorMessage = `Failed to get tables for connection '${connection.name}' and connector '${connector.name}'`;
        errorMessage += `: ${JSON.stringify(error)}`;
        throw new Error(errorMessage);
    }
}
export async function getSqlStoredProceduresAsync(connection, connector, dataset, logger) {
    const { httpClient, region } = getPlayerServiceConfig();
    const authResource = getApiHubResource(region);
    const scenario = logger.trackScenario('ApimService.GetSqlStoredProceduresAsync', {
        connectionId: connection.id,
        connectionName: connection.name,
        apiId: connector.id,
        apiName: connector.name,
        dataset,
    });
    // eslint-disable-next-line @typescript-eslint/no-explicit-any
    let additionalScenarioData = {};
    try {
        let url = getRuntimeRequestUrl(connector, connection);
        const safeDataset = ensureEncoded(dataset);
        url += `/datasets/${safeDataset}/procedures`;
        additionalScenarioData = { url, authResource };
        const response = await httpClient.get(url, {
            authResource,
        });
        if (response.status === 200) {
            scenario.complete(additionalScenarioData);
        }
        else {
            additionalScenarioData.response = response.data;
            additionalScenarioData.status = response.status;
            scenario.completeWithError(additionalScenarioData);
        }
        return response.data;
    }
    catch (error) {
        scenario.failure({ error, ...additionalScenarioData });
        let errorMessage = `Failed to get stored procedures for connection '${connection.name}' and connector '${connector.name}'`;
        errorMessage += `: ${JSON.stringify(error)}`;
        throw new Error(errorMessage);
    }
}
export async function getSqlStoredProcedureMetadataAsync(connection, connector, dataset, storedProcedure, logger) {
    return getResourceMetadataAsync(connection, connector, dataset, storedProcedure, 'procedures', logger);
}
export async function getTableMetadataAsync(connection, connector, dataset, tableName, logger) {
    return getResourceMetadataAsync(connection, connector, dataset, tableName, 'tables', logger);
}
/**
 * Returns the tabular API version string for a connector (e.g. 'v2', or '' for v1).
 * Determined by the highest-numbered production revision of the CDPTabular interface.
 */
export function getTabularConnectorVersion(connector) {
    const latestBaseUrl = getLatestCdpTabularBaseUrl(connector);
    if (latestBaseUrl === null) {
        return '';
    }
    const match = latestBaseUrl.match(/\/(v\d+)/);
    return match ? match[1] : '';
}
/**
 * Handles the first party login step of connection creation by POSTing the OBO token to the
 * provided firstPartyLoginUri.
 *
 * Calls httpClient.post with no authResource so that the underlying HTTP client omits the
 * Authorization header — the consent server rejects requests that carry one. The token is sent
 * only in the request body as { accessToken }.
 */
export async function connectionFirstPartyLoginAsync(authProvider, firstPartyLoginUri, logger) {
    const scenario = logger.trackScenario('ApimService.ConnectionFirstPartyLoginAsync');
    const { httpClient, region } = getPlayerServiceConfig();
    const authResource = getApiHubResource(region);
    const consentToken = await authProvider.getAccessTokenForResource(authResource);
    // No authResource → CliHttpClient omits the Authorization header.
    // The consent server expects only { accessToken } in the body and rejects requests
    // that carry an Authorization header.
    const response = await httpClient.post(firstPartyLoginUri, {
        body: { accessToken: consentToken },
    });
    if (response.status < 200 || response.status >= 300) {
        const error = new Error(`First-party OBO authentication failed: HTTP ${response.status}`);
        scenario.failure({ error, status: response.status });
        throw error;
    }
    scenario.complete({ status: response.status });
}
/**
 * Verifies the connection is live by calling the server-provided test link.
 *
 * The connection PUT/GET response includes `properties.testLinks`, an array of
 * `{ requestUri, method }` entries owned by the API Hub runtime. We invoke the
 * first entry, using the API Hub bearer audience for the current region.
 */
export async function testConnectionAsync(connection, logger) {
    const scenario = logger?.trackScenario('TestConnectionAsync', {
        connectionId: connection.id,
    });
    let additionalScenarioData = {};
    try {
        const testLink = connection.properties.testLinks?.[0];
        if (!testLink?.requestUri) {
            throw new Error(`Connection '${connection.id}' did not return a testLinks entry — ` +
                `cannot verify the connection.`);
        }
        const { httpClient, region } = getPlayerServiceConfig();
        const authResource = getApiHubResource(region);
        const url = testLink.requestUri;
        const method = (testLink.method ?? 'GET').toUpperCase();
        additionalScenarioData = { url, method, authResource };
        let response;
        if (method === 'GET') {
            response = await httpClient.get(url, { authResource });
        }
        else if (method === 'POST') {
            response = await httpClient.post(url, { authResource });
        }
        else {
            throw new Error(`Connection '${connection.id}' testLinks entry has unsupported method '${testLink.method}'; ` +
                `expected 'GET' or 'POST'.`);
        }
        additionalScenarioData.status = response.status;
        if (response.status !== 200 && response.status !== 204) {
            scenario?.completeWithError(additionalScenarioData);
            throw new Error(`Connection test failed for connection '${connection.id}': HTTP ${response.status}`);
        }
        scenario?.complete(additionalScenarioData);
    }
    catch (error) {
        scenario?.failure({ error, ...additionalScenarioData });
        throw error;
    }
}
//# sourceMappingURL=ApimService.js.map