"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.jobFromEndpoint = exports.topicNameForEndpoint = exports.jobNameForEndpoint = exports.createOrReplaceJob = exports.getJob = exports.deleteJob = void 0;
const _ = require("lodash");
const error_1 = require("../error");
const logger_1 = require("../logger");
const api_1 = require("../api");
const apiv2_1 = require("../apiv2");
const backend = require("../deploy/functions/backend");
const proto = require("./proto");
const checkIam_1 = require("../deploy/functions/checkIam");
const functional_1 = require("../functional");
const VERSION = "v1";
const DEFAULT_TIME_ZONE_V1 = "America/Los_Angeles";
const DEFAULT_TIME_ZONE_V2 = "UTC";
const apiClient = new apiv2_1.Client({ urlPrefix: api_1.cloudschedulerOrigin, apiVersion: VERSION });
function createJob(job) {
    const strippedName = job.name.substring(0, job.name.lastIndexOf("/"));
    const json = job.pubsubTarget
        ? Object.assign({ timeZone: DEFAULT_TIME_ZONE_V1 }, job) : Object.assign({ timeZone: DEFAULT_TIME_ZONE_V2 }, job);
    return apiClient.post(`/${strippedName}`, json);
}
function deleteJob(name) {
    return apiClient.delete(`/${name}`);
}
exports.deleteJob = deleteJob;
function getJob(name) {
    return apiClient.get(`/${name}`, {
        resolveOnHTTPError: true,
    });
}
exports.getJob = getJob;
function updateJob(job) {
    let fieldMasks;
    let json;
    if (job.pubsubTarget) {
        fieldMasks = proto.fieldMasks(job, "pubsubTarget");
        json = Object.assign({ timeZone: DEFAULT_TIME_ZONE_V1 }, job);
    }
    else {
        fieldMasks = proto.fieldMasks(job, "httpTarget");
        json = Object.assign({ timeZone: DEFAULT_TIME_ZONE_V2 }, job);
    }
    return apiClient.patch(`/${job.name}`, json, {
        queryParams: {
            updateMask: fieldMasks.join(","),
        },
    });
}
async function createOrReplaceJob(job) {
    var _a, _b;
    const jobName = job.name.split("/").pop();
    const existingJob = await getJob(job.name);
    if (existingJob.status === 404) {
        let newJob;
        try {
            newJob = await createJob(job);
        }
        catch (err) {
            if (((_b = (_a = err === null || err === void 0 ? void 0 : err.context) === null || _a === void 0 ? void 0 : _a.response) === null || _b === void 0 ? void 0 : _b.statusCode) === 404) {
                throw new error_1.FirebaseError(`Cloud resource location is not set for this project but scheduled functions require it. ` +
                    `Please see this documentation for more details: https://firebase.google.com/docs/projects/locations.`);
            }
            throw new error_1.FirebaseError(`Failed to create scheduler job ${job.name}: ${err.message}`);
        }
        logger_1.logger.debug(`created scheduler job ${jobName}`);
        return newJob;
    }
    if (!job.timeZone) {
        job.timeZone = job.pubsubTarget ? DEFAULT_TIME_ZONE_V1 : DEFAULT_TIME_ZONE_V2;
    }
    if (!needUpdate(existingJob.body, job)) {
        logger_1.logger.debug(`scheduler job ${jobName} is up to date, no changes required`);
        return;
    }
    const updatedJob = await updateJob(job);
    logger_1.logger.debug(`updated scheduler job ${jobName}`);
    return updatedJob;
}
exports.createOrReplaceJob = createOrReplaceJob;
function needUpdate(existingJob, newJob) {
    if (!existingJob) {
        return true;
    }
    if (!newJob) {
        return true;
    }
    if (existingJob.schedule !== newJob.schedule) {
        return true;
    }
    if (existingJob.timeZone !== newJob.timeZone) {
        return true;
    }
    if (newJob.retryConfig) {
        if (!existingJob.retryConfig) {
            return true;
        }
        if (!_.isMatch(existingJob.retryConfig, newJob.retryConfig)) {
            return true;
        }
    }
    return false;
}
function jobNameForEndpoint(endpoint, location) {
    const id = backend.scheduleIdForFunction(endpoint);
    return `projects/${endpoint.project}/locations/${location}/jobs/${id}`;
}
exports.jobNameForEndpoint = jobNameForEndpoint;
function topicNameForEndpoint(endpoint) {
    const id = backend.scheduleIdForFunction(endpoint);
    return `projects/${endpoint.project}/topics/${id}`;
}
exports.topicNameForEndpoint = topicNameForEndpoint;
function jobFromEndpoint(endpoint, location, projectNumber) {
    const job = {};
    job.name = jobNameForEndpoint(endpoint, location);
    if (endpoint.platform === "gcfv1") {
        job.timeZone = endpoint.scheduleTrigger.timeZone || DEFAULT_TIME_ZONE_V1;
        job.pubsubTarget = {
            topicName: topicNameForEndpoint(endpoint),
            attributes: {
                scheduled: "true",
            },
        };
    }
    else if (endpoint.platform === "gcfv2") {
        job.timeZone = endpoint.scheduleTrigger.timeZone || DEFAULT_TIME_ZONE_V2;
        job.httpTarget = {
            uri: endpoint.uri,
            httpMethod: "POST",
            oidcToken: {
                serviceAccountEmail: (0, checkIam_1.getDefaultComputeServiceAgent)(projectNumber),
            },
        };
    }
    else {
        (0, functional_1.assertExhaustive)(endpoint.platform);
    }
    if (!endpoint.scheduleTrigger.schedule) {
        throw new error_1.FirebaseError("Cannot create a scheduler job without a schedule:" + JSON.stringify(endpoint));
    }
    job.schedule = endpoint.scheduleTrigger.schedule;
    if (endpoint.scheduleTrigger.retryConfig) {
        job.retryConfig = {};
        proto.copyIfPresent(job.retryConfig, endpoint.scheduleTrigger.retryConfig, "maxDoublings", "retryCount");
        proto.convertIfPresent(job.retryConfig, endpoint.scheduleTrigger.retryConfig, "maxBackoffDuration", "maxBackoffSeconds", (0, functional_1.nullsafeVisitor)(proto.durationFromSeconds));
        proto.convertIfPresent(job.retryConfig, endpoint.scheduleTrigger.retryConfig, "minBackoffDuration", "minBackoffSeconds", (0, functional_1.nullsafeVisitor)(proto.durationFromSeconds));
        proto.convertIfPresent(job.retryConfig, endpoint.scheduleTrigger.retryConfig, "maxRetryDuration", "maxRetrySeconds", (0, functional_1.nullsafeVisitor)(proto.durationFromSeconds));
        if (!Object.keys(job.retryConfig).length) {
            delete job.retryConfig;
        }
    }
    return job;
}
exports.jobFromEndpoint = jobFromEndpoint;
