import prettyMilliseconds from 'pretty-ms';
import { add, adjust, identity, inc, pipe, prop, replace } from 'rambda';
import { asc, byDate, desc } from 'sort-lib';
import { weeks } from '../../utils.js';
const repoId = (build) => { var _a, _b; return (_b = (_a = build.repository) === null || _a === void 0 ? void 0 : _a.id) !== null && _b !== void 0 ? _b : '<unknown>'; };
const defaultBuildStats = {
    count: 0,
    name: 'unknown',
    url: '',
    success: 0,
    duration: [],
    status: { type: 'unknown' },
    buildsByWeek: weeks.map(() => 0),
    successesByWeek: weeks.map(() => 0),
};
const status = (a, b) => {
    if (a.status.type === 'unknown')
        return b.status;
    if (b.status.type === 'unknown')
        return a.status;
    if (a.status.type === 'succeeded' && b.status.type === 'succeeded') {
        return a.status.latest < b.status.latest ? b.status : a.status;
    }
    if (a.status.type === 'succeeded' && b.status.type === 'failed') {
        return a.status.latest < b.status.since ? b.status : a.status;
    }
    if (a.status.type === 'failed' && b.status.type === 'succeeded') {
        return a.status.since < b.status.latest ? b.status : a.status;
    }
    if (a.status.type === 'failed' && b.status.type === 'failed') {
        return a.status.since < b.status.since ? a.status : b.status;
    }
    return b.status;
};
const combineStats = (incoming, existing = defaultBuildStats) => {
    const weekIndex = weeks.findIndex(isInWeek => isInWeek(incoming.date));
    return {
        count: existing.count + incoming.count,
        name: incoming.name,
        url: incoming.url,
        success: existing.success + incoming.success,
        duration: [...existing.duration, ...incoming.duration],
        status: status(incoming, existing),
        buildsByWeek: adjust(weekIndex, weekIndex === -1 ? identity : inc, existing.buildsByWeek),
        successesByWeek: incoming.success
            ? adjust(weekIndex, weekIndex === -1 ? identity : inc, existing.successesByWeek)
            : existing.successesByWeek,
    };
};
const buildDefinitionWebUrl = pipe(replace('_apis/build/Definitions/', '_build/definition?definitionId='), replace(/\?revision=.*/, ''));
export default (builds, buildDefinitionsByRepoId, repos, centralTemplateBuildCount) => {
    const defaultBranchByRepoId = repos.reduce((acc, repo) => {
        acc[repo.id] = repo.defaultBranch;
        return acc;
    }, {});
    const { buildStats, allMasterBuilds } = [...builds]
        .sort(desc(byDate(prop('finishTime'))))
        .reduce((acc, build) => {
        var _a, _b;
        const rId = repoId(build);
        return {
            buildStats: {
                ...acc.buildStats,
                [rId]: {
                    ...acc.buildStats[rId],
                    [build.definition.id]: combineStats({
                        count: 1,
                        name: build.definition.name,
                        url: buildDefinitionWebUrl(build.definition.url),
                        success: build.result === 'failed' ? 0 : 1,
                        duration: [
                            new Date(build.finishTime).getTime() -
                                new Date(build.startTime).getTime(),
                        ],
                        status: build.result === 'failed'
                            ? { type: 'failed', since: build.finishTime }
                            : { type: 'succeeded', latest: build.finishTime },
                        date: build.finishTime,
                    }, ((_a = acc.buildStats[rId]) === null || _a === void 0 ? void 0 : _a[build.definition.id]) || undefined),
                },
            },
            allMasterBuilds: {
                ...acc.allMasterBuilds,
                [rId]: {
                    ...acc.allMasterBuilds[rId],
                    ...(build.sourceBranch === defaultBranchByRepoId[rId]
                        ? {
                            [build.definition.id]: [
                                ...(((_b = acc.allMasterBuilds[rId]) === null || _b === void 0 ? void 0 : _b[build.definition.id]) || []),
                                build,
                            ],
                        }
                        : {}),
                },
            },
        };
    }, { buildStats: {}, allMasterBuilds: {} });
    return {
        buildsByRepoId: async (id) => {
            if (!id)
                return null;
            if (!buildStats[id])
                return null;
            const pipelines = await Promise.all(Object.entries(buildStats[id]).map(async ([definitionId, buildStats]) => {
                var _a;
                const centralTemplateRuns = await centralTemplateBuildCount(definitionId);
                return {
                    count: buildStats.count,
                    name: buildStats.name,
                    url: buildStats.url,
                    success: buildStats.success,
                    definitionId,
                    duration: {
                        average: prettyMilliseconds(buildStats.duration.reduce(add, 0) / buildStats.duration.length),
                        min: prettyMilliseconds(Math.min(...buildStats.duration)),
                        max: prettyMilliseconds(Math.max(...buildStats.duration)),
                    },
                    status: buildStats.status.type === 'succeeded' ||
                        buildStats.status.type === 'unknown'
                        ? buildStats.status
                        : {
                            type: 'failed',
                            since: buildStats.status.since,
                        },
                    type: ((_a = buildDefinitionsByRepoId(id).find(bd => bd.id === Number(definitionId))) === null || _a === void 0 ? void 0 : _a.process.processType) === 1
                        ? 'ui'
                        : 'yml',
                    buildsByWeek: buildStats.buildsByWeek,
                    successesByWeek: buildStats.successesByWeek,
                    centralTemplateRuns,
                };
            }));
            const pipelinesWithUnused = buildDefinitionsByRepoId(id)
                ? pipelines.concat(buildDefinitionsByRepoId(id)
                    .filter(d => !pipelines.some(p => p.definitionId === d.id.toString()))
                    .sort(asc(byDate(x => { var _a; return ((_a = x.latestBuild) === null || _a === void 0 ? void 0 : _a.startTime) || new Date(0); })))
                    .map(d => {
                    var _a;
                    return ({
                        count: 0,
                        name: d.name,
                        url: buildDefinitionWebUrl(d.url),
                        success: 0,
                        definitionId: d.id.toString(),
                        duration: { average: '0', min: '0', max: '0' },
                        status: { type: 'unused', since: (_a = d.latestBuild) === null || _a === void 0 ? void 0 : _a.startTime },
                        type: d.process.processType === 1 ? 'ui' : 'yml',
                        centralTemplateRuns: 0,
                    });
                }))
                : pipelines;
            return {
                count: pipelinesWithUnused.reduce((acc, p) => acc + p.count, 0),
                pipelines: pipelinesWithUnused,
            };
        },
        allMasterBuilds,
    };
};
