import { z } from 'zod';
import { prop, propEq, intersection, length } from 'rambda';
import { RepositoryModel } from './mongoose-models/RepositoryModel.js';
import { getHealthyBranchesSummary, getTotalBranchesForRepositoryIds, } from './branches.js';
import { getSuccessfulBuildsBy, getTotalBuildsBy } from './build-listing.js';
import { getTotalCentralTemplateUsage, getCentralTemplateBuildDefs, getActivePipelineCentralTemplateBuilds, } from './build-reports/index.js';
import { getAllRepoDefaultBranchIDs, getDefaultBranchAndNameForRepoIds, getReposSortedByBranchesCount, getReposSortedByBuildCount, getReposSortedByCommitsCount, getReposSortedByPullRequestsCount, } from './repos.js';
import { getHasReleasesSummary, getReposConformingToBranchPolicies, releaseBranchesForRepo, } from './release-listing.js';
import { BuildDefinitionModel } from './mongoose-models/BuildDefinitionModel.js';
import { getTestsAndCoverageByWeek, getReposSortedByTests, getTotalTestsForRepositoryIds, getTestsAndCoveragesCount, } from './testruns.js';
import { getActivePipelineBuilds, getTotalBuildsForRepositoryIds } from './builds.js';
import { getDevsCommittedToRepositoryIds, getTotalCommitsForRepositoryIds, getWeeklyCommitsForRepositoryIds, } from './commits.js';
import { getReposSortedByCodeQuality, getReposWithSonarQube, getSonarProjectsCount, getSonarQualityGateStatusForRepoIds, updateWeeklySonarProjectCount, updatedWeeklyReposWithSonarQubeCount, } from './sonar.js';
import { fromContext } from './utils.js';
import { getWeeklyPullRequestMerges, getTotalPullRequestsForRepositoryIds, } from './pull-requests.js';
import { pipelineCountForRepo } from './releases.js';
import { getWeeklyPipelinesWithTestsCount, getActivePipelineIds, getWeeklyPipelinesWithCoverageCount, } from './build-definitions.js';
import { filteredReposInputParser, getActiveRepos, searchAndFilterReposBy, } from './active-repos.js';
const pipelineUrlForUI = (url) => url
    .replace('/_apis/build/Definitions/', '/_build?definitionId=')
    .replace(/\?revision=.*/, '');
