package uk.gov.pay.api.it.directdebit;

import com.fasterxml.jackson.core.JsonProcessingException;
import com.github.tomakehurst.wiremock.matching.StringValuePattern;
import org.apache.http.HttpStatus;
import org.junit.Before;
import org.junit.Test;
import uk.gov.pay.api.it.PaymentResourceITestBase;
import uk.gov.pay.api.model.PaymentConnectorResponseLink;
import uk.gov.pay.api.model.directdebit.mandates.MandateConnectorResponse;
import uk.gov.pay.api.model.directdebit.mandates.MandateState;
import uk.gov.pay.api.model.directdebit.mandates.Payer;
import uk.gov.pay.api.model.links.SearchNavigationLinks;
import uk.gov.pay.api.model.search.directdebit.MandateSearchConnectorResponse;
import uk.gov.pay.api.utils.PublicAuthMockClient;
import uk.gov.pay.api.utils.mocks.ConnectorDDMockClient;

import java.time.ZonedDateTime;
import java.util.Collections;
import java.util.Map;

import static com.github.tomakehurst.wiremock.client.WireMock.equalTo;
import static io.restassured.RestAssured.given;
import static io.restassured.http.ContentType.JSON;
import static javax.ws.rs.core.HttpHeaders.AUTHORIZATION;
import static org.hamcrest.core.Is.is;
import static uk.gov.pay.api.model.TokenPaymentType.DIRECT_DEBIT;
import static uk.gov.pay.api.model.directdebit.mandates.MandateConnectorResponse.MandateConnectorResponseBuilder.aMandateConnectorResponse;
import static uk.gov.pay.api.model.search.directdebit.MandateSearchConnectorResponse.SearchMandateConnectorResponseBuilder.aSearchMandateConnectorResponse;
import static uk.gov.pay.api.utils.Urls.mandateLocationFor;
import static uk.gov.pay.commons.model.ApiResponseDateTimeFormatter.ISO_INSTANT_MILLISECOND_PRECISION;

public class MandateResourceSearchMandateIT extends PaymentResourceITestBase {

    private ConnectorDDMockClient connectorDDMockClient = new ConnectorDDMockClient(connectorDDMock);
    private PublicAuthMockClient publicAuthMockClient = new PublicAuthMockClient(publicAuthMock);

    private static final String MANDATE_ID = "mandateId";
    private static final String MANDATE_REFERENCE = "test_mandate_reference";
    private static final String SERVICE_REFERENCE = "test_service_reference";
    private static final String RETURN_URL = "https://service-name.gov.uk/transactions/12345";
    private static final String PROVIDER_ID = "MD1234";
    private static final String CREATED_DATE = ISO_INSTANT_MILLISECOND_PRECISION.format(ZonedDateTime.parse("2016-01-01T12:00:00Z"));
    private static final String PAYER_NAME = "payer";
    private static final String PAYER_EMAIL = "[email protected]";
    public static final String DD_CONNECTOR_BASE_SEARCH_URL = "https://connector/v1/api/accounts/%s/mandates?page=1&display_size=500";

    @Before
    public void setup() {
        publicAuthMockClient.mapBearerTokenToAccountId(API_KEY, GATEWAY_ACCOUNT_ID, DIRECT_DEBIT);
    }

    @Test
    public void shouldReturnErrorForInvalidPageNumber() {
        given().port(app.getLocalPort())
                .accept(JSON)
                .contentType(JSON)
                .header(AUTHORIZATION, "Bearer " + API_KEY)
                .queryParam("page", -1)
                .get("/v1/directdebit/mandates/")
                .then()
                .statusCode(HttpStatus.SC_UNPROCESSABLE_ENTITY)
                .body("field", is("page"))
                .body("code", is("P0102"))
                .body("description", is("Invalid attribute value: page. Must be greater than or equal to 1"));
    }

    @Test
    public void shouldReturnErrorForInvalidFromDate() {
        given().port(app.getLocalPort())
                .accept(JSON)
                .contentType(JSON)
                .header(AUTHORIZATION, "Bearer " + API_KEY)
                .queryParam("from_date", "this isn't a date")
                .get("/v1/directdebit/mandates/")
                .then()
                .statusCode(HttpStatus.SC_UNPROCESSABLE_ENTITY)
                .body("field", is("from_date"))
                .body("code", is("P0102"))
                .body("description", is("Invalid attribute value: from_date. Must be a valid date"));
    }

    @Test
    public void shouldReturnErrorForInvalidToDate() {
        given().port(app.getLocalPort())
                .accept(JSON)
                .contentType(JSON)
                .header(AUTHORIZATION, "Bearer " + API_KEY)
                .queryParam("to_date", "this isn't a date")
                .get("/v1/directdebit/mandates/")
                .then()
                .statusCode(HttpStatus.SC_UNPROCESSABLE_ENTITY)
                .body("field", is("to_date"))
                .body("code", is("P0102"))
                .body("description", is("Invalid attribute value: to_date. Must be a valid date"));
    }

    @Test
    public void shouldReturnErrorForDisplaySizeTooGreat() {
        given().port(app.getLocalPort())
                .accept(JSON)
                .contentType(JSON)
                .header(AUTHORIZATION, "Bearer " + API_KEY)
                .queryParam("display_size", 501)
                .get("/v1/directdebit/mandates/")
                .then()
                .statusCode(HttpStatus.SC_UNPROCESSABLE_ENTITY)
                .body("field", is("display_size"))
                .body("code", is("P0102"))
                .body("description", is("Invalid attribute value: display_size. Must be less than or equal to 500"));
    }

    @Test
    public void shouldReturnErrorForDisplaySizeTooSmall() {
        given().port(app.getLocalPort())
                .accept(JSON)
                .contentType(JSON)
                .header(AUTHORIZATION, "Bearer " + API_KEY)
                .queryParam("display_size", 0)
                .get("/v1/directdebit/mandates/")
                .then()
                .statusCode(HttpStatus.SC_UNPROCESSABLE_ENTITY)
                .body("field", is("display_size"))
                .body("code", is("P0102"))
                .body("description", is("Invalid attribute value: display_size. Must be greater than or equal to 1"));
    }

    @Test
    public void shouldReturnErroForInvalidMandateState() {
        given().port(app.getLocalPort())
                .accept(JSON)
                .contentType(JSON)
                .header(AUTHORIZATION, "Bearer " + API_KEY)
                .queryParam("state", "doesn't exist")
                .get("/v1/directdebit/mandates/")
                .then()
                .statusCode(HttpStatus.SC_UNPROCESSABLE_ENTITY)
                .body("field", is("state"))
                .body("code", is("P0102"))
                .body("description", is("Invalid attribute value: state. Must be one of created, started, pending, active, inactive, cancelled, failed, abandoned or error"));
    }

    @Test
    public void shouldSuccessfullySearchMandate() throws JsonProcessingException {
        var searchNavigationLinksFromConnector = new SearchNavigationLinks()
                .withFirstLink(DD_CONNECTOR_BASE_SEARCH_URL + "&firstLink")
                .withLastLink(DD_CONNECTOR_BASE_SEARCH_URL + "&lastLink")
                .withNextLink(DD_CONNECTOR_BASE_SEARCH_URL + "&nextLink")
                .withSelfLink(DD_CONNECTOR_BASE_SEARCH_URL + "&selfLink");

        var selfLink = new PaymentConnectorResponseLink("self", "https://connector", "GET", null, null);

        MandateConnectorResponse mandate = aMandateConnectorResponse()
                .withMandateId(MANDATE_ID)
                .withMandateReference(MANDATE_REFERENCE)
                .withServiceReference(SERVICE_REFERENCE)
                .withReturnUrl(RETURN_URL)
                .withState(new MandateState("created", false, "mandate_state_details"))
                .withProviderId(PROVIDER_ID)
                .withCreatedDate(CREATED_DATE)
                .withPayer(new Payer(PAYER_NAME, PAYER_EMAIL))
                .withLinks(Collections.singletonList(selfLink))
                .build();

        MandateSearchConnectorResponse connectorResponse = aSearchMandateConnectorResponse()
                .withCount(1)
                .withPage(1)
                .withTotal(1)
                .withMandates(Collections.singletonList(mandate))
                .withLinks(searchNavigationLinksFromConnector)
                .build();

        String toDate = ISO_INSTANT_MILLISECOND_PRECISION.format(ZonedDateTime.now());
        String fromDate = ISO_INSTANT_MILLISECOND_PRECISION.format(ZonedDateTime.now().minusDays(1));

        Map<String, StringValuePattern>  searchParams = Map.of(
                "reference", equalTo(SERVICE_REFERENCE),
                "page", equalTo("1"),
                "display_size", equalTo("500"),
                "state", equalTo("created"),
                "to_date", equalTo(toDate),
                "from_date", equalTo(fromDate)
        );

        connectorDDMockClient.respondOk_whenSearchMandatesRequest(searchParams, connectorResponse, GATEWAY_ACCOUNT_ID);

        given().port(app.getLocalPort())
                .accept(JSON)
                .contentType(JSON)
                .header(AUTHORIZATION, "Bearer " + API_KEY)
                .queryParam("reference", SERVICE_REFERENCE)
                .queryParam("state", "created")
                .queryParam("to_date", toDate)
                .queryParam("from_date", fromDate)
                .get("/v1/directdebit/mandates/")
                .then()
                .statusCode(200)
                .contentType(JSON)
                .body("total", is(1))
                .body("count", is(1))
                .body("page", is(1))
                .body("results[0].mandate_id", is(MANDATE_ID))
                .body("results[0].provider_id", is(PROVIDER_ID))
                .body("results[0].reference", is(SERVICE_REFERENCE))
                .body("results[0].return_url", is(RETURN_URL))
                .body("results[0].state.status", is("created"))
                .body("results[0].state.details", is("mandate_state_details"))
                .body("results[0].created_date", is(CREATED_DATE))
                .body("results[0].payer.name", is(PAYER_NAME))
                .body("results[0].payer.email", is(PAYER_EMAIL))
                .body("results[0]._links.self.href", is(mandateLocationFor(MANDATE_ID)))
                .body("results[0]._links.self.method", is("GET"))
                .body("results[0]._links.payments.href", is("http://publicapi.url/v1/directdebit/payments?mandate_id=" + MANDATE_ID))
                .body("results[0]._links.payments.method", is("GET"))
                .body("_links.self.href", is("http://publicapi.url/v1/directdebit/mandates?page=1&display_size=500&selfLink"))
                .body("_links.first_page.href", is("http://publicapi.url/v1/directdebit/mandates?page=1&display_size=500&firstLink"))
                .body("_links.last_page.href", is("http://publicapi.url/v1/directdebit/mandates?page=1&display_size=500&lastLink"))
                .body("_links.next_page.href", is("http://publicapi.url/v1/directdebit/mandates?page=1&display_size=500&nextLink"));
    }
}