import { z } from 'zod';
import { CommitModel } from './mongoose-models/CommitModel.js';
import { inDateRange } from './helpers.js';
import { getConfig } from '../config.js';
import { queryContextInputParser, fromContext, weekIndexValue } from './utils.js';
import { RepositoryModel } from './mongoose-models/RepositoryModel.js';
export const getLatestCommitIdAndDate = async (collectionName, project, repositoryId) => {
    const result = await CommitModel.findOne({ collectionName, project, repositoryId }, { 'commitId': 1, 'committer.date': 1 }, { sort: { 'committer.date': -1 } }).lean();
    if (!result)
        return;
    return {
        commitId: result.commitId,
        date: result.committer.date,
    };
};
export const bulkSaveCommits = (collectionName, project, repositoryId) => (commits) => {
    return CommitModel.bulkWrite(commits
        .filter(commit => commit.changeCounts)
        .map(commit => {
        if (!commit.changeCounts)
            throw new Error('Error to keep TS happy');
        return {
            updateOne: {
                filter: {
                    collectionName,
                    project,
                    repositoryId,
                    commitId: commit.commitId,
                },
                update: {
                    $set: {
                        ...commit,
                        changeCounts: {
                            add: commit.changeCounts.Add,
                            edit: commit.changeCounts.Edit,
                            delete: commit.changeCounts.Delete,
                        },
                    },
                },
                upsert: true,
            },
        };
    }));
};
export const getCommits = (collectionName, project) => (repositoryId) => {
    const { queryFrom } = getConfig().azure;
    return CommitModel.find({
        collectionName,
        project,
        repositoryId,
        'author.date': { $gt: queryFrom },
    });
};
export const devCommitsDetailsInputParser = z.object({
    repositoryId: z.string(),
    queryContext: queryContextInputParser,
    authorEmail: z.string(),
});
export const getRepoCommitsDetailsForAuthorEmail = ({ queryContext, repositoryId, authorEmail, }) => {
    const { collectionName, project, startDate, endDate } = fromContext(queryContext);
    return CommitModel.aggregate([
        {
            $match: {
                collectionName,
                project,
                'author.date': inDateRange(startDate, endDate),
            },
        },
        { $addFields: { authorEmail: { $toLower: '$author.email' } } },
        { $match: { authorEmail } },
        { $sort: { 'author.date': -1 } },
        {
            $group: {
                _id: '$authorEmail',
                repoCommits: {
                    $sum: { $cond: [{ $eq: ['$repositoryId', repositoryId] }, 1, 0] },
                },
                totalCommits: { $sum: 1 },
                totalAdd: {
                    $sum: {
                        $cond: [{ $eq: ['$repositoryId', repositoryId] }, '$changeCounts.add', 0],
                    },
                },
                totalEdit: {
                    $sum: {
                        $cond: [{ $eq: ['$repositoryId', repositoryId] }, '$changeCounts.edit', 0],
                    },
                },
                totalDelete: {
                    $sum: {
                        $cond: [{ $eq: ['$repositoryId', repositoryId] }, '$changeCounts.delete', 0],
                    },
                },
                allRepos: { $addToSet: '$repositoryId' },
                authorName: { $first: '$author.name' },
                authorImageUrl: { $first: '$author.imageUrl' },
                authorEmail: { $first: '$authorEmail' },
                latestCommit: { $first: '$author.date' },
            },
        },
        { $addFields: { totalReposCommitted: { $size: '$allRepos' } } },
        {
            $project: {
                _id: 0,
                allRepos: 0,
            },
        },
    ])
        .exec()
        .then(x => x[0]);
};
export const getTotalCommitsForRepositoryIds = (queryContext, repositoryIds) => {
    const { collectionName, project, startDate, endDate } = fromContext(queryContext);
    return CommitModel.aggregate([
        {
            $match: {
                collectionName,
                project,
                'repositoryId': { $in: repositoryIds },
                'author.date': inDateRange(startDate, endDate),
            },
        },
        { $group: { _id: '$repositoryId', count: { $sum: 1 } } },
        {
            $project: {
                _id: 0,
                repositoryId: '$_id',
                count: 1,
            },
        },
    ]).exec();
};
const devSortKeys = ['authorName', 'totalReposCommitted'];
const getUniqueDevsCommittedToRepo = async (queryContext, repositoryName) => {
    const { collectionName, project, startDate, endDate } = fromContext(queryContext);
    const repositoryId = await RepositoryModel.findOne({
        collectionName,
        'project.name': project,
        'name': repositoryName,
    }, {
        _id: 0,
        id: 1,
    })
        .exec()
        .then(x => x === null || x === void 0 ? void 0 : x.id);
    if (!repositoryId) {
        return [];
    }
    return CommitModel.aggregate([
        {
            $match: {
                collectionName,
                project,
                repositoryId,
                'author.date': inDateRange(startDate, endDate),
            },
        },
        { $addFields: { authorEmail: { $toLower: '$author.email' } } },
        { $group: { _id: '$authorEmail' } },
        { $project: { _id: 0, authorEmail: '$_id' } },
    ])
        .exec()
        .then(x => x.map(x => x.authorEmail));
};
const repoSearchTermHelper = async (queryContext, searchTerm) => {
    if (searchTerm === '') {
        return { isRepoSearch: false, repoSearchTerm: undefined, devEmails: undefined };
    }
    const isRepoSearch = searchTerm === null || searchTerm === void 0 ? void 0 : searchTerm.includes('repo:');
    const repoSearchTerm = isRepoSearch
        ? searchTerm === null || searchTerm === void 0 ? void 0 : searchTerm.replaceAll('repo:', '').replaceAll('"', '')
        : undefined;
    const devEmails = repoSearchTerm
        ? await getUniqueDevsCommittedToRepo(queryContext, repoSearchTerm)
        : undefined;
    return { isRepoSearch, repoSearchTerm, devEmails };
};
export const devListingInputParser = z.object({
    queryContext: queryContextInputParser,
    searchTerm: z.string().optional(),
    pageSize: z.number(),
    pageNumber: z.number(),
    sortBy: z.enum(devSortKeys).optional(),
    sortDirection: z.enum(['asc', 'desc']).optional(),
    cursor: z
        .object({
        pageSize: z.number().optional(),
        pageNumber: z.number().optional(),
    })
        .nullish(),
});
export const getSortedDevListing = async ({ queryContext, searchTerm, sortBy, sortDirection, cursor, }) => {
    const { collectionName, project, startDate, endDate } = fromContext(queryContext);
    const sortOrderNum = sortDirection === 'asc' ? 1 : -1;
    const pageSize = (cursor === null || cursor === void 0 ? void 0 : cursor.pageSize) || 20;
    const pageNumber = (cursor === null || cursor === void 0 ? void 0 : cursor.pageNumber) || 0;
    const { isRepoSearch, devEmails } = await repoSearchTermHelper(queryContext, searchTerm || '');
    const sortStage = {
        $sort: {
            ...(!sortBy || sortBy === 'authorName'
                ? { lowerAuthorName: sortOrderNum }
                : {
                    [sortBy]: sortOrderNum,
                }),
            lowerAuthorEmail: sortDirection === 'desc' ? -1 : 1,
        },
    };
    const [repos, commits] = await Promise.all([
        RepositoryModel.find({ collectionName, 'project.name': project }).lean(),
        CommitModel.aggregate([
            {
                $match: {
                    collectionName,
                    project,
                    'author.date': inDateRange(startDate, endDate),
                    'author.email': { $exists: true },
                    ...(searchTerm && !isRepoSearch
                        ? { 'author.name': { $regex: new RegExp(searchTerm, 'i') } }
                        : {}),
                },
            },
            {
                $addFields: {
                    ...(searchTerm && isRepoSearch
                        ? { lowerAuthorEmail: { $toLower: '$author.email' } }
                        : {}),
                    authorDate: {
                        $dateToString: {
                            format: '%Y-%m-%d',
                            date: '$author.date',
                        },
                    },
                },
            },
            ...(searchTerm && isRepoSearch && devEmails
                ? [{ $match: { lowerAuthorEmail: { $in: devEmails } } }]
                : []),
            {
                $group: {
                    _id: {
                        authorEmail: {
                            $toLower: '$author.email',
                        },
                        repositoryId: '$repositoryId',
                        authorDate: '$authorDate',
                    },
                    dailyCommitsCount: { $sum: 1 },
                    dailyAdd: { $sum: '$changeCounts.add' },
                    dailyEdit: { $sum: '$changeCounts.edit' },
                    dailyDelete: { $sum: '$changeCounts.delete' },
                    authorEmail: { $first: '$author.email' },
                    repositoryId: { $first: '$repositoryId' },
                    authorDate: { $first: '$authorDate' },
                    authorName: { $first: '$author.name' },
                    authorImage: { $first: '$author.imageUrl' },
                    latestCommit: { $max: '$author.date' },
                },
            },
            {
                $group: {
                    _id: {
                        authorEmail: {
                            $toLower: '$authorEmail',
                        },
                        repositoryId: '$repositoryId',
                    },
                    dailyCommits: {
                        $push: {
                            count: '$dailyCommitsCount',
                            add: '$dailyAdd',
                            edit: '$dailyEdit',
                            delete: '$dailyDelete',
                            date: '$authorDate',
                        },
                    },
                    add: { $sum: '$dailyAdd' },
                    edit: { $sum: '$dailyEdit' },
                    delete: { $sum: '$dailyDelete' },
                    commitCount: { $sum: '$dailyCommitsCount' },
                    authorEmail: { $first: '$authorEmail' },
                    repositoryId: { $first: '$repositoryId' },
                    authorName: { $first: '$authorName' },
                    authorImage: { $first: '$authorImage' },
                    latestCommit: { $max: '$latestCommit' },
                },
            },
            {
                $group: {
                    _id: { $toLower: '$authorEmail' },
                    totalCommits: { $sum: '$commitCount' },
                    repos: { $push: '$$ROOT' },
                    authorEmail: { $first: '$authorEmail' },
                    authorName: { $first: '$authorName' },
                    authorImage: { $first: '$authorImage' },
                    latestCommit: { $max: '$latestCommit' },
                },
            },
            {
                $project: {
                    _id: 0,
                    totalCommits: 1,
                    repos: 1,
                    authorEmail: 1,
                    authorName: 1,
                    authorImage: 1,
                    lowerAuthorName: { $toLower: '$authorName' },
                    lowerAuthorEmail: { $toLower: '$authorEmail' },
                    latestCommit: 1,
                },
            },
            sortStage,
            { $skip: pageSize * pageNumber },
            { $limit: pageSize },
            {
                $project: {
                    lowerAuthorName: 0,
                    lowerAuthorEmail: 0,
                },
            },
        ]).exec(),
    ]);
    const findRepo = (repoId) => repos.find(repo => repo.id === repoId);
    const devCommits = commits.map(commit => {
        const repos = commit.repos.map(repo => {
            return {
                ...repo,
                // eslint-disable-next-line @typescript-eslint/no-non-null-assertion
                name: findRepo(repo.repositoryId).name,
                // eslint-disable-next-line @typescript-eslint/no-non-null-assertion
                url: findRepo(repo.repositoryId).url,
            };
        });
        return { ...commit, repos };
    });
    return {
        items: devCommits,
        nextCursor: {
            pageNumber: pageNumber + 1,
            pageSize,
        },
    };
};
export const devFilterInputParser = z.object({
    queryContext: queryContextInputParser,
    searchTerm: z.string().optional(),
});
export const getFilteredDevCount = async ({ queryContext, searchTerm, }) => {
    var _a;
    const { collectionName, project, startDate, endDate } = fromContext(queryContext);
    const { isRepoSearch, devEmails } = await repoSearchTermHelper(queryContext, searchTerm || '');
    const filteredDevs = await CommitModel.aggregate([
        {
            $match: {
                collectionName,
                project,
                'author.date': inDateRange(startDate, endDate),
                '$and': [
                    { 'author.email': { $exists: true } },
                    ...(searchTerm && !isRepoSearch
                        ? [{ 'author.name': { $regex: new RegExp(searchTerm, 'i') } }]
                        : []),
                ],
            },
        },
        {
            $addFields: {
                ...(searchTerm && isRepoSearch
                    ? { lowerAuthorEmail: { $toLower: '$author.email' } }
                    : {}),
            },
        },
        ...(searchTerm && isRepoSearch && devEmails
            ? [{ $match: { lowerAuthorEmail: { $in: devEmails } } }]
            : []),
        { $group: { _id: { authorEmail: { $toLower: '$author.email' } } } },
    ])
        .count('total')
        .exec();
    return ((_a = filteredDevs[0]) === null || _a === void 0 ? void 0 : _a.total) || 0;
};
export const getDevsCommittedToRepositoryIds = async (queryContext, repositoryIds) => {
    const { collectionName, project, startDate, endDate } = fromContext(queryContext);
    return CommitModel.aggregate([
        {
            $match: {
                collectionName,
                project,
                'repositoryId': { $in: repositoryIds },
                'author.date': inDateRange(startDate, endDate),
                'author.email': { $exists: true },
            },
        },
        {
            $group: {
                _id: { repositoryId: '$repositoryId', email: { $toLower: '$author.email' } },
                name: { $first: '$author.name' },
                imageUrl: { $first: '$author.imageUrl' },
                totalCommits: { $sum: 1 },
            },
        },
        { $sort: { totalCommits: -1 } },
        {
            $group: {
                _id: '$_id.repositoryId',
                count: { $sum: 1 },
                devs: {
                    $push: {
                        name: '$name',
                        email: '$_id.email',
                        imageUrl: '$imageUrl',
                    },
                },
            },
        },
        {
            $project: {
                _id: 0,
                repositoryId: '$_id',
                count: 1,
                top: { $slice: ['$devs', 5] },
            },
        },
    ]);
};
export const getWeeklyCommitsForRepositoryIds = async (queryContext, repositoryIds) => {
    const { collectionName, project, startDate, endDate } = fromContext(queryContext);
    return CommitModel.aggregate([
        {
            $match: {
                collectionName,
                project,
                'repositoryId': { $in: repositoryIds },
                'author.date': inDateRange(startDate, endDate),
                'author.email': { $exists: true },
            },
        },
        { $sort: { authorDate: 1 } },
        {
            $group: {
                _id: {
                    repositoryId: '$repositoryId',
                    weekIndex: weekIndexValue(startDate, '$author.date'),
                },
                totalCommits: { $sum: 1 },
            },
        },
        {
            $group: {
                _id: '$_id.repositoryId',
                repositoryId: { $first: '$_id.repositoryId' },
                commits: {
                    $push: {
                        weekIndex: '$_id.weekIndex',
                        count: '$totalCommits',
                    },
                },
            },
        },
    ]);
};