export const getYamlPipelinesCountSummary = async (queryContext, repoIds) => {
    const { collectionName, project } = fromContext(queryContext);
    const result = await BuildDefinitionModel.aggregate([
        {
            $match: {
                collectionName,
                project,
                ...(repoIds ? { repositoryId: { $in: repoIds } } : {}),
            },
        },
        {
            $group: {
                _id: { collectionName: '$collectionName', project: '$project' },
                totalCount: { $sum: 1 },
                yamlCount: {
                    $sum: {
                        $cond: {
                            if: { $eq: ['$process.processType', 2] },
                            then: 1,
                            else: 0,
                        },
                    },
                },
            },
        },
        {
            $project: {
                _id: 0,
                totalCount: 1,
                yamlCount: 1,
            },
        },
    ]);
    return result[0] || { totalCount: 0, yamlCount: 0 };
};
export const getCentralTemplatePipeline = async (queryContext, repoIds, repoNames) => {
    const { collectionName, project } = fromContext(queryContext);
    const centralTemplateBuildDefs = await getCentralTemplateBuildDefs(queryContext, repoNames);
    const buildDefinitionIds = centralTemplateBuildDefs === null || centralTemplateBuildDefs === void 0 ? void 0 : centralTemplateBuildDefs.map(buildDef => buildDef.buildDefinitionId);
    const result = await BuildDefinitionModel.aggregate([
        {
            $match: {
                collectionName,
                project,
                ...(repoIds ? { repositoryId: { $in: repoIds } } : {}),
                ...(buildDefinitionIds ? { id: { $in: buildDefinitionIds } } : {}),
            },
        },
        {
            $lookup: {
                from: 'repositories',
                let: {
                    repositoryId: '$repositoryId',
                },
                pipeline: [
                    {
                        $match: {
                            collectionName,
                            'project.name': project,
                            '$expr': {
                                $eq: ['$id', '$$repositoryId'],
                            },
                        },
                    },
                    {
                        $project: {
                            name: 1,
                            defaultBranch: 1,
                        },
                    },
                ],
                as: 'repo',
            },
        },
        {
            $addFields: {
                defaultBranch: { $arrayElemAt: ['$repo.defaultBranch', 0] },
            },
        },
        {
            $project: {
                _id: 0,
                collectionName: 1,
                project: 1,
                repositoryId: 1,
                buildDefinitionId: '$id',
                buildDefinitionName: '$name',
                defaultBranch: 1,
            },
        },
        {
            $lookup: {
                from: 'builds',
                let: {
                    repositoryId: '$repositoryId',
                    buildDefinitionId: '$buildDefinitionId',
                    defaultBranch: '$defaultBranch',
                },
                pipeline: [
                    {
                        $match: {
                            collectionName,
                            project,
                            $expr: {
                                $and: [
                                    { $eq: ['$repository.id', '$$repositoryId'] },
                                    { $eq: ['$definition.id', '$$buildDefinitionId'] },
                                    { $eq: ['$sourceBranch', '$$defaultBranch'] },
                                ],
                            },
                        },
                    },
                    { $sort: { finishTime: -1 } },
                    { $limit: 1 },
                    { $project: { sourceBranch: 1 } },
                ],
                as: 'builds',
            },
        },
        {
            $addFields: {
                sourceBranch: { $arrayElemAt: ['$builds.sourceBranch', 0] },
            },
        },
        {
            $match: {
                $expr: {
                    $and: [
                        { $ne: ['$defaultBranch', null] },
                        { $ne: ['$sourceBranch', null] },
                        { $eq: ['$sourceBranch', '$defaultBranch'] },
                    ],
                },
            },
        },
        {
            $project: {
                _id: 0,
                buildDefinitionId: 1,
            },
        },
    ]);
    return {
        total: centralTemplateBuildDefs.reduce((total, build) => total + build.centralTemplateBuilds, 0),
        totalCentralTemplatePipelines: centralTemplateBuildDefs.length,
        central: result.length,
        idsWithMainBranchBuilds: result.map(r => r.buildDefinitionId),
    };
};
export const getFilteredReposCount = async ({ queryContext, searchTerms, teams, }) => {
    return searchAndFilterReposBy({
        queryContext,
        searchTerms,
        teams,
    }).then(length);
};
export const getSummaryAsChunks = async ({ queryContext, searchTerms, teams }, onChunk) => {
    const sendChunk = (key) => (data) => {
        onChunk({ [key]: data });
    };
    const { collectionName, project } = fromContext(queryContext);
    const activeRepos = await getActiveRepos(queryContext, searchTerms, teams);
    const activeRepoIds = activeRepos.map(prop('id'));
    const activeRepoNames = activeRepos.map(prop('name'));
    sendChunk('totalActiveRepos')(activeRepoIds.length);
    const defaultBranchIDs = await getAllRepoDefaultBranchIDs(collectionName, project, activeRepoIds);
    const activePipelineIdsPromise = getActivePipelineIds(queryContext, activeRepoIds);
    const centralTemplatePipelinePromise = getCentralTemplatePipeline(queryContext, activeRepoIds, activeRepoNames);
    const activePipelineWithCentralTemplateCountPromise = Promise.all([
        activePipelineIdsPromise,
        centralTemplatePipelinePromise,
    ]).then(([activePipelineIds, centralTemplatePipeline]) => {
        return intersection(activePipelineIds, centralTemplatePipeline.idsWithMainBranchBuilds).length;
    });
    await Promise.all([
        getSuccessfulBuildsBy(queryContext, activeRepoIds).then(sendChunk('successfulBuilds')),
        getTotalBuildsBy(queryContext, activeRepoIds).then(sendChunk('totalBuilds')),
        getTotalCentralTemplateUsage(queryContext, activeRepoNames).then(sendChunk('centralTemplateUsage')),
        getYamlPipelinesCountSummary(queryContext, activeRepoIds).then(sendChunk('pipelines')),
        getWeeklyPipelinesWithTestsCount(queryContext, activeRepoIds).then(sendChunk('weeklyPipelinesWithTestsCount')),
        getWeeklyPipelinesWithCoverageCount(queryContext, activeRepoIds).then(sendChunk('weeklyPipelinesWithCoverageCount')),
        getHealthyBranchesSummary(queryContext, activeRepoIds, defaultBranchIDs).then(sendChunk('healthyBranches')),
        getHasReleasesSummary(queryContext, activeRepoIds).then(sendChunk('hasReleasesReposCount')),
        centralTemplatePipelinePromise
            .then(x => {
            // eslint-disable-next-line @typescript-eslint/no-unused-vars
            const { idsWithMainBranchBuilds, ...rest } = x;
            return rest;
        })
            .then(sendChunk('centralTemplatePipeline')),
        getTestsAndCoveragesCount(queryContext, activeRepoIds).then(sendChunk('defSummary')),
        getTestsAndCoverageByWeek(queryContext, activeRepoIds).then(({ testsByWeek, coveragesByWeek }) => {
            sendChunk('weeklyTestsSummary')(testsByWeek);
            sendChunk('weeklyCoverageSummary')(coveragesByWeek);
        }),
        searchAndFilterReposBy({ queryContext, searchTerms, teams }).then(x => sendChunk('totalRepos')(x.length)),
        getSonarProjectsCount(collectionName, project, activeRepoIds).then(sendChunk('sonarProjects')),
        updateWeeklySonarProjectCount(queryContext, activeRepoIds).then(sendChunk('weeklySonarProjectsCount')),
        getReposWithSonarQube(collectionName, project, activeRepoIds).then(sendChunk('reposWithSonarQube')),
        updatedWeeklyReposWithSonarQubeCount(queryContext, activeRepoIds).then(sendChunk('weeklyReposWithSonarQubeCount')),
        getReposConformingToBranchPolicies(queryContext, activeRepoIds).then(sendChunk('branchPolicies')),
        activePipelineIdsPromise.then(length).then(sendChunk('activePipelinesCount')),
        getActivePipelineCentralTemplateBuilds(queryContext, activeRepoNames, activeRepoIds).then(sendChunk('activePipelineCentralTemplateBuilds')),
        getActivePipelineBuilds(queryContext, activeRepoIds).then(sendChunk('activePipelineBuilds')),
        activePipelineWithCentralTemplateCountPromise.then(sendChunk('activePipelineWithCentralTemplateCount')),
        getWeeklyPullRequestMerges(queryContext, activeRepoIds).then(sendChunk('pullRequestMerges')),
    ]);
};
export const getSummary = async (filterArgs) => {
    let mergedChunks = {};
    await getSummaryAsChunks(filterArgs, x => {
        mergedChunks = { ...mergedChunks, ...x };
    });
    return mergedChunks;
};
export const getNonYamlPipelines = async ({ queryContext, searchTerms, teams, }) => {
    const { collectionName, project } = fromContext(queryContext);
    const activeRepos = await getActiveRepos(queryContext, searchTerms, teams);
    return RepositoryModel.aggregate([
        {
            $match: {
                collectionName,
                'project.name': project,
                'id': { $in: activeRepos.map(prop('id')) },
            },
        },
        {
            $lookup: {
                from: 'builddefinitions',
                let: {
                    repositoryId: '$id',
                },
                pipeline: [
                    {
                        $match: {
                            collectionName,
                            project,
                            $expr: {
                                $and: [
                                    { $eq: ['$repositoryId', '$$repositoryId'] },
                                    { $eq: ['$process.processType', 1] },
                                ],
                            },
                        },
                    },
                ],
                as: 'buildsDefinitions',
            },
        },
        { $match: { $expr: { $gt: [{ $size: '$buildsDefinitions' }, 0] } } },
        {
            $project: {
                _id: 0,
                repositoryId: '$id',
                name: 1,
                total: { $size: '$buildsDefinitions ' },
            },
        },
        { $sort: { total: -1 } },
    ]).exec();
};
export const getYAMLPipelinesForDownload = async ({ queryContext, searchTerms, teams, }) => {
    const { collectionName, project, startDate, endDate } = fromContext(queryContext);
    const activeRepos = await getActiveRepos(queryContext, searchTerms, teams);
    const repos = await RepositoryModel.aggregate([
        {
            $match: {
                collectionName,
                'project.name': project,
                'id': { $in: activeRepos.map(prop('id')) },
            },
        },
        {
            $lookup: {
                from: 'builddefinitions',
                let: {
                    repositoryId: '$id',
                },
                pipeline: [
                    {
                        $match: {
                            collectionName,
                            project,
                            $expr: { $eq: ['$repositoryId', '$$repositoryId'] },
                        },
                    },
                    {
                        $lookup: {
                            from: 'builds',
                            let: {
                                repositoryId: '$repositoryId',
                                definitionId: '$id',
                            },
                            pipeline: [
                                {
                                    $match: {
                                        collectionName,
                                        project,
                                        $expr: {
                                            $and: [
                                                { $eq: ['$repository.id', '$$repositoryId'] },
                                                { $eq: ['$definition.id', '$$definitionId'] },
                                                { $gt: ['$finishTime', startDate] },
                                                { $lt: ['$finishTime', endDate] },
                                            ],
                                        },
                                    },
                                },
                                { $count: 'buildCount' },
                                { $project: { buildCount: 1 } },
                            ],
                            as: 'builds',
                        },
                    },
                    {
                        $project: {
                            _id: 0,
                            name: 1,
                            url: 1,
                            lastRun: '$latestBuild.startTime',
                            runCount: { $first: '$builds.buildCount' },
                            yaml: { $eq: ['$process.processType', 2] },
                        },
                    },
                ],
                as: 'pipelines',
            },
        },
        { $match: { $expr: { $gt: [{ $size: '$pipelines' }, 0] } } },
        {
            $project: {
                _id: 0,
                name: 1,
                // repositoryId: '$id',
                pipelines: 1,
            },
        },
        { $sort: { total: -1 } },
    ]).exec();
    return repos.flatMap(repo => repo.pipelines.map(pipeline => ({
        ...pipeline,
        runCount: pipeline.runCount || 0,
        url: pipelineUrlForUI(pipeline.url),
        repoName: repo.name,
    })));
};
export const getRepoListingWithPipelineCount = async ({ queryContext, searchTerms, teams, }) => {
    const { collectionName, project } = fromContext(queryContext);
    const activeRepos = await getActiveRepos(queryContext, searchTerms, teams);
    return RepositoryModel.aggregate([
        {
            $match: {
                collectionName,
                'project.name': project,
                'id': { $in: activeRepos.map(prop('id')) },
            },
        },
        {
            $lookup: {
                from: 'builddefinitions',
                let: { repositoryId: '$id' },
                pipeline: [
                    {
                        $match: {
                            collectionName,
                            project,
                            $expr: {
                                $and: [{ $eq: ['$repositoryId', '$$repositoryId'] }],
                            },
                        },
                    },
                ],
                as: 'buildsDefinitions',
            },
        },
        { $match: { $expr: { $gt: [{ $size: '$buildsDefinitions' }, 0] } } },
        {
            $project: {
                _id: 0,
                repositoryId: '$id',
                name: 1,
                total: { $size: '$buildsDefinitions' },
                nonYaml: {
                    $size: {
                        $filter: {
                            input: '$buildsDefinitions',
                            as: 'def',
                            cond: { $eq: ['$$def.process.processType', 1] },
                        },
                    },
                },
                yaml: {
                    $size: {
                        $filter: {
                            input: '$buildsDefinitions',
                            as: 'def',
                            cond: { $eq: ['$$def.process.processType', 2] },
                        },
                    },
                },
            },
        },
        { $sort: { total: -1 } },
    ]).exec();
};
const getTotalPipelineCountForRepositoryIds = (queryContext, repositoryIds) => {
    return Promise.all(repositoryIds.map(async (id) => {
        return {
            repositoryId: id,
            count: await pipelineCountForRepo(queryContext, id),
        };
    }));
};
const getTotalReleaseBranchesForRepositoryIds = (queryContext, repositoryIds) => {
    return Promise.all(repositoryIds.map(async (id) => {
        return {
            repositoryId: id,
            branches: await releaseBranchesForRepo(queryContext, id),
        };
    }));
};
const sorters = {
    'builds': getReposSortedByBuildCount,
    'branches': getReposSortedByBranchesCount,
    'commits': getReposSortedByCommitsCount,
    'pull-requests': getReposSortedByPullRequestsCount,
    'tests': getReposSortedByTests,
    'code-quality': getReposSortedByCodeQuality,
};
const sortKeys = [
    'builds',
    'branches',
    'commits',
    'pull-requests',
    'tests',
    'code-quality',
];
export const repoFiltersAndSorterInputParser = filteredReposInputParser.extend({
    pageSize: z.number(),
    pageNumber: z.number(),
    sortBy: z.enum(sortKeys).optional(),
    sortDirection: z.enum(['asc', 'desc']).optional(),
    cursor: z
        .object({
        pageSize: z.number().optional(),
        pageNumber: z.number().optional(),
    })
        .nullish(),
});
export const getFilteredAndSortedReposWithStats = async ({ queryContext, searchTerms, teams, sortBy = 'builds', sortDirection = 'desc', cursor, }) => {
    const filteredRepos = await searchAndFilterReposBy({
        queryContext,
        searchTerms,
        teams,
    });
    const repositoryIds = filteredRepos.map(prop('id'));
    const sortedRepos = await sorters[sortBy](queryContext, repositoryIds, sortDirection, (cursor === null || cursor === void 0 ? void 0 : cursor.pageSize) || 10, (cursor === null || cursor === void 0 ? void 0 : cursor.pageNumber) || 0);
    const sortedRepoIds = sortedRepos.map(repo => repo.repositoryId);
    const [repoDetails, builds, branches, commits, devs, weeklyCommits, tests, sonarQualityGateStatuses, pullRequests, pipelineCounts, releaseBranches,] = await Promise.all([
        getDefaultBranchAndNameForRepoIds(queryContext, sortedRepoIds),
        getTotalBuildsForRepositoryIds(queryContext, sortedRepoIds),
        getTotalBranchesForRepositoryIds(queryContext, sortedRepoIds),
        getTotalCommitsForRepositoryIds(queryContext, sortedRepoIds),
        getDevsCommittedToRepositoryIds(queryContext, sortedRepoIds),
        getWeeklyCommitsForRepositoryIds(queryContext, sortedRepoIds),
        getTotalTestsForRepositoryIds(queryContext, sortedRepoIds),
        getSonarQualityGateStatusForRepoIds(queryContext, sortedRepoIds),
        getTotalPullRequestsForRepositoryIds(queryContext, sortedRepoIds),
        getTotalPipelineCountForRepositoryIds(queryContext, sortedRepoIds),
        getTotalReleaseBranchesForRepositoryIds(queryContext, sortedRepoIds),
    ]);
    const repos = sortedRepos.map(repo => {
        var _a, _b, _c, _d, _e, _f, _g, _h;
        const matchingRepo = propEq('repositoryId', repo.repositoryId);
        return {
            repositoryId: repo.repositoryId,
            // eslint-disable-next-line @typescript-eslint/no-non-null-assertion
            repoDetails: repoDetails.find(repoDetail => repoDetail.id === repo.repositoryId),
            builds: ((_a = builds.find(matchingRepo)) === null || _a === void 0 ? void 0 : _a.count) || 0,
            branches: ((_b = branches.find(matchingRepo)) === null || _b === void 0 ? void 0 : _b.total) || 0,
            devs: devs.find(matchingRepo),
            weeklyCommits: (_c = weeklyCommits.find(matchingRepo)) === null || _c === void 0 ? void 0 : _c.commits,
            commits: ((_d = commits.find(matchingRepo)) === null || _d === void 0 ? void 0 : _d.count) || 0,
            tests: ((_e = tests.find(matchingRepo)) === null || _e === void 0 ? void 0 : _e.totalTests) || 0,
            sonarQualityGateStatuses: sonarQualityGateStatuses.find(matchingRepo),
            pullRequests: ((_f = pullRequests.find(matchingRepo)) === null || _f === void 0 ? void 0 : _f.total) || 0,
            pipelineCounts: (_g = pipelineCounts.find(matchingRepo)) === null || _g === void 0 ? void 0 : _g.count,
            releaseBranches: (_h = releaseBranches.find(matchingRepo)) === null || _h === void 0 ? void 0 : _h.branches,
        };
    });
    return {
        items: repos,
        nextCursor: {
            pageNumber: ((cursor === null || cursor === void 0 ? void 0 : cursor.pageNumber) || 0) + 1,
            pageSize: (cursor === null || cursor === void 0 ? void 0 : cursor.pageSize) || 10,
        },
    };
};
