context: using System;
  using System.Collections.Generic;
  using System.ComponentModel.Composition;
  using System.Diagnostics;
  using System.Globalization;
  using System.IO;
  using System.Linq;
  using System.Net;
  using System.Net.Http;
  using System.Net.Http.Headers;
  using System.Reactive.Concurrency;
  using System.Reactive.Linq;
  using System.Text;
  using System.Text.RegularExpressions;
  using System.Threading;
  using System.Threading.Tasks;
  using System.Xml.Linq;
  using System.Xml.XPath;
  using GitCommands.Utils;
  using GitUI;
  using GitUIPluginInterfaces;
  using GitUIPluginInterfaces.BuildServerIntegration;
  using JetBrains.Annotations;
  using Microsoft.VisualStudio.Threading;
  
  namespace TeamCityIntegration
  {
      [MetadataAttribute]
      [AttributeUsage(AttributeTargets.Class)]
      public class TeamCityIntegrationMetadataAttribute : BuildServerAdapterMetadataAttribute
      {
          public TeamCityIntegrationMetadataAttribute(string buildServerType)
              : base(buildServerType)
          {
          }
  
          public override string CanBeLoaded
          {
              get
              {
                  if (EnvUtils.IsNet4FullOrHigher())
                  {
                      return null;
                  }
                  else
                  {
                      return ".Net 4 full framework required";
                  }
              }
          }
      }
  
      [Export(typeof(IBuildServerAdapter))]
      [TeamCityIntegrationMetadata(PluginName)]
      [PartCreationPolicy(CreationPolicy.NonShared)]
      internal class TeamCityAdapter : IBuildServerAdapter
      {
          public const string PluginName = "TeamCity";
          private IBuildServerWatcher _buildServerWatcher;
  
          private HttpClientHandler _httpClientHandler;
          private HttpClient _httpClient;
  
          private string _httpClientHostSuffix;
  
          private readonly List<JoinableTask<IEnumerable<string>>> _getBuildTypesTask = new List<JoinableTask<IEnumerable<string>>>();
  
          private CookieContainer _teamCityNtlmAuthCookie;
  
          private string HostName { get; set; }
  
          private string[] ProjectNames { get; set; }
  
          private Regex BuildIdFilter { get; set; }
  
          private CookieContainer GetTeamCityNtlmAuthCookie(string serverUrl, IBuildServerCredentials buildServerCredentials)
          {
              if (_teamCityNtlmAuthCookie is not null)
              {
                  return _teamCityNtlmAuthCookie;
              }
  
              string url = serverUrl + "ntlmLogin.html";
              var cookieContainer = new CookieContainer();
              var request = (HttpWebRequest)WebRequest.Create(url);
              request.CookieContainer = cookieContainer;
  
              if (buildServerCredentials is not null
                  && !string.IsNullOrEmpty(buildServerCredentials.Username)
                  && !string.IsNullOrEmpty(buildServerCredentials.Password))
              {
                  request.Credentials = new NetworkCredential(buildServerCredentials.Username, buildServerCredentials.Password);
              }
              else
              {
                  request.Credentials = CredentialCache.DefaultCredentials;
              }
  
              request.PreAuthenticate = true;
              request.GetResponse();
  
              _teamCityNtlmAuthCookie = cookieContainer;
              return _teamCityNtlmAuthCookie;
          }
  
          public string LogAsGuestUrlParameter { get; set; }
  
          public void Initialize(IBuildServerWatcher buildServerWatcher, ISettingsSource config, Action openSettings, Func<ObjectId, bool> isCommitInRevisionGrid = null)
          {
              if (_buildServerWatcher is not null)
              {
                  throw new InvalidOperationException("Already initialized");
              }
  
              _buildServerWatcher = buildServerWatcher;
  
              ProjectNames = buildServerWatcher.ReplaceVariables(config.GetString("ProjectName", ""))
                  .Split(new[] { '|' }, StringSplitOptions.RemoveEmptyEntries);
  
              var buildIdFilerSetting = config.GetString("BuildIdFilter", "");
              if (!BuildServerSettingsHelper.IsRegexValid(buildIdFilerSetting))
              {
                  return;
              }
  
              BuildIdFilter = new Regex(buildIdFilerSetting, RegexOptions.Compiled);
              HostName = config.GetString("BuildServerUrl", null);
              LogAsGuestUrlParameter = config.GetBool("LogAsGuest", false) ? "&guest=1" : string.Empty;
  
              if (!string.IsNullOrEmpty(HostName))
              {
                  InitializeHttpClient(HostName);
                  if (ProjectNames.Length > 0)
                  {
                      _getBuildTypesTask.Clear();
                      foreach (var name in ProjectNames)
                      {
                          _getBuildTypesTask.Add(ThreadHelper.JoinableTaskFactory.RunAsync(async () =>
                          {
                              var response = await GetProjectFromNameXmlResponseAsync(name, CancellationToken.None).ConfigureAwait(false);
                              return from element in response.XPathSelectElements("/project/buildTypes/buildType")
                                     select element.Attribute("id").Value;
                          }));
                      }
                  }
              }
          }
  
          public void InitializeHttpClient(string hostname)
          {
              CreateNewHttpClient(hostname);
              UpdateHttpClientOptionsGuestAuth();
          }
  
          private void CreateNewHttpClient(string hostName)
          {
              _httpClientHandler = new HttpClientHandler();
              _httpClient = new HttpClient(_httpClientHandler)
              {
                  Timeout = TimeSpan.FromMinutes(2),
                  BaseAddress = hostName.Contains("://")
                      ? new Uri(hostName, UriKind.Absolute)
                      : new Uri(string.Format("{0}://{1}", Uri.UriSchemeHttp, hostName), UriKind.Absolute)
              };
              _httpClient.DefaultRequestHeaders.Accept.Add(new MediaTypeWithQualityHeaderValue("application/xml"));
          }
  
          /// <summary>
          /// Gets a unique key which identifies this build server.
          /// </summary>
          public string UniqueKey => _httpClient.BaseAddress.Host;
  
          public IObservable<BuildInfo> GetFinishedBuildsSince(IScheduler scheduler, DateTime? sinceDate = null)
          {
              return GetBuilds(scheduler, sinceDate, false);
          }
  
          public IObservable<BuildInfo> GetRunningBuilds(IScheduler scheduler)
          {
              return GetBuilds(scheduler, null, true);
          }
  
          public IObservable<BuildInfo> GetBuilds(IScheduler scheduler, DateTime? sinceDate = null, bool? running = null)
          {
              if (_httpClient is null || _httpClient.BaseAddress is null || ProjectNames.Length == 0)
              {
                  return Observable.Empty<BuildInfo>(scheduler);
              }
  
              return Observable.Create<BuildInfo>((observer, cancellationToken) =>
                  Task.Run(
                      () => scheduler.Schedule(() => ObserveBuilds(sinceDate, running, observer, cancellationToken))));
          }
  
          private void ObserveBuilds(DateTime? sinceDate, bool? running, IObserver<BuildInfo> observer, CancellationToken cancellationToken)
          {
              try
              {
                  if (_getBuildTypesTask.Any(task => PropagateTaskAnomalyToObserver(task.Task, observer)))
                  {
                      return;
                  }
  
                  var localObserver = observer;
                  var buildTypes = _getBuildTypesTask.SelectMany(t => t.Join()).Where(id => BuildIdFilter.IsMatch(id));
                  var buildIdTasks = buildTypes.Select(buildTypeId => GetFilteredBuildsXmlResponseAsync(buildTypeId, cancellationToken, sinceDate, running)).ToArray();
  
                  Task.Factory
                      .ContinueWhenAll(
                          buildIdTasks,
                          completedTasks =>
                              {
                                  var buildIds = completedTasks.Where(task => task.Status == TaskStatus.RanToCompletion)
                                                               .SelectMany(
                                                                   buildIdTask =>
                                                                   buildIdTask.CompletedResult()
                                                                              .XPathSelectElements("/builds/build")
                                                                              .Select(x => x.Attribute("id").Value))
                                                               .ToArray();
  
                                  NotifyObserverOfBuilds(buildIds, observer, cancellationToken);
                              },
                          cancellationToken,
                          TaskContinuationOptions.AttachedToParent | TaskContinuationOptions.ExecuteSynchronously,
                          TaskScheduler.Current)
                      .ContinueWith(
                          task => localObserver.OnError(task.Exception),
                          CancellationToken.None,
                          TaskContinuationOptions.AttachedToParent | TaskContinuationOptions.ExecuteSynchronously | TaskContinuationOptions.OnlyOnFaulted,
                          TaskScheduler.Current);
              }
              catch (OperationCanceledException)
              {
                  // Do nothing, the observer is already stopped
              }
              catch (Exception ex)
              {
                  observer.OnError(ex);
              }
          }
  
          private void NotifyObserverOfBuilds(string[] buildIds, IObserver<BuildInfo> observer, CancellationToken cancellationToken)
          {
              var tasks = new List<Task>(8);
              var buildsLeft = buildIds.Length;
  
              foreach (var buildId in buildIds.OrderByDescending(int.Parse))
              {
                  var notifyObserverTask =
                      GetBuildFromIdXmlResponseAsync(buildId, cancellationToken)
                          .ContinueWith(
                              task =>
                                  {
                                      if (task.Status == TaskStatus.RanToCompletion)
                                      {
  
gt:                                         var buildDetails = task.CompletedResult();