using System.Net;
using Microsoft.Extensions.Logging;
using Platform.Logging.Extensions;
using Platform.Geolocation.IpResolver.Stun.Attributes;

namespace Platform.Geolocation.IpResolver.Stun;

public class StunClient : IStunClient
{
    private readonly ILogger _logger;

    private const int TimeoutMilliseconds = 1500;

    /// <summary>
    /// https://github.com/pradt2/always-online-stun/blob/master/valid_ipv4s_tcp.txt
    /// </summary>
    private readonly string[] _hosts =
    {
        "213.239.212.105:3478",
        "129.153.212.128:3478",
        "79.140.42.88:3478",
        "172.105.99.33:3478",
        "134.2.17.14:3478",
        "141.54.160.48:3478",
        "31.184.236.23:3478",
        "213.239.206.5:3478",
        "104.238.184.174:3478",
        "34.74.124.204:3478",
        "188.40.203.74:3478",
        "185.125.180.70:3478",
        "94.23.17.185:3478",
        "91.213.98.54:3478",
        "157.161.10.32:3478",
        "92.205.106.161:3478",
        "143.198.60.79:3478",
        "95.110.198.3:3478",
        "188.68.43.182:3478",
        "5.161.52.174:3478",
        "3.70.155.58:3478",
        "212.103.68.7:3478",
        "91.151.52.200:3478",
        "37.97.65.52:3478",
        "62.72.83.10:3478",
        "136.243.59.79:3478",
        "51.68.112.203:3478",
        "52.26.251.34:3478",
        "95.216.145.84:3478",
        "188.118.52.172:3478",
        "147.182.188.245:3478",
        "188.138.90.169:3478",
        "212.69.48.253:3478",
        "90.145.158.66:3478",
        "81.82.206.117:3478",
        "159.69.191.124:3478",
        "192.172.233.145:3478",
        "5.161.57.75:3478",
        "147.182.188.245:3478",
        "20.93.239.171:3478",
        "198.100.144.121:3478",
        "52.26.251.34:3478",
        "212.53.40.40:3478",
        "44.230.252.214:3478",
        "162.243.29.166:3478",
        "212.53.40.40:3478",
        "83.64.250.246:3478",
        "24.204.48.11:3478",
        "49.12.125.53:3478",
        "195.145.93.141:3478",
        "212.144.246.197:3478",
        "88.198.203.20:3478",
        "95.216.78.222:3478",
        "80.156.214.187:3478",
        "81.83.12.46:3478",
        "157.161.10.32:3478",
        "5.161.57.75:3478",
        "34.73.197.25:3478",
        "54.177.85.190:3478",
        "217.19.216.178:3478",
        "88.198.151.128:3478",
        "34.197.205.39:3478",
        "83.64.250.246:3478",
        "134.119.17.210:3478",
        "23.21.92.55:3478",
        "143.198.60.79:3478",
        "185.88.236.76:3478",
        "52.24.174.49:3478",
        "176.9.24.184:3478",
        "23.21.92.55:3478",
        "91.212.41.85:3478",
        "52.52.70.85:3478",
        "87.129.12.229:3478",
        "80.155.54.123:3478",
        "5.161.52.174:3478",
        "212.53.40.40:3478",
        "69.20.59.115:3478",
        "45.15.102.34:3478",
        "5.161.57.75:3478",
        "193.22.17.97:3478",
        "101.200.40.149:3478",
        "136.243.59.79:3478",
        "34.73.197.25:3478",
        "137.74.112.113:3478",
        "83.64.250.246:3478",
        "178.63.107.149:3478",
        "51.83.15.212:3478",
        "51.68.45.75:3478",
        "83.64.250.246:3478",
        "85.197.87.182:3478",
        "81.3.27.44:3478",
        "195.201.132.113:3478",
        "217.91.243.229:3478",
        "192.76.120.66:3478",
        "195.208.107.138:3478",
        "54.197.117.0:3478",
        "88.99.67.241:3478",
        "95.216.190.5:3478",
        "194.149.74.157:3478",
        "51.83.201.84:3478",
        "94.130.130.49:3478",
        "52.24.174.49:3478",
        "202.1.117.2:3478",
        "84.198.248.217:3478",
        "212.53.40.40:3478",
        "87.253.140.133:3478",
        "35.180.81.93:3478",
        "5.161.52.174:3478",
        "83.64.250.246:3478",
        "54.176.195.118:3478",
        "5.161.57.75:3478",
        "94.130.130.49:3478",
        "162.13.119.185:3478",
        "88.218.220.40:3478",
        "66.228.54.23:3478",
        "52.47.70.236:3478",
        "185.125.180.70:3478",
        "130.236.12.148:3478",
        "23.21.199.62:3478",
        "5.161.52.174:3478",
        "159.69.191.124:443",
        "212.53.40.40:3478",
        "34.206.168.53:3478"
    };

    public StunClient(ILogger<StunClient> logger) => _logger = logger;

    public async Task<IPAddress> Send()
    {
        var message = MakeStunMessage();

        foreach (var host in _hosts.ShuffleElements())
        {
            try
            {
                var response = await StunClientTcp.SendRequest(message, host.ToStunUrl()).AwaitWithTimeout(TimeoutMilliseconds);

                var ipEndpoint = response.Attributes
                    .First(a => a.Type == StunAttributeType.XOR_MAPPED_ADDRESS)
                    .GetXorMappedAddress();

                _logger.Info($"The Stun tcp response from the host '{host}' is {ipEndpoint.Address}");

                if (Equals(ipEndpoint.Address, IPAddress.None) || IPAddress.IsLoopback(ipEndpoint.Address))
                {
                    continue;
                }

                return ipEndpoint.Address;
            }
            catch (Exception e)
            {
                _logger.Error($"An error was thrown while the Stun endpoint call '{host}'. '{e.Message}'", e);
            }
        }

        return IPAddress.None;
    }

    private static StunMessage MakeStunMessage()
    {
        var message = new StunMessage();
        var softwareAttribute = new StunAttribute(StunAttributeType.SOFTWARE, message);
        softwareAttribute.SetSoftware("foxy");
        message.Attributes.Add(softwareAttribute);

        return message;
    }
}