using DNS.Client.RequestResolver;
using DNS.Protocol;
using DNS.Protocol.ResourceRecords;
using Microsoft.Extensions.Logging;
using Microsoft.Extensions.Options;
using System;
using System.Linq;
using System.Net;
using System.Threading;
using System.Threading.Tasks;
namespace FastGithub.Dns
{
    /// 
    /// dns解析者
    ///  
    sealed class RequestResolver : IRequestResolver
    {
        private readonly TimeSpan ttl = TimeSpan.FromMinutes(1d);
        private readonly IRequestResolver untrustedResolver;
        private readonly IOptionsMonitor options;
        private readonly ILogger logger;
        /// 
        /// dns解析者
        ///  
        /// 
        /// 
        public RequestResolver(
            IOptionsMonitor options,
            ILogger logger)
        {
            this.options = options;
            this.logger = logger;
            this.untrustedResolver = new UdpRequestResolver(options.CurrentValue.UntrustedDns.ToIPEndPoint());
        }
        /// 
        /// 解析域名
        /// 
        /// 
        /// 
        /// 
        public async Task Resolve(IRequest request, CancellationToken cancellationToken = default)
        {
            var response = Response.FromRequest(request);
            if (request is not RemoteEndPointRequest remoteEndPointRequest)
            {
                return response;
            }
            var question = request.Questions.FirstOrDefault();
            if (question == null || question.Type != RecordType.A)
            {
                return response;
            }
            var domain = question.Name;
            if (this.options.CurrentValue.IsMatch(domain.ToString()) == true)
            {
                var localAddress = remoteEndPointRequest.GetLocalAddress() ?? IPAddress.Loopback;
                var record = new IPAddressResourceRecord(domain, localAddress, this.ttl);
                response.AnswerRecords.Add(record);
                this.logger.LogInformation($"[{domain}->{localAddress}]");
                return response;
            }
            return await this.untrustedResolver.Resolve(request, cancellationToken);
        }
    }
}