﻿using OpenMail.Domain.interfaces;
using System;
using System.Linq;
using OpenMail.Domain.models;
using OpenMail.Infra.NetworkProtocols.dns;
using OpenMail.Infra.NetworkProtocols.smtp;
using System.Collections.Generic;
using System.Threading.Tasks;
using System.Threading;

namespace OpenMail.Domain.services
{
    public class MailService : IMailService
    {   
        public MailModel TestEmail(MailModel mail)
        {
            var watch = System.Diagnostics.Stopwatch.StartNew();
            
            var _mxRecords = DnsLookup.GetMxRecords(mail.Domain);
            SmtpStatusResponse _response;
            using (SmtpServer _smtp = new SmtpServer() { Server = _mxRecords.FirstOrDefault() })
            {
                _response = _smtp.VerifyMail(mail.FullAddress);
            }

            switch (_response)
            {
                case SmtpStatusResponse.Valid:
                    mail.Status = MailStatusModel.Valid;
                    break;
                case SmtpStatusResponse.Invalid:
                    mail.Status = MailStatusModel.Invalid;
                    break;
                default:
                    mail.Status = MailStatusModel.Undef;
                    break;
            }

            watch.Stop();
            mail.TimeInSeconds = TimeSpan.FromMilliseconds(watch.ElapsedMilliseconds).TotalSeconds;

            return mail;
        }

        public List<MailModel> TestEmails(List<MailModel> models)
        {
            var _resp = new List<MailModel>();

            if (models != null)
            {
                Parallel.ForEach(models, new ParallelOptions { MaxDegreeOfParallelism = 5 }, 
                (_model) =>
                {
                    _resp.Add(this.TestEmail(_model));
                });
            }

            return _resp;
        }

        public List<MailModel> TestEmailsWithThreads(List<MailModel> models)
        {
            List<MailModel> _listReturn = new List<MailModel>();
            int _total = models.Count;
            int _limit = 5;
            int _items = (models.Count % _limit);

            Parallel.For(0, _items, i =>
            {
                List<MailModel> _list = null;
                if (i == 0)
                {
                    _list = models.GetRange(0, (_limit - 1) < _total ? (_limit - 1) : _total);
                }
                else
                {
                    if ((_limit - 1) < _total)
                    {
                        _list = models.GetRange((_limit - 1), (_limit - 1) + i < _total ? (_limit - 1) + i : _total);
                    }
                }

                if (_list != null && _list.Count > 0)
                {
                    var thread = new Thread(() => {
                        _listReturn = _listReturn.Concat(this.TestEmails(_list)).ToList();
                    });
                    thread.IsBackground = true;
                    thread.Start();
                    thread.Join();
                }
            });
            return _listReturn;
        }

        public void Dispose()
        {
            GC.SuppressFinalize(this);
        }
        
    }
}
