using System;
using System.Linq;
using System.Net;
using System.Net.Http;
using System.Net.Http.Headers;
using System.Threading.Tasks;
using Should;
using WebApiBook.IssueTrackerApi.Infrastructure;
using WebApiBook.IssueTrackerApi.Models;
using WebApiContrib.CollectionJson;
using WebApiContrib.Formatting.CollectionJson.Client;
using Xbehave;

namespace WebApiBook.IssueTrackerApp.AcceptanceTests.Features
{
    public class RetrievingIssues : IssuesFeature
    {
        private readonly Uri _uriIssues = new Uri("http://localhost/issue");
        private readonly Uri _uriIssue1 = new Uri("http://localhost/issue/1");
        private readonly Uri _uriIssue2 = new Uri("http://localhost/issue/2");

        [Scenario]
        public void RetrievingAnIssue(IssueState issue, Issue fakeIssue)
        {
            "Mając istniejące zgłoszenie błędu".
                f(() =>
                {
                    fakeIssue = FakeIssues.FirstOrDefault();
                    MockIssueStore.Setup(i => i.FindAsync("1")).Returns(Task.FromResult(fakeIssue));
                });
            "Kiedy zostanie pobrane".
                f(() =>
                {
                    Request.RequestUri = _uriIssue1;
                    Response = Client.SendAsync(Request).Result;
                    issue = Response.Content.ReadAsAsync<IssueState>().Result;
                });
            "Wtedy będzie zwrócony kod stanu '200 OK'".
                f(() => Response.StatusCode.ShouldEqual(HttpStatusCode.OK));
            "Wtedy będzie zwrócone".
                f(() => issue.ShouldNotBeNull());
            "Wtedy powinno mieć identyfikator".
                f(() => issue.Id.ShouldEqual(fakeIssue.Id));
            "Wtedy powinno mieć tytuł".
                f(() => issue.Title.ShouldEqual(fakeIssue.Title));
            "Wtedy powinno mieć opis".
                f(() => issue.Description.ShouldEqual(fakeIssue.Description));
            "Wtedy powinno mieć informacje o stanie".
                f(() => issue.Status.ShouldEqual(Enum.GetName(typeof(IssueStatus), fakeIssue.Status)));
            "Wtedy powinno mieć łącze 'self'".
                f(() =>
                {
                    var link = issue.Links.FirstOrDefault(l => l.Rel == IssueLinkFactory.Rels.Self);
                    link.ShouldNotBeNull();
                    link.Href.AbsoluteUri.ShouldEqual("http://localhost/issue/1");
                });
            "Wtedy powinno mieć łącze 'transition'".
                f(() =>
                {
                    var link = issue.Links.FirstOrDefault(l => l.Rel == IssueLinkFactory.Rels.Transition && l.Action == IssueLinkFactory.Actions.Transition);
                    link.ShouldNotBeNull();
                    link.Href.AbsoluteUri.ShouldEqual("http://localhost/issueprocessor/1?action=transition");
                });
        }

        [Scenario]
        public void RetrievingAnOpenIssue(Issue fakeIssue, IssueState issue)
        {
            "Mając istniejące otwarte zgłoszenie błędu".
                f(() =>
                {
                    fakeIssue = FakeIssues.Single(i => i.Status == IssueStatus.Open);
                    MockIssueStore.Setup(i => i.FindAsync("1")).Returns(Task.FromResult(fakeIssue));
                });
            "Kiedy zostanie pobrane".
                f(() =>
                {
                    Request.RequestUri = _uriIssue1;
                    issue = Client.SendAsync(Request).Result.Content.ReadAsAsync<IssueState>().Result;
                });
            "Wtedy powinno mieć łącze akcji 'close'".
                f(() =>
                {
                    var link = issue.Links.FirstOrDefault(l => l.Rel == IssueLinkFactory.Rels.Close && l.Action == IssueLinkFactory.Actions.Close);
                    link.ShouldNotBeNull();
                    link.Href.AbsoluteUri.ShouldEqual("http://localhost/issueprocessor/1?action=close");
                });
        }

        [Scenario]
        public void RetrievingAClosedIssue(Issue fakeIssue, IssueState issue)
        {
            "Mając istniejące zamknięte zgłoszenie błędu".
                f(() =>
                {
                    fakeIssue = FakeIssues.Single(i => i.Status == IssueStatus.Closed);
                    MockIssueStore.Setup(i => i.FindAsync("2")).Returns(Task.FromResult(fakeIssue));
                });
            "Kiedy zostanie pobrane".
                f(() =>
                {
                    Request.RequestUri = _uriIssue2;
                    issue = Client.SendAsync(Request).Result.Content.ReadAsAsync<IssueState>().Result;
                });
            "Wtedy powinno mieć łącze akcji 'open'".
                f(() =>
                {
                    var link = issue.Links.FirstOrDefault(l => l.Rel == IssueLinkFactory.Rels.Open && l.Action == IssueLinkFactory.Actions.Open);
                    link.ShouldNotBeNull();
                    link.Href.AbsoluteUri.ShouldEqual("http://localhost/issueprocessor/2?action=open");
                });
        }

        [Scenario]
        public void RetrievingAnIssueThatDoesNotExist()
        {
            "Mając nieistniejące zgłoszenie błędu".
                f(() => MockIssueStore.Setup(i => i.FindAsync("1")).Returns(Task.FromResult((Issue)null)));
            "Kiedy zostanie pobrane".
                f(() =>
                {
                    Request.RequestUri = _uriIssue1;
                    Response = Client.SendAsync(Request).Result;
                });
            "Wtedy będzie zwrócony kod stanu '404 Not Found'".
                f(() => Response.StatusCode.ShouldEqual(HttpStatusCode.NotFound));
        }

        [Scenario]
        public void RetrievingAllIssues(IssuesState issuesState)
        {
            "Mając istniejące zgłoszenia błędów".
                f(() => MockIssueStore.Setup(i => i.FindAsync()).Returns(Task.FromResult(FakeIssues)));
            "Wtedy wszystkie zgłoszenia błędów zostaną pobrane".
                f(() =>
                {
                    Request.RequestUri = _uriIssues;
                    Response = Client.SendAsync(Request).Result;
                    issuesState = Response.Content.ReadAsAsync<IssuesState>().Result;
                });
            "Wtedy będzie zwrócony kod stanu '200 OK'".
                f(() => Response.StatusCode.ShouldEqual(HttpStatusCode.OK));
            "Wtedy zostaną zwrócone".
                f(() =>
                {
                    issuesState.Issues.FirstOrDefault(i => i.Id == "1").ShouldNotBeNull();
                    issuesState.Issues.FirstOrDefault(i => i.Id == "2").ShouldNotBeNull();
                });
            "Wtedy kolekcja powinna mieć łącze 'self'".
                f(() =>
                {
                    var link = issuesState.Links.FirstOrDefault(l => l.Rel == IssueLinkFactory.Rels.Self);
                    link.ShouldNotBeNull();
                    link.Href.AbsoluteUri.ShouldEqual("http://localhost/issue");
                });
        }

        [Scenario]
        public void RetrievingAllIssuesAsCollectionJson(IReadDocument readDocument)
        {
            "Mając istniejące zgłoszenia błędów".
                f(() => MockIssueStore.Setup(i => i.FindAsync()).Returns(Task.FromResult(FakeIssues)));
            "Mając istniejące zgłoszenia błędów".
                f(() =>
                {
                    Request.RequestUri = _uriIssues;
                    Request.Headers.Accept.Clear();
                    Request.Headers.Accept.Add(new MediaTypeWithQualityHeaderValue("application/vnd.collection+json"));
                    Response = Client.SendAsync(Request).Result;
                    readDocument = Response.Content.ReadAsAsync<ReadDocument>(new[] { new CollectionJsonFormatter() }).Result;
                });
            "Wtedy będzie zwrócony kod stanu '200 OK'".
               f(() => Response.StatusCode.ShouldEqual(HttpStatusCode.OK));
            "Wtedy będzie zwrócony Collection+Json".
                f(() => readDocument.ShouldNotBeNull());
            "Wtedy powinien być ustawiony atrybut href".
                f(() => readDocument.Collection.Href.AbsoluteUri.ShouldEqual("http://localhost/issue"));
            "Wtedy wszystkie zgłoszenia błędów będą zwrócone".
                f(() =>
                {
                    readDocument.Collection.Items.FirstOrDefault(i => i.Href.AbsoluteUri == "http://localhost/issue/1").ShouldNotBeNull();
                    readDocument.Collection.Items.FirstOrDefault(i => i.Href.AbsoluteUri == "http://localhost/issue/2").ShouldNotBeNull();
                });
            "Wtedy ciąg tekstowy wyszukiwania będzie zwrócony".
                f(() => readDocument.Collection.Queries.SingleOrDefault(
                            q => q.Rel == IssueLinkFactory.Rels.SearchQuery).ShouldNotBeNull());
        }

        [Scenario]
        public void SearchingIssues(IssuesState issuesState)
        {
            "Mając istniejące zgłoszenia błędów".
                f(() => MockIssueStore.Setup(i => i.FindAsyncQuery("another")).Returns(Task.FromResult(FakeIssues.Where(i => i.Id == "2"))));
            "Kiedy zgłoszenia błędów są wyszukiwane".
                f(() =>
                {
                    Request.RequestUri = new Uri(_uriIssues, "?searchtext=another");
                    Response = Client.SendAsync(Request).Result;
                    issuesState = Response.Content.ReadAsAsync<IssuesState>().Result;
                });
            "Wtedy będzie zwrócony kod stanu '200 OK'".
                f(() => Response.StatusCode.ShouldEqual(HttpStatusCode.OK));
            "Wtedy kolekcja powinna mieć łącze 'self'".
                f(() =>
                {
                    var link = issuesState.Links.FirstOrDefault(l => l.Rel == IssueLinkFactory.Rels.Self);
                    link.ShouldNotBeNull();
                    link.Href.AbsoluteUri.ShouldEqual("http://localhost/issue?searchtext=another");
                });
            "Wtedy dopasowane zgłoszenia błędów zostaną zwrócone".
                f(() =>
                {
                    var issue = issuesState.Issues.FirstOrDefault();
                    issue.ShouldNotBeNull();
                    issue.Id.ShouldEqual("2");
                });
        }
    }
}
