import { collections, collectionsAndProjects, getConfig } from '../config.js';
import { getLastReleaseFetchDate, setLastReleaseFetchDate, } from '../models/cron-update-dates.js';
import { ReleaseModel } from '../models/mongoose-models/ReleaseEnvironment.js';
import { getReleaseUpdateDates } from '../models/releases.js';
import azure from '../scraper/network/azure.js';
import { is404 } from '../scraper/network/http-error.js';
import { invokeSeries } from '../utils.js';
import { shouldUpdate } from './utils.js';
const defaultQueryStart = () => {
    const d = new Date();
    d.setDate(d.getDate() - 395);
    return d;
};
const environmentFromAPI = (environment) => {
    const { deploySteps, ...rest } = environment;
    return {
        ...rest,
        deploySteps: deploySteps.map(d => {
            const { requestedBy, requestedFor, lastModifiedBy, ...rest } = d;
            return {
                ...rest,
                requestedById: requestedBy.id,
                requestedForId: requestedFor.id,
                lastModifiedById: lastModifiedBy.id,
            };
        }),
    };
};
const artifactFromAPI = (artifact) => {
    var _a, _b, _c, _d, _e, _f, _g, _h, _j, _k, _l, _m, _o, _p;
    return ({
        sourceId: artifact.sourceId,
        type: artifact.type,
        alias: artifact.alias,
        isPrimary: artifact.isPrimary,
        definition: {
            isTriggeringArtifact: artifact.definitionReference.IsTriggeringArtifact
                ? artifact.definitionReference.IsTriggeringArtifact.id === 'True'
                : undefined,
            buildPipelineUrl: ((_a = artifact.definitionReference.artifactSourceDefinitionUrl) === null || _a === void 0 ? void 0 : _a.id) || undefined,
            buildUri: ((_b = artifact.definitionReference.buildUri) === null || _b === void 0 ? void 0 : _b.id) || undefined,
            buildDefinitionId: ((_c = artifact.definitionReference.definition) === null || _c === void 0 ? void 0 : _c.id)
                ? Number.isNaN(Number(artifact.definitionReference.definition.id))
                    ? undefined
                    : Number(artifact.definitionReference.definition.id)
                : undefined,
            branch: ((_d = artifact.definitionReference.branch) === null || _d === void 0 ? void 0 : _d.id) || undefined,
            pullRequestId: ((_e = artifact.definitionReference.pullRequestId) === null || _e === void 0 ? void 0 : _e.id) || undefined,
            pullRequestSourceBranch: ((_f = artifact.definitionReference.pullRequestSourceBranch) === null || _f === void 0 ? void 0 : _f.id) || undefined,
            pullRequestSourceBranchCommitId: ((_g = artifact.definitionReference.pullRequestSourceBranchCommitId) === null || _g === void 0 ? void 0 : _g.id) || undefined,
            pullRequestMergeCommitId: ((_h = artifact.definitionReference.pullRequestMergeCommitId) === null || _h === void 0 ? void 0 : _h.id) || undefined,
            pullRequestTargetBranch: ((_j = artifact.definitionReference.pullRequestTargetBranch) === null || _j === void 0 ? void 0 : _j.id) || undefined,
            requestedForId: ((_k = artifact.definitionReference.requestedForId) === null || _k === void 0 ? void 0 : _k.id) || undefined,
            repositoryId: ((_l = artifact.definitionReference.repository) === null || _l === void 0 ? void 0 : _l.id) || undefined,
            repositoryName: ((_m = artifact.definitionReference.repository) === null || _m === void 0 ? void 0 : _m.name) || undefined,
            connectionName: ((_o = artifact.definitionReference.connection) === null || _o === void 0 ? void 0 : _o.name) || undefined,
            connectionId: ((_p = artifact.definitionReference.connection) === null || _p === void 0 ? void 0 : _p.id) || undefined,
        },
    });
};
export const bulkSaveReleases = (collectionName) => (releases) => ReleaseModel.bulkWrite(releases.map(release => {
    const { projectReference, environments, artifacts, releaseDefinition, ...rest } = release;
    return {
        updateOne: {
            filter: {
                collectionName,
                id: release.id,
                project: projectReference.name,
            },
            update: {
                $set: {
                    ...rest,
                    releaseDefinitionId: releaseDefinition.id,
                    releaseDefinitionName: releaseDefinition.name,
                    releaseDefinitionUrl: releaseDefinition.url,
                    project: projectReference.name,
                    environments: environments.map(environmentFromAPI),
                    artifacts: artifacts.map(artifactFromAPI),
                },
            },
            upsert: true,
        },
    };
}));
export const getReleases = async () => {
    const { getReleasesAsChunks } = azure(getConfig());
    await Promise.all(collections().map(({ name: collectionName, projects }) => {
        return invokeSeries(projects, async ({ name: project }) => {
            await getReleasesAsChunks(collectionName, project, (await getLastReleaseFetchDate(collectionName, project)) || defaultQueryStart(), bulkSaveReleases(collectionName));
            await setLastReleaseFetchDate(collectionName, project);
        });
    }));
};
export const getReleaseUpdates = async () => {
    const { getReleasesForReleaseIdsAsChunks } = azure(getConfig());
    await Promise.all(collectionsAndProjects().map(async ([collection, project]) => {
        const releaseUpdateDates = await getReleaseUpdateDates(collection.name, project.name);
        const releasesToFetch = releaseUpdateDates
            .filter(x => shouldUpdate(x.date))
            .map(x => x.id);
        const recurse = async (rids) => {
            try {
                await getReleasesForReleaseIdsAsChunks(collection.name, project.name, rids, bulkSaveReleases(collection.name));
            }
            catch (error) {
                if (!is404(error))
                    throw error;
                // This is a 404, which means one of the releases was deleted.
                // Start a recursive binary search to find and delete the offending
                // release, while saving the rest.
                if (rids.length === 1) {
                    // Search complete. This release ID doesn't exist. Delete it.
                    return ReleaseModel.deleteOne({
                        collectionName: collection.name,
                        project,
                        id: rids[0],
                    });
                }
                if (rids.length === 0) {
                    // lolwut?
                    return;
                }
                // Split releaseIds list in two, recurse
                const parts = [rids.slice(0, rids.length / 2), rids.slice(rids.length / 2)];
                return Promise.all(parts.map(recurse));
            }
        };
        return recurse(releasesToFetch);
    }));
};
