//  
//   Rebex Sample Code License
// 
//   Copyright 2026, REBEX CR s.r.o.
//   All rights reserved.
//   https://www.rebex.net/
// 
//   Permission to use, copy, modify, and/or distribute this software for any
//   purpose with or without fee is hereby granted.
// 
//   THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
//   EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
//   OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
//   NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
//   HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
//   WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
//   FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
//   OTHER DEALINGS IN THE SOFTWARE.
// 

using Rebex.Net;
using Rebex.Security.Certificates;
using System;
using System.Net;
using System.Net.Sockets;

namespace Rebex.Samples
{
    /// <summary>
    /// Parses input arguments.
    /// </summary>
    public class TlsProxyConfiguration
    {
        /// <summary>
        /// Local address the proxy listens for incoming connections.
        /// </summary>
        public IPEndPoint ListeningAddress { get; private set; }

        /// <summary>
        /// Address of outgoing connections.
        /// </summary>
        public EndPoint TargetAddress { get; private set; }

        /// <summary>
        /// TLS Proxy (server) certificate to be used for authenticating the incoming TLS connection.
        /// </summary>
        public CertificateChain Certificate { get; private set; }

        /// <summary>
        /// Logger object.
        /// </summary>
        public ILogWriter LogWriter { get; private set; }

        /// <summary>
        /// How long the proxy keeps tunnels opened.
        /// </summary>
        public int Timeout { get; private set; }

        /// <summary>
        /// Desired TLS versions of the incoming connection.
        /// </summary>
        public TlsVersion TlsVersions { get; private set; }

        /// <summary>
        /// Initializes new proxy configuration object.
        /// </summary>
        public TlsProxyConfiguration()
        {
            // set defaults
            Timeout = 60 * 1000; // 60 seconds
            LogWriter = new ConsoleLogWriter(LogLevel.Info); // INFO logger using system's Console
            TlsVersions = TlsVersion.TLS11 | TlsVersion.TLS12 | TlsVersion.TLS13;
        }

        /// <summary>
        /// Parses given arguments.
        /// </summary>
        public void Parse(string[] args)
        {
            if (args.Length < 3)
                throw new ParseException("Invalid number of arguments.");

            ListeningAddress = (IPEndPoint)ParseEndPoint(args[0], requireIP: true);

            TargetAddress = ParseEndPoint(args[1], requireIP: false);

            string certPath = args[2];
            string certPassword = "";

            if (args.Length > 3 && args[3] != null)
                certPassword = args[3];

            Certificate = CertificateChain.LoadPfx(certPath, certPassword);
        }

        /// <summary>
        /// Parses input value as EndPoint.
        /// </summary>
        private static EndPoint ParseEndPoint(string value, bool requireIP)
        {
            var parts = value.Split(':');
            if (parts.Length != 2)
                throw new ParseException("Invalid address ({0}). An address with port is required. See examples.", value);

            IPAddress ip;
            if (IPAddress.TryParse(parts[0], out ip))
                return new IPEndPoint(ip, GetPort(parts[1]));

            if (requireIP)
                throw new ParseException("IP address required ({0}).", value);
            else
                return new DnsEndPoint(parts[0], GetPort(parts[1]), AddressFamily.InterNetwork);
        }

        /// <summary>
        /// Converts string to a valid port number.
        /// </summary>
        private static int GetPort(string value)
        {
            int port;
            if (!int.TryParse(value, out port))
                throw new ParseException("Invalid port ({0}).", value);

            if (port > 0 && port <= ushort.MaxValue)
                return port;

            throw new ParseException("Invalid port ({0}).", value);
        }

        /// <summary>
        /// Represents a parser error.
        /// </summary>
        public class ParseException : Exception
        {
            public ParseException(string message) : base(message)
            {
            }
            public ParseException(string format, params object[] args) : base(string.Format(format, args))
            {
            }
        }
    }
}
