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());
    });
};
import { BaseGitProviderClient } from "./BaseGitProviderClient";
import { WorkspaceBackendError } from "./errors";
import { parseGitRemote } from "./gitRemote";
function inferApiBase(config, host) {
    const configured = config.apiBaseUrl;
    if (configured === null || configured === void 0 ? void 0 : configured.trim())
        return configured.trim().replace(/\/+$/g, "");
    if (host === "gitlab.com")
        return "https://gitlab.com/api/v4";
    return `https://${host}/api/v4`;
}
export class GitlabProviderClient extends BaseGitProviderClient {
    static canHandle(config) {
        const provider = config.provider;
        if (provider && provider !== "auto")
            return provider === "gitlab";
        try {
            const { host } = parseGitRemote(config.remoteUrl);
            return host === "gitlab.com" || host.includes("gitlab");
        }
        catch (_a) {
            return false;
        }
    }
    request(apiBase, config, path, init) {
        return __awaiter(this, void 0, void 0, function* () {
            var _a;
            const headers = {
                Accept: "application/json",
                "Content-Type": "application/json",
            };
            const token = (_a = config.auth.token) === null || _a === void 0 ? void 0 : _a.trim();
            if (token)
                headers["PRIVATE-TOKEN"] = token;
            const res = yield fetch(`${apiBase}${path}`, Object.assign(Object.assign({}, init), { cache: "no-store", headers: Object.assign(Object.assign({}, headers), init === null || init === void 0 ? void 0 : init.headers) }));
            const status = res.status;
            const contentType = res.headers.get("content-type") || "";
            if (contentType.includes("application/json")) {
                const text = yield res.text();
                if (text.trim()) {
                    try {
                        const json = JSON.parse(text);
                        return { status, json };
                    }
                    catch (_b) {
                        return { status };
                    }
                }
            }
            return { status };
        });
    }
    getProjectId(config) {
        const { repoPath } = parseGitRemote(config.remoteUrl);
        return encodeURIComponent(repoPath);
    }
    getBranch(config, apiBase) {
        return __awaiter(this, void 0, void 0, function* () {
            var _a, _b;
            const configured = (_a = config.branch) === null || _a === void 0 ? void 0 : _a.trim();
            if (configured)
                return configured;
            const projectId = this.getProjectId(config);
            const { status, json } = yield this.request(apiBase, config, `/projects/${projectId}`);
            this.ensureOk(status, "Failed to resolve repository default branch.");
            const branch = (_b = json === null || json === void 0 ? void 0 : json.default_branch) === null || _b === void 0 ? void 0 : _b.trim();
            if (!branch)
                throw new WorkspaceBackendError("UNKNOWN", "Could not determine default branch.");
            return branch;
        });
    }
    validateAccess(config) {
        return __awaiter(this, void 0, void 0, function* () {
            const { host } = parseGitRemote(config.remoteUrl);
            const apiBase = inferApiBase(config, host);
            const projectId = this.getProjectId(config);
            const { status } = yield this.request(apiBase, config, `/projects/${projectId}`);
            this.ensureOk(status, "Could not access GitLab project with the provided token.");
        });
    }
    listFolder(config, folderId) {
        return __awaiter(this, void 0, void 0, function* () {
            const { host } = parseGitRemote(config.remoteUrl);
            const apiBase = inferApiBase(config, host);
            const relPath = (folderId === null || folderId === void 0 ? void 0 : folderId.trim()) || "";
            const fullPath = this.joinPath(config.rootPath, relPath);
            const projectId = this.getProjectId(config);
            const branch = yield this.getBranch(config, apiBase);
            const qsBase = new URLSearchParams();
            qsBase.set("ref", branch);
            qsBase.set("per_page", "100");
            if (fullPath)
                qsBase.set("path", fullPath);
            const items = [];
            for (let page = 1; page <= 50; page++) {
                const qs = new URLSearchParams(qsBase);
                qs.set("page", String(page));
                const { status, json } = yield this.request(apiBase, config, `/projects/${projectId}/repository/tree?${qs.toString()}`);
                if (status === 404)
                    return [];
                this.ensureOk(status, "Failed to list folder contents.");
                const batch = Array.isArray(json) ? json : [];
                items.push(...batch);
                if (batch.length < 100)
                    break;
            }
            const entries = [];
            for (const item of items) {
                if (item.type === "tree") {
                    const id = relPath ? this.joinPath(relPath, item.name) : item.name;
                    entries.push({ kind: "folder", id, label: item.name, parentId: relPath || undefined });
                    continue;
                }
                if (item.type === "blob") {
                    if (!/\.sparql$/i.test(item.name))
                        continue;
                    const id = relPath ? this.joinPath(relPath, item.name) : item.name;
                    const label = item.name.replace(/\.sparql$/i, "");
                    entries.push({ kind: "query", id, label, parentId: relPath || undefined });
                }
            }
            entries.sort((a, b) => {
                if (a.kind !== b.kind)
                    return a.kind === "folder" ? -1 : 1;
                return a.label.toLowerCase().localeCompare(b.label.toLowerCase());
            });
            return entries;
        });
    }
    readFileAtRef(config, apiBase, queryId, ref) {
        return __awaiter(this, void 0, void 0, function* () {
            const projectId = this.getProjectId(config);
            const filePath = this.joinPath(config.rootPath, queryId);
            const branch = ref || (yield this.getBranch(config, apiBase));
            const { status, json } = yield this.request(apiBase, config, `/projects/${projectId}/repository/files/${encodeURIComponent(filePath)}?ref=${encodeURIComponent(branch)}`);
            this.ensureOk(status, "Failed to read query.");
            const content = json === null || json === void 0 ? void 0 : json.content;
            if (!content)
                throw new WorkspaceBackendError("NOT_FOUND", "Query not found");
            const text = (json === null || json === void 0 ? void 0 : json.encoding) === "base64" ? this.base64DecodeUtf8(content) : content;
            return { text, lastCommitId: json === null || json === void 0 ? void 0 : json.last_commit_id };
        });
    }
    readQuery(config, queryId) {
        return __awaiter(this, void 0, void 0, function* () {
            const { host } = parseGitRemote(config.remoteUrl);
            const apiBase = inferApiBase(config, host);
            const { text, lastCommitId } = yield this.readFileAtRef(config, apiBase, queryId);
            return { queryText: text, versionTag: lastCommitId };
        });
    }
    writeQuery(config, queryId, queryText, options) {
        return __awaiter(this, void 0, void 0, function* () {
            const { host } = parseGitRemote(config.remoteUrl);
            const apiBase = inferApiBase(config, host);
            const projectId = this.getProjectId(config);
            const filePath = this.joinPath(config.rootPath, queryId);
            const branch = yield this.getBranch(config, apiBase);
            let exists = true;
            let lastCommitId;
            try {
                const res = yield this.readFileAtRef(config, apiBase, queryId);
                lastCommitId = res.lastCommitId;
            }
            catch (e) {
                const err = e;
                exists = false;
                if ((err === null || err === void 0 ? void 0 : err.code) && err.code !== "NOT_FOUND")
                    throw e;
            }
            if ((options === null || options === void 0 ? void 0 : options.expectedVersionTag) && lastCommitId && options.expectedVersionTag !== lastCommitId) {
                throw new WorkspaceBackendError("CONFLICT", "The file changed remotely since it was last opened. Please reload the managed query and try saving again.");
            }
            const message = this.getCommitMessage(queryId, options, !exists);
            const body = {
                branch,
                content: this.base64EncodeUtf8(queryText),
                encoding: "base64",
                commit_message: message,
            };
            const method = exists ? "PUT" : "POST";
            const { status } = yield this.request(apiBase, config, `/projects/${projectId}/repository/files/${encodeURIComponent(filePath)}`, {
                method,
                body: JSON.stringify(body),
            });
            this.ensureOk(status, "Failed to save query to GitLab workspace.");
        });
    }
    listVersions(config, queryId) {
        return __awaiter(this, void 0, void 0, function* () {
            const { host } = parseGitRemote(config.remoteUrl);
            const apiBase = inferApiBase(config, host);
            const projectId = this.getProjectId(config);
            const filePath = this.joinPath(config.rootPath, queryId);
            const branch = yield this.getBranch(config, apiBase);
            const qs = new URLSearchParams();
            qs.set("ref_name", branch);
            qs.set("path", filePath);
            qs.set("per_page", "30");
            const { status, json } = yield this.request(apiBase, config, `/projects/${projectId}/repository/commits?${qs.toString()}`);
            this.ensureOk(status, "Failed to list query versions.");
            const commits = Array.isArray(json) ? json : [];
            return commits
                .map((c) => {
                const createdAt = c.created_at || new Date().toISOString();
                return { id: c.id, createdAt, author: c.author_name, message: c.message };
            })
                .filter((v) => !!v.id);
        });
    }
    readVersion(config, queryId, versionId) {
        return __awaiter(this, void 0, void 0, function* () {
            const { host } = parseGitRemote(config.remoteUrl);
            const apiBase = inferApiBase(config, host);
            const { text, lastCommitId } = yield this.readFileAtRef(config, apiBase, queryId, versionId);
            return { queryText: text, versionTag: lastCommitId };
        });
    }
    deleteQuery(config, queryId) {
        return __awaiter(this, void 0, void 0, function* () {
            const { host } = parseGitRemote(config.remoteUrl);
            const apiBase = inferApiBase(config, host);
            const projectId = this.getProjectId(config);
            const filePath = this.joinPath(config.rootPath, queryId);
            const branch = yield this.getBranch(config, apiBase);
            const body = {
                branch,
                commit_message: this.getDeleteMessage(queryId),
            };
            const { status } = yield this.request(apiBase, config, `/projects/${projectId}/repository/files/${encodeURIComponent(filePath)}`, { method: "DELETE", body: JSON.stringify(body) });
            if (status === 404)
                return;
            this.ensureOk(status, "Failed to delete query from GitLab workspace.");
        });
    }
}
//# sourceMappingURL=GitlabProviderClient.js.map