<commit_before>﻿using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Threading.Tasks;
using GitHub.Extensions;
using GitHub.InlineReviews.Models;
using GitHub.Models;
using GitHub.Services;
using ReactiveUI;
using System.Threading;
using System.Reactive.Subjects;
using static System.FormattableString;

namespace GitHub.InlineReviews.Services
{
    /// <summary>
    /// A pull request session used to display inline reviews.
    /// </summary>
    /// <remarks>
    /// A pull request session represents the real-time state of a pull request in the IDE.
    /// It takes the pull request model and updates according to the current state of the
    /// repository on disk and in the editor.
    /// </remarks>
    [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Design", "CA1001:TypesThatOwnDisposableFieldsShouldBeDisposable",
        Justification = "PullRequestSession is shared and shouldn't be disposed")]
    public class PullRequestSession : ReactiveObject, IPullRequestSession
    {
        readonly IPullRequestSessionService service;
        readonly Dictionary<string, PullRequestSessionFile> fileIndex = new Dictionary<string, PullRequestSessionFile>();
        readonly SemaphoreSlim getFilesLock = new SemaphoreSlim(1);
        bool isCheckedOut;
        string mergeBase;
        IReadOnlyList<IPullRequestSessionFile> files;
        IPullRequestModel pullRequest;
        string pullRequestNodeId;
        Subject<IPullRequestModel> pullRequestChanged = new Subject<IPullRequestModel>();
        bool hasPendingReview;
        string pendingReviewNodeId { get; set; }

        public PullRequestSession(
            IPullRequestSessionService service,
            IAccount user,
            IPullRequestModel pullRequest,
            ILocalRepositoryModel localRepository,
            string repositoryOwner,
            bool isCheckedOut)
        {
            Guard.ArgumentNotNull(service, nameof(service));
            Guard.ArgumentNotNull(user, nameof(user));
            Guard.ArgumentNotNull(pullRequest, nameof(pullRequest));
            Guard.ArgumentNotNull(localRepository, nameof(localRepository));

            this.service = service;
            this.isCheckedOut = isCheckedOut;
            this.pullRequest = pullRequest;
            User = user;
            LocalRepository = localRepository;
            RepositoryOwner = repositoryOwner;
            UpdatePendingReview();
        }

        /// <inheritdoc/>
        public async Task<IReadOnlyList<IPullRequestSessionFile>> GetAllFiles()
        {
            if (files == null)
            {
                files = await CreateAllFiles();
            }

            return files;
        }

        /// <inheritdoc/>
        public async Task<IPullRequestSessionFile> GetFile(
            string relativePath,
            string commitSha = "HEAD")
        {
            await getFilesLock.WaitAsync();

            try
            {
                PullRequestSessionFile file;
                var normalizedPath = relativePath.Replace("\\", "/");
                var key = normalizedPath + '@' + commitSha;

                if (!fileIndex.TryGetValue(key, out file))
                {
                    file = new PullRequestSessionFile(normalizedPath, commitSha);
                    await UpdateFile(file);
                    fileIndex.Add(key, file);
                }

                return file;
            }
            finally
            {
                getFilesLock.Release();
            }
        }

        /// <inheritdoc/>
        public async Task<string> GetMergeBase()
        {
            if (mergeBase == null)
            {
                mergeBase = await service.GetPullRequestMergeBase(LocalRepository, PullRequest);
            }

            return mergeBase;
        }

        /// <inheritdoc/>
        public string GetRelativePath(string path)
        {
            if (Path.IsPathRooted(path))
            {
                var basePath = LocalRepository.LocalPath;

                if (path.StartsWith(basePath, StringComparison.OrdinalIgnoreCase) && path.Length > basePath.Length + 1)
                {
                    return path.Substring(basePath.Length + 1);
                }
            }

            return null;
        }

        /// <inheritdoc/>
        public async Task<IPullRequestReviewCommentModel> PostReviewComment(
            string body,
            string commitId,
            string path,
            IReadOnlyList<DiffChunk> diff,
            int position)
        {
            IPullRequestReviewCommentModel model;

            if (!HasPendingReview)
            {
                var pullRequestNodeId = await GetPullRequestNodeId();
                model = await service.PostStandaloneReviewComment(
                    LocalRepository,
                    User,
                    pullRequestNodeId,
                    body,
                    commitId,
                    path,
                    position);
            }
            else
            {
                model = await service.PostPendingReviewComment(
                    LocalRepository,
                    User,
                    pendingReviewNodeId,
                    body,
                    commitId,
                    path,
                    position);
            }

            await AddComment(model);
            return model;
        }

        /// <inheritdoc/>
        public async Task DeleteComment(
            int number)
        {
            await service.DeleteComment(
                LocalRepository,
                RepositoryOwner,
                User,
                number);

            await RemoveComment(number);
        }

        /// <inheritdoc/>
        public async Task<IPullRequestReviewCommentModel> EditComment(string commentNodeId, string body)
        {
            var model = await service.EditComment(
                LocalRepository,
                RepositoryOwner,
                User,
                commentNodeId,
                body);

            await ReplaceComment(model);
            return model;
        }

        /// <inheritdoc/>
        public async Task<IPullRequestReviewCommentModel> PostReviewComment(
            string body,
            string inReplyToNodeId)
        {
            IPullRequestReviewCommentModel model;

            if (!HasPendingReview)
            {
                model = await service.PostStandaloneReviewCommentReply(
                    LocalRepository,
                    User,
                    pullRequestNodeId,
                    body,
                    inReplyToNodeId);
            }
            else
            {
                model = await service.PostPendingReviewCommentReply(
                    LocalRepository,<commit_msg>Adding missing check to update the pullRequestNodeId<commit_after>﻿using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Threading.Tasks;
using GitHub.Extensions;
using GitHub.InlineReviews.Models;
using GitHub.Models;
using GitHub.Services;
using ReactiveUI;
using System.Threading;
using System.Reactive.Subjects;
using static System.FormattableString;

namespace GitHub.InlineReviews.Services
{
    /// <summary>
    /// A pull request session used to display inline reviews.
    /// </summary>
    /// <remarks>
    /// A pull request session represents the real-time state of a pull request in the IDE.
    /// It takes the pull request model and updates according to the current state of the
    /// repository on disk and in the editor.
    /// </remarks>
    [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Design", "CA1001:TypesThatOwnDisposableFieldsShouldBeDisposable",
        Justification = "PullRequestSession is shared and shouldn't be disposed")]
    public class PullRequestSession : ReactiveObject, IPullRequestSession
    {
        readonly IPullRequestSessionService service;
        readonly Dictionary<string, PullRequestSessionFile> fileIndex = new Dictionary<string, PullRequestSessionFile>();
        readonly SemaphoreSlim getFilesLock = new SemaphoreSlim(1);
        bool isCheckedOut;
        string mergeBase;
        IReadOnlyList<IPullRequestSessionFile> files;
        IPullRequestModel pullRequest;
        string pullRequestNodeId;
        Subject<IPullRequestModel> pullRequestChanged = new Subject<IPullRequestModel>();
        bool hasPendingReview;
        string pendingReviewNodeId { get; set; }

        public PullRequestSession(
            IPullRequestSessionService service,
            IAccount user,
            IPullRequestModel pullRequest,
            ILocalRepositoryModel localRepository,
            string repositoryOwner,
            bool isCheckedOut)
        {
            Guard.ArgumentNotNull(service, nameof(service));
            Guard.ArgumentNotNull(user, nameof(user));
            Guard.ArgumentNotNull(pullRequest, nameof(pullRequest));
            Guard.ArgumentNotNull(localRepository, nameof(localRepository));

            this.service = service;
            this.isCheckedOut = isCheckedOut;
            this.pullRequest = pullRequest;
            User = user;
            LocalRepository = localRepository;
            RepositoryOwner = repositoryOwner;
            UpdatePendingReview();
        }

        /// <inheritdoc/>
        public async Task<IReadOnlyList<IPullRequestSessionFile>> GetAllFiles()
        {
            if (files == null)
            {
                files = await CreateAllFiles();
            }

            return files;
        }

        /// <inheritdoc/>
        public async Task<IPullRequestSessionFile> GetFile(
            string relativePath,
            string commitSha = "HEAD")
        {
            await getFilesLock.WaitAsync();

            try
            {
                PullRequestSessionFile file;
                var normalizedPath = relativePath.Replace("\\", "/");
                var key = normalizedPath + '@' + commitSha;

                if (!fileIndex.TryGetValue(key, out file))
                {
                    file = new PullRequestSessionFile(normalizedPath, commitSha);
                    await UpdateFile(file);
                    fileIndex.Add(key, file);
                }

                return file;
            }
            finally
            {
                getFilesLock.Release();
            }
        }

        /// <inheritdoc/>
        public async Task<string> GetMergeBase()
        {
            if (mergeBase == null)
            {
                mergeBase = await service.GetPullRequestMergeBase(LocalRepository, PullRequest);
            }

            return mergeBase;
        }

        /// <inheritdoc/>
        public string GetRelativePath(string path)
        {
            if (Path.IsPathRooted(path))
            {
                var basePath = LocalRepository.LocalPath;

                if (path.StartsWith(basePath, StringComparison.OrdinalIgnoreCase) && path.Length > basePath.Length + 1)
                {
                    return path.Substring(basePath.Length + 1);
                }
            }

            return null;
        }

        /// <inheritdoc/>
        public async Task<IPullRequestReviewCommentModel> PostReviewComment(
            string body,
            string commitId,
            string path,
            IReadOnlyList<DiffChunk> diff,
            int position)
        {
            IPullRequestReviewCommentModel model;

            if (!HasPendingReview)
            {
                var pullRequestNodeId = await GetPullRequestNodeId();
                model = await service.PostStandaloneReviewComment(
                    LocalRepository,
                    User,
                    pullRequestNodeId,
                    body,
                    commitId,
                    path,
                    position);
            }
            else
            {
                model = await service.PostPendingReviewComment(
                    LocalRepository,
                    User,
                    pendingReviewNodeId,
                    body,
                    commitId,
                    path,
                    position);
            }

            await AddComment(model);
            return model;
        }

        /// <inheritdoc/>
        public async Task DeleteComment(
            int number)
        {
            await service.DeleteComment(
                LocalRepository,
                RepositoryOwner,
                User,
                number);

            await RemoveComment(number);
        }

        /// <inheritdoc/>
        public async Task<IPullRequestReviewCommentModel> EditComment(string commentNodeId, string body)
        {
            var model = await service.EditComment(
                LocalRepository,
                RepositoryOwner,
                User,
                commentNodeId,
                body);

            await ReplaceComment(model);
            return model;
        }

        /// <inheritdoc/>
        public async Task<IPullRequestReviewCommentModel> PostReviewComment(
            string body,
            string inReplyToNodeId)
        {
            IPullRequestReviewCommentModel model;

            if (!HasPendingReview)
            {
                var pullRequestNodeId = await GetPullRequestNodeId();
                model = await service.PostStandaloneReviewCommentReply(
                    LocalRepository,
                    User,
                    pullRequestNodeId,
                    body,
                    inReplyToNodeId);
            }
            else
            {
                model = await service.PostPendingReviewCommentReply(
                    LocalRepository,
{'fraction_non_alphanumeric': 0.07025421887448395, 'fraction_numerical': 0.0010139784167451293, 'mean_word_length': 1.5261617270398828, 'pattern_counts': {'":': 0, '<': 53, '<?xml version=': 0, '>': 55, 'https://': 0, 'lorem ipsum': 0, 'www.': 0, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 0, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '369683', 'n_tokens_mistral': 3214, 'n_tokens_neox': 3076, 'n_words': 729}