using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Net;
using System.Text;
using System.Threading.Tasks;
using System.Xml.Linq;
using System.Xml.Serialization;
using Disco.Data.Repository;
using Disco.Models.BI.Interop.Community;
using Disco.Services.Tasks;
using Newtonsoft.Json;

namespace Disco.BI.Interop.Community
{
    public static class UpdateCheck
    {
        private static string UpdateUrl()
        {
            return string.Concat(Disco.Data.Configuration.CommunityHelpers.CommunityUrl(), "DiscoUpdate/V1");
        }

        public static Version CurrentDiscoVersion()
        {
            return typeof(UpdateCheck).Assembly.GetName().Version;
        }
        public static string CurrentDiscoVersionFormatted()
        {
            var v = CurrentDiscoVersion();
            return string.Format("{0}.{1}.{2:0000}.{3:0000}", v.Major, v.Minor, v.Build, v.Revision);
        }

        public static UpdateResponse Check(DiscoDataContext db, bool UseProxy, ScheduledTaskStatus status = null)
        {
            if (status != null)
                status.UpdateStatus(10, "Building Update Request");

            var request = BuildRequest(db);
            //var requestJson = JsonConvert.SerializeObject(request);

            if (status != null)
                status.UpdateStatus(40, "Sending Request");

            var DiscoBIVersion = CurrentDiscoVersionFormatted();

            HttpWebRequest webRequest = (HttpWebRequest)HttpWebRequest.Create(UpdateUrl());

            // Added: 2013-02-08 G#
            // Fix for Proxy Servers which dont support KeepAlive
            webRequest.KeepAlive = false;
            // End Added: 2013-02-08 G#

            if (!UseProxy)
                webRequest.Proxy = new WebProxy();

            webRequest.ContentType = "application/json";
            webRequest.Method = WebRequestMethods.Http.Post;
            webRequest.UserAgent = string.Format("Disco/{0} (Update)", DiscoBIVersion);

            using (var wrStream = webRequest.GetRequestStream())
            {
                XmlSerializer xml = new XmlSerializer(typeof(UpdateRequestV1));
                xml.Serialize(wrStream, request);
            }
            if (status != null)
                status.UpdateStatus(50, "Waiting for Response");
            using (HttpWebResponse webResponse = (HttpWebResponse)webRequest.GetResponse())
            {
                if (webResponse.StatusCode == HttpStatusCode.OK)
                {
                    if (status != null)
                        status.UpdateStatus(90, "Reading Response");
                    UpdateResponse result;
                    using (var wResStream = webResponse.GetResponseStream())
                    {
                        XmlSerializer xml = new XmlSerializer(typeof(UpdateResponse));
                        result = (UpdateResponse)xml.Deserialize(wResStream);
                    }
                    //var result = JsonConvert.DeserializeObject<UpdateResponse>(responseContent);
                    db.DiscoConfiguration.UpdateLastCheck = result;
                    db.SaveChanges();

                    status.SetFinishedMessage(string.Format("The update server reported Version {0} is the latest.", result.Version));

                    return result;
                }
                else
                {
                    if (status != null)
                        status.SetTaskException(new WebException(string.Format("Server responded with: [{0}] {1}", webResponse.StatusCode, webResponse.StatusDescription)));
                    return null;
                }
            }
        }

        private static UpdateRequestV1 BuildRequest(DiscoDataContext db)
        {
            var m = new UpdateRequestV1();

            m.DeploymentId = db.DiscoConfiguration.DeploymentId;

            m.CurrentDiscoVersion = CurrentDiscoVersionFormatted();

            m.OrganisationName = db.DiscoConfiguration.OrganisationName;
            m.BroadbandDoeWanId = GetBroadbandDoeWanId();
            m.BetaDeployment = db.DiscoConfiguration.UpdateBetaDeployment;

            m.Stat_JobCounts = db.Jobs.GroupBy(j => j.JobTypeId).Select(g => new Disco.Models.BI.Interop.Community.UpdateRequestV1.Stat { Key = g.Key, Count = g.Count() }).ToList();
            m.Stat_OpenJobCounts = db.Jobs.Where(j => j.ClosedDate == null).GroupBy(j => j.JobTypeId).Select(g => new Disco.Models.BI.Interop.Community.UpdateRequestV1.Stat { Key = g.Key, Count = g.Count() }).ToList();
            var activeThreshold = DateTime.Now.AddDays(-60);
            m.Stat_ActiveDeviceModelCounts = db.DeviceModels.Select(dm => new Disco.Models.BI.Interop.Community.UpdateRequestV1.Stat { Key = dm.Manufacturer + ";" + dm.Model, Count = dm.Devices.Count(d => d.DecommissionedDate == null && (d.LastNetworkLogonDate == null || d.LastNetworkLogonDate > activeThreshold)) }).ToList();
            m.Stat_UserCounts = db.Users.GroupBy(u => u.Type).Select(g => new Disco.Models.BI.Interop.Community.UpdateRequestV1.Stat { Key = g.Key, Count = g.Count() }).ToList();

            m.InstalledPlugins = Disco.Services.Plugins.Plugins.GetPlugins().Select(manifest => new Disco.Models.BI.Interop.Community.UpdateRequestV1.PluginRef { Id = manifest.Id, Version = manifest.VersionFormatted }).ToList();

            return m;
        }

        #region DoE Query
        public static string GetBroadbandDoeWanId()
        {
            // DnsQuery for broadband.doe.wan
            IPHostEntry doeWanDnsEntry;
            try
            {
                doeWanDnsEntry = Dns.GetHostEntry("broadband.doe.wan");
            }
            catch (Exception)
            { return null; } // Fail on error

            // Try using IPSearch feature
            XDocument doeWanIPSearchResult = TryDownloadDoeIPSearch(false);
            if (doeWanIPSearchResult == null)
                doeWanIPSearchResult = TryDownloadDoeIPSearch(true);

            if (doeWanIPSearchResult == null)
                return null;

            try
            {
                return doeWanIPSearchResult.Element("resultset").Element("site").Element("number").Value.ToLower();
            }
            catch (Exception)
            { return null; } // Fail on error
        }
        private static XDocument TryDownloadDoeIPSearch(bool useProxy)
        {
            try
            {
                var DiscoBIVersion = CurrentDiscoVersionFormatted();

                HttpWebRequest wReq = (HttpWebRequest)HttpWebRequest.Create("http://broadband.doe.wan/ipsearch/showresult.php");
                // Added: 2013-02-08 G#
                // Fix for Proxy Servers which dont support KeepAlive
                wReq.KeepAlive = false;
                // End Added: 2013-02-08 G#
                if (!useProxy)
                    wReq.Proxy = new WebProxy(); // Empty Proxy Config
                wReq.Method = WebRequestMethods.Http.Post;
                wReq.ContentType = "application/x-www-form-urlencoded";
                wReq.UserAgent = string.Format("Disco/{0}", DiscoBIVersion);
                using (var wrStream = wReq.GetRequestStream())
                {
                    using (var wrStreamWriter = new StreamWriter(wrStream))
                    {
                        wrStreamWriter.Write("mode=whoami");
                    }
                }
                using (HttpWebResponse wRes = (HttpWebResponse)wReq.GetResponse())
                {
                    if (wRes.StatusCode == HttpStatusCode.OK)
                    {
                        using (var wResStream = wRes.GetResponseStream())
                        {
                            return XDocument.Load(wResStream);
                        }
                    }
                    else
                        return null;
                }
            }
            catch (Exception)
            { return null; } // Fail on error
        }
        #endregion

    }
}
