using Renci.SshNet.TestTools.OpenSSH;
namespace Renci.SshNet.IntegrationTests
{
    internal sealed class RemoteSshdConfig
    {
        private const string SshdConfigFilePath = "/etc/ssh/sshd_config";
        private static readonly Encoding Utf8NoBom = new UTF8Encoding(false, true);
        private readonly RemoteSshd _remoteSshd;
        private readonly IConnectionInfoFactory _connectionInfoFactory;
        private readonly SshdConfig _config;
        public RemoteSshdConfig(RemoteSshd remoteSshd, IConnectionInfoFactory connectionInfoFactory)
        {
            _remoteSshd = remoteSshd;
            _connectionInfoFactory = connectionInfoFactory;
            using (var client = new ScpClient(_connectionInfoFactory.Create()))
            {
                client.Connect();
                using (var memoryStream = new MemoryStream())
                {
                    client.Download(SshdConfigFilePath, memoryStream);
                    memoryStream.Position = 0;
                    _config = SshdConfig.LoadFrom(memoryStream, Encoding.UTF8);
                }
            }
        }
        /// 
        /// Specifies whether challenge-response authentication is allowed.
        /// 
        ///  to allow challenge-response authentication.
        /// 
        /// The current  instance.
        /// 
        public RemoteSshdConfig WithChallengeResponseAuthentication(bool? value)
        {
            _config.ChallengeResponseAuthentication = value;
            return this;
        }
        /// 
        /// Specifies whether to allow keyboard-interactive authentication.
        /// 
        ///  to allow keyboard-interactive authentication.
        /// 
        /// The current  instance.
        /// 
        public RemoteSshdConfig WithKeyboardInteractiveAuthentication(bool value)
        {
            _config.KeyboardInteractiveAuthentication = value;
            return this;
        }
        /// 
        /// Specifies whether sshd should print /etc/motd when a user logs in interactively.
        /// 
        ///  if sshd should print /etc/motd when a user logs in interactively.
        /// 
        /// The current  instance.
        /// 
        public RemoteSshdConfig PrintMotd(bool? value = true)
        {
            _config.PrintMotd = value;
            return this;
        }
        /// 
        /// Specifies whether TTY is permitted.
        /// 
        ///  to permit TTY.
        /// 
        /// The current  instance.
        /// 
        public RemoteSshdConfig PermitTTY(bool? value = true)
        {
            _config.PermitTTY = value;
            return this;
        }
        /// 
        /// Specifies whether TCP forwarding is permitted.
        /// 
        ///  to allow TCP forwarding.
        /// 
        /// The current  instance.
        /// 
        public RemoteSshdConfig AllowTcpForwarding(bool? value = true)
        {
            _config.AllowTcpForwarding = value;
            return this;
        }
        public RemoteSshdConfig WithAuthenticationMethods(string user, string authenticationMethods)
        {
            var sshNetMatch = _config.Matches.Find(m => m.Users.Contains(user));
            if (sshNetMatch is null)
            {
                sshNetMatch = new Match(new[] { user }, Array.Empty());
                _config.Matches.Add(sshNetMatch);
            }
            sshNetMatch.AuthenticationMethods = authenticationMethods;
            return this;
        }
        public RemoteSshdConfig ClearCiphers()
        {
            _config.Ciphers.Clear();
            return this;
        }
        public RemoteSshdConfig AddCipher(Cipher cipher)
        {
            _config.Ciphers.Add(cipher);
            return this;
        }
        public RemoteSshdConfig ClearKeyExchangeAlgorithms()
        {
            _config.KeyExchangeAlgorithms.Clear();
            return this;
        }
        public RemoteSshdConfig AddKeyExchangeAlgorithm(KeyExchangeAlgorithm keyExchangeAlgorithm)
        {
            _config.KeyExchangeAlgorithms.Add(keyExchangeAlgorithm);
            return this;
        }
        public RemoteSshdConfig ClearPublicKeyAcceptedAlgorithms()
        {
            _config.PublicKeyAcceptedAlgorithms.Clear();
            return this;
        }
        public RemoteSshdConfig AddPublicKeyAcceptedAlgorithm(PublicKeyAlgorithm publicKeyAlgorithm)
        {
            _config.PublicKeyAcceptedAlgorithms.Add(publicKeyAlgorithm);
            return this;
        }
        public RemoteSshdConfig ClearMessageAuthenticationCodeAlgorithms()
        {
            _config.MessageAuthenticationCodeAlgorithms.Clear();
            return this;
        }
        public RemoteSshdConfig AddMessageAuthenticationCodeAlgorithm(MessageAuthenticationCodeAlgorithm messageAuthenticationCodeAlgorithm)
        {
            _config.MessageAuthenticationCodeAlgorithms.Add(messageAuthenticationCodeAlgorithm);
            return this;
        }
        public RemoteSshdConfig ClearHostKeyAlgorithms()
        {
            _config.HostKeyAlgorithms.Clear();
            return this;
        }
        public RemoteSshdConfig AddHostKeyAlgorithm(HostKeyAlgorithm hostKeyAlgorithm)
        {
            _config.HostKeyAlgorithms.Add(hostKeyAlgorithm);
            return this;
        }
        public RemoteSshdConfig ClearSubsystems()
        {
            _config.Subsystems.Clear();
            return this;
        }
        public RemoteSshdConfig AddSubsystem(Subsystem subsystem)
        {
            _config.Subsystems.Add(subsystem);
            return this;
        }
        public RemoteSshdConfig WithLogLevel(LogLevel logLevel)
        {
            _config.LogLevel = logLevel;
            return this;
        }
        public RemoteSshdConfig WithUsePAM(bool usePAM)
        {
            _config.UsePAM = usePAM;
            return this;
        }
        public RemoteSshdConfig ClearHostKeyFiles()
        {
            _config.HostKeyFiles.Clear();
            return this;
        }
        public RemoteSshdConfig AddHostKeyFile(string hostKeyFile)
        {
            _config.HostKeyFiles.Add(hostKeyFile);
            return this;
        }
        public RemoteSshdConfig WithHostKeyCertificate(string hostKeyCertificate)
        {
            _config.HostCertificate = hostKeyCertificate;
            return this;
        }
        public RemoteSshd Update()
        {
            using (var client = new ScpClient(_connectionInfoFactory.Create()))
            {
                client.Connect();
                using (var memoryStream = new MemoryStream())
                using (var sw = new StreamWriter(memoryStream, Utf8NoBom))
                {
                    sw.NewLine = "\n";
                    _config.SaveTo(sw);
                    sw.Flush();
                    memoryStream.Position = 0;
                    client.Upload(memoryStream, SshdConfigFilePath);
                }
            }
            return _remoteSshd;
        }
    }
}