context: using System;
  using System.Collections.Generic;
  using System.Linq;
  using System.Text.RegularExpressions;
  using UberDeployer.Common.SyntaxSugar;
  
  namespace UberDeployer.Core.Domain
  {
    public class EnvironmentInfo
    {
      private static readonly Regex _DriveLetterRegex = new Regex(@"^(?<DriveLetter>[a-z]):\\", RegexOptions.Compiled | RegexOptions.IgnoreCase);
  
      private readonly List<String> _webServerMachines;
      private readonly List<String> _schedulerServerTasksMachineNames;
      private readonly List<String> _schedulerServerBinariesMachineNames;
      private readonly Dictionary<string, EnvironmentUser> _environmentUsersByIdDict;
      private readonly Dictionary<string, IisAppPoolInfo> _appPoolInfosByNameDict;
      private readonly Dictionary<string, DatabaseServer> _databaseServersByIdDict;
      private readonly Dictionary<string, ProjectToFailoverClusterGroupMapping> _projectToFailoverClusterGroupMappingsDict;
      private readonly Dictionary<string, WebAppProjectConfigurationOverride> _webAppProjectConfigurationOverridesDict;
      private readonly Dictionary<string, DbProjectConfigurationOverride> _dbProjectConfigurationOverridesDict;
  
      public EnvironmentInfo(
        string name,
        string configurationTemplateName,
        string appServerMachineName,
        string failoverClusterMachineName,
        IEnumerable<string> webServerMachineNames,
        string terminalServerMachineName,
        IEnumerable<string> schedulerServerTasksMachineNames,
        IEnumerable<string> schedulerServerBinariesMachineNames,
        string ntServicesBaseDirPath,
        string webAppsBaseDirPath,
        string schedulerAppsBaseDirPath,
        string terminalAppsBaseDirPath,
        bool enableFailoverClusteringForNtServices,      
        IEnumerable<EnvironmentUser> environmentUsers,
        IEnumerable<IisAppPoolInfo> appPoolInfos,
        IEnumerable<DatabaseServer> databaseServers,
        IEnumerable<ProjectToFailoverClusterGroupMapping> projectToFailoverClusterGroupMappings,
        IEnumerable<WebAppProjectConfigurationOverride> webAppProjectConfigurationOverrides,
        IEnumerable<DbProjectConfigurationOverride> dbProjectConfigurations,
        string terminalAppShortcutFolder,
        string manualDeploymentPackageDirPath
        )
      {
        Guard.NotNullNorEmpty(name, "name");
        Guard.NotNull(configurationTemplateName, "configurationTemplateName");
        Guard.NotNullNorEmpty(appServerMachineName, "appServerMachineName");
        Guard.NotNull(failoverClusterMachineName, "failoverClusterMachineName");
        
        if (webServerMachineNames == null)
        {
          throw new ArgumentNullException("webServerMachineNames");
        }
  
        if (schedulerServerTasksMachineNames == null)
        {
          throw new ArgumentNullException("schedulerServerTasksMachineNames");
        }
  
        if (schedulerServerBinariesMachineNames == null)
        {
          throw new ArgumentNullException("schedulerServerBinariesMachineNames");
        }
  
        Guard.NotNullNorEmpty(terminalServerMachineName, "terminalServerMachineName");
        Guard.NotNullNorEmpty(ntServicesBaseDirPath, "ntServicesBaseDirPath");
        Guard.NotNullNorEmpty(webAppsBaseDirPath, "webAppsBaseDirPath");
        Guard.NotNullNorEmpty(schedulerAppsBaseDirPath, "schedulerAppsBaseDirPath");
        Guard.NotNullNorEmpty(terminalAppsBaseDirPath, "terminalAppsBaseDirPath");
  
        if (environmentUsers == null)
        {
          throw new ArgumentNullException("environmentUsers");
        }
  
        if (appPoolInfos == null)
        {
          throw new ArgumentNullException("appPoolInfos");
        }
  
        if (enableFailoverClusteringForNtServices && string.IsNullOrEmpty(failoverClusterMachineName))
        {
          throw new ArgumentException("If enableFailoverClusteringForNtServices is set, failoverClusterMachineName must not be empty.", "enableFailoverClusteringForNtServices");
        }
  
        if (webAppProjectConfigurationOverrides == null)
        {
          throw new ArgumentNullException("webAppProjectConfigurationOverrides");
        }
  
        if (projectToFailoverClusterGroupMappings == null)
        {
          throw new ArgumentNullException("projectToFailoverClusterGroupMappings");
        }
  
        if (dbProjectConfigurations == null)
        {
          throw new ArgumentNullException("dbProjectConfigurations");
        }
  
        Guard.NotNullNorEmpty(name, "terminalAppShortcutPath");      
  
        Name = name;
        ConfigurationTemplateName = configurationTemplateName;
        AppServerMachineName = appServerMachineName;
        FailoverClusterMachineName = failoverClusterMachineName;
        _webServerMachines = webServerMachineNames.ToList();
        TerminalServerMachineName = terminalServerMachineName;
        _schedulerServerTasksMachineNames = schedulerServerTasksMachineNames.ToList();
  
        if (_schedulerServerTasksMachineNames.Count == 0)
        {
          throw new ArgumentException("At least one scheduler server tasks machine name must be present.", "schedulerServerTasksMachineNames");
        }
  
        _schedulerServerBinariesMachineNames = schedulerServerBinariesMachineNames.ToList();
  
        if (_schedulerServerBinariesMachineNames.Count == 0)
        {
          throw new ArgumentException("At least one scheduler server binaries machine name must be present.", "schedulerServerTasksMachineNames");
        }
  
        NtServicesBaseDirPath = ntServicesBaseDirPath;
        WebAppsBaseDirPath = webAppsBaseDirPath;
        SchedulerAppsBaseDirPath = schedulerAppsBaseDirPath;
        TerminalAppsBaseDirPath = terminalAppsBaseDirPath;
        EnableFailoverClusteringForNtServices = enableFailoverClusteringForNtServices;
  
        _environmentUsersByIdDict = environmentUsers.ToDictionary(e => e.Id);
        _appPoolInfosByNameDict = appPoolInfos.ToDictionary(e => e.Name);
        _databaseServersByIdDict = databaseServers.ToDictionary(e => e.Id);
  
        _projectToFailoverClusterGroupMappingsDict = projectToFailoverClusterGroupMappings.ToDictionary(e => e.ProjectName);
  
        _webAppProjectConfigurationOverridesDict = webAppProjectConfigurationOverrides.ToDictionary(e => e.ProjectName);
        _dbProjectConfigurationOverridesDict = dbProjectConfigurations.ToDictionary(e => e.ProjectName);
  
        TerminalAppsShortcutFolder = terminalAppShortcutFolder;
        ManualDeploymentPackageDirPath = manualDeploymentPackageDirPath;
      }
  
      public static string GetNetworkPath(string machineName, string absoluteLocalPath)
      {
        Guard.NotNullNorEmpty(machineName, "machineName");
        Guard.NotNullNorEmpty(absoluteLocalPath, "absoluteLocalPath");
  
        if (absoluteLocalPath.StartsWith(@"\\"))
        {
          throw new ArgumentException(string.Format("The path is already a network path. Absolute local path: '{0}'.", absoluteLocalPath), "absoluteLocalPath");
        }
  
        Match driveLetterMatch = _DriveLetterRegex.Match(absoluteLocalPath);
  
        if (!driveLetterMatch.Success)
        {
          throw new ArgumentException(string.Format("The path is not an absolute local path, ie. it doesn't start with a drive letter followed by a colon and a backslash. Absolute local path: '{0}'.", absoluteLocalPath), "absoluteLocalPath");
        }
  
        string driveLetter = driveLetterMatch.Groups["DriveLetter"].Value;
  
        return
          string.Format(
            "\\\\{0}\\{1}$\\{2}",
            machineName,
            driveLetter,
            absoluteLocalPath.Substring(driveLetterMatch.Length));
      }
  
      public string GetAppServerNetworkPath(string absoluteLocalPath)
      {
        Guard.NotNullNorEmpty(absoluteLocalPath, "absoluteLocalPath");
  
        return GetNetworkPath(AppServerMachineName, absoluteLocalPath);
      }
  
      public string GetSchedulerServerNetworkPath(string schedulerServerMachineName, string absoluteLocalPath)
      {
        Guard.NotNullNorEmpty(absoluteLocalPath, "absoluteLocalPath");
  
        return GetNetworkPath(schedulerServerMachineName, absoluteLocalPath);
      }
  
      public string GetWebServerNetworkPath(string webServerMachineName, string absoluteLocalPath)
      {
        Guard.NotNullNorEmpty(webServerMachineName, "webServerMachineName");
        Guard.NotNullNorEmpty(absoluteLocalPath, "absoluteLocalPath");
  
        return GetNetworkPath(webServerMachineName, absoluteLocalPath);
      }
  
      public string GetTerminalServerNetworkPath(string absoluteLocalPath)
      {
        Guard.NotNullNorEmpty(absoluteLocalPath, "absoluteLocalPath");
  
        return GetNetworkPath(TerminalServerMachineName, absoluteLocalPath);
      }
  
      public EnvironmentUser GetEnvironmentUser(string userId)
      {
        Guard.NotNullNorEmpty(userId, "userId");
  
        EnvironmentUser environmentUser;
  
        if (_environmentUsersByIdDict.TryGetValue(userId, out environmentUser))
        {
          return environmentUser;
        }
  
        throw new InvalidOperationException(string.Format("There's no environment user with id '{0}' defined in environment named '{1}'.", userId, Name));
      }
  
      public string GetFailoverClusterGroupNameForProject(string projectName)
      {
        Guard.NotNullNorEmpty(projectName, "projectName");
  
        ProjectToFailoverClusterGroupMapping mapping;
  
        if (_projectToFailoverClusterGroupMappingsDict.TryGetValue(projectName, out mapping))
        {
          return mapping.ClusterGroupName;
        }
  
        return null;
      }
  
      public WebAppProjectConfiguration GetWebAppProjectConfiguration(WebAppProjectInfo webAppProjectInfo)
      {
        Guard.NotNull(webAppProjectInfo, "webAppProjectInfo");
  
        string projectName = webAppProjectInfo.Name;
        string appPoolId = webAppProjectInfo.AppPoolId;
        string webSiteName = webAppProjectInfo.WebSiteName;
        string webAppDirName = webAppProjectInfo.WebAppDirName;
        string webAppName = webAppProjectInfo.WebAppName;
  
        WebAppProjectConfigurationOverride webAppProjectConfigurationOverride =
          FindWebAppProjectConfigurationOverride(projectName);
  
        if (webAppProjectConfigurationOverride != null)
        {
  
gt:         if (webAppProjectConfigurationOverride.AppPoolId != null)