using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Linq;
using System.Net;
using System.Text;
using System.Threading;
using System.Threading.Tasks;

namespace RiskAssessment
{
    public class RiskCalculator
    {
        private const string ServiceUrl = "http://localhost:51099";
        #region Synchronous
        public double GetScore(string ssn, params string[] agencies) {
            var reports = new List<CreditReport>();

            //get the credit report for each agency
            foreach(var agency in agencies) {
                var report = RetrieveCreditReport(ssn, agency);
                reports.Add(report);
            }

            var score = CalculateConsolidatedScore(reports);
            return score;
        }

        private CreditReport RetrieveCreditReport(string ssn, string agency) {
            var client = new WebClient();
            var url = String.Format(
                ServiceUrl + "/CreditReport?ssn={0}&agency={1}",
                ssn, agency);

            string reportJson = client.DownloadString(new Uri(url));
            return reportJson.DeserializeJson<CreditReport>();
        }

        private double CalculateConsolidatedScore(IEnumerable<CreditReport> reports) {
            //pretend this is exceedingly more complex than my humble calculation here:
            Thread.Sleep(600);
            return reports.Average(r => r.Rating);
        }

        #endregion

        #region Async today

        public void GetScoreAsyncToday(string ssn, string[] agencies,
            Action<double> callback)
        {
            var reports = new List<CreditReport>();
            GetScoreAsyncIterator(agencies.ToList().GetEnumerator(), ssn, reports, callback);
        }

        private void GetScoreAsyncIterator(
            IEnumerator<string> enumerator, string ssn, 
            IList<CreditReport> reports,
            Action<double> callback)
        {
            if (enumerator.MoveNext()) {
                var agency = enumerator.Current;
                var client = new WebClient();
                var url = String.Format(
                    ServiceUrl + "/CreditReport?ssn={0}&agency={1}",
                    ssn, agency);
                client.DownloadStringCompleted += (sender, e) => {
                    string reportJson = e.Result;
                    var report = reportJson.DeserializeJson<CreditReport>();
                    reports.Add(report);
                    GetScoreAsyncIterator(enumerator, ssn, reports, callback);
                };
                client.DownloadStringAsync(new Uri(url));
            } else {
                //done
                enumerator.Dispose();
                var score = CalculateConsolidatedScore(reports);
                callback(score);
            }
        }


        #endregion

        #region Async CTP

        public async Task<double> GetScoreAsync(string ssn, params string[] agencies) {
            var reports = new List<CreditReport>();

            //get the credit report for each agency
            foreach(var agency in agencies) {
                var report = await RetrieveCreditReportAsync(ssn, agency);
                reports.Add(report);
            }

            var score = await CalculateConsolidatedScoreAsync(reports);
            return score;
        }

        private async Task<CreditReport> RetrieveCreditReportAsync(string ssn, string agency) {
            var url = String.Format(
                ServiceUrl + "/CreditReport?ssn={0}&agency={1}",
                ssn, agency);

            string reportJson = await new WebClient().DownloadStringTaskAsync(new Uri(url));
            return reportJson.DeserializeJson<CreditReport>();
        }

        private async Task<double> CalculateConsolidatedScoreAsync(IEnumerable<CreditReport> reports) {
            //pretend this is exceedingly more complex than my humble calculation here:
            await TaskEx.Delay(TimeSpan.FromMilliseconds(600));
            return reports.Average(r => r.Rating);
        }

        #endregion

        #region Async CTP Parallel

        public async Task<double> GetScoreParallelAsync(string ssn, params string[] agencies) {

            //get the credit report for each agency
            var retrievalTasks = new List<Task<CreditReport>>();

            foreach(var agency in agencies) {
                var task = RetrieveCreditReportAsync(ssn, agency);
                retrievalTasks.Add(task);
            }

            var reports = await TaskEx.WhenAll(retrievalTasks);

            var score = await CalculateConsolidatedScoreAsync(reports);
            return score;
        }

        #endregion


    }
}
