package com.example.cezenPBX.controller;
import com.example.cezenPBX.DTO.ReturnStatus;
import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.databind.ObjectMapper;
import org.json.JSONException;
import org.json.JSONObject;
import org.junit.jupiter.api.*;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.boot.test.web.client.TestRestTemplate;
import org.springframework.http.*;

import java.util.List;

@SpringBootTest(webEnvironment = SpringBootTest.WebEnvironment.RANDOM_PORT)
@TestMethodOrder(MethodOrderer.OrderAnnotation.class)
@TestInstance(TestInstance.Lifecycle.PER_CLASS)
public class PbxControllerIntegrationTest {

    @Autowired
    private TestRestTemplate restTemplate;


    static HttpEntity<String> stringBasedRequest;


    static HttpEntity<String> extensionEndpoint;

    @BeforeAll
    public static void createAnEndpoint() throws JSONException {

        JSONObject endpointDetails = new JSONObject();
        endpointDetails.put("id","testEndpoint");
//        endpointDetails.put("transport","");
//        endpointDetails.put("context","");
//        endpointDetails.put("disallow","");
//        endpointDetails.put("allow","");
//        endpointDetails.put("directMedia","");
        endpointDetails.put("connectedLineMethod",null);
        endpointDetails.put("callerid",null);
        endpointDetails.put("dtmfMode",null);
//        endpointDetails.put("mohsuggest","");
        endpointDetails.put("mailboxes",null);

        HttpHeaders headers = new HttpHeaders();
        headers.setContentType(MediaType.APPLICATION_JSON);
        headers.setAccept(List.of(MediaType.APPLICATION_JSON));

        stringBasedRequest = new HttpEntity<>(endpointDetails.toString(), headers);

        //Arrange
        JSONObject extensionDetails = new JSONObject();
        extensionDetails.put("context","default");
        extensionDetails.put("extension","testEndpoint");
        extensionDetails.put("priority","5");
        extensionDetails.put("app","Dial");
        extensionDetails.put("appdata","PJSIP/1005,20,m(default)");

        extensionEndpoint = new HttpEntity<String>(extensionDetails.toString(), headers);
    }


    // test will create an testEndpoint endpoint
    @Test
    @DisplayName("User can be created")
    @Order(1)
    void testIfEndpointCanBeCreatedWithValidInput() {
        // Act
        ResponseEntity<String> response = this.restTemplate.postForEntity("/cezen/add_user", stringBasedRequest, String.class);

        // Assert
        Assertions.assertEquals(HttpStatus.OK, response.getStatusCode(), () -> "Wrong Status Code");
    }

    //duplicate entry must return false
    @Test
    @DisplayName("Duplicate values must result in false")
    @Order(2)
    void integrationTest_duplicateValurSubmission_ThrowsAnException()  {
        // Act
        ResponseEntity<String> response = this.restTemplate.postForEntity("/cezen/add_user", stringBasedRequest, String.class);
        String responseBody = response.getBody();

        // Assert
        Assertions.assertEquals(HttpStatus.OK, response.getStatusCode(), () -> "Wrong Status Code");
        Assertions.assertEquals("false", responseBody, () -> "must return false when value already exists");
    }


    // test for extension

    // if SIP end point doesn't exist then return a json ReturnStatus object with false in it

    @Test
    @DisplayName("For no SIP endpoint Should return ReturnStatus object with false in it")
    public void extensionTest_tryingToAddAnExtensionIfSipEndpointDoesntExist_returnAReturnStatusObjectWithFalseInIt() throws JSONException, JsonProcessingException {

        //Arrange
        JSONObject extensionDetails = new JSONObject();
        extensionDetails.put("context","default");
        extensionDetails.put("extension","NoEndPointEver");
        extensionDetails.put("priority","5");
        extensionDetails.put("app","Dial");
        extensionDetails.put("appdata","PJSIP/1005,20,m(default)");

        HttpHeaders headers = new HttpHeaders();
        headers.setContentType(MediaType.APPLICATION_JSON);
        headers.setAccept(List.of(MediaType.APPLICATION_JSON));

        HttpEntity<String> extensionEndpoint = new HttpEntity<String>(extensionDetails.toString(), headers);

        //Act
        ResponseEntity<String> response = this.restTemplate.postForEntity("/cezen/add_extension", extensionEndpoint, String.class);
        System.out.println(response.getBody());

        ObjectMapper mapper = new ObjectMapper();
        ReturnStatus status = mapper.readValue(response.getBody(), ReturnStatus.class);

        //Assert
        Assertions.assertFalse(status.status(), ()  -> "Wrong Status flag expected false in return ");
        Assertions.assertEquals("Create Endpoint for NoEndPointEver", status.message(), () -> "Wrong message for given input");
    }

    @Test
    @DisplayName("For no SIP endpoint Should return ReturnStatus object with false in it")
    @Order(3)
    public void extensionTest_addingANewExtension_StatusMessageMustReturnTrueInIt() throws JSONException, JsonProcessingException {

        //Act
        ResponseEntity<String> responseFromExtension = this.restTemplate.postForEntity("/cezen/add_extension", extensionEndpoint, String.class);
        System.out.println(responseFromExtension.getBody());

        ObjectMapper mapper = new ObjectMapper();
        ReturnStatus statusFromExtension = mapper.readValue(responseFromExtension.getBody(), ReturnStatus.class);

        //Assert
        Assertions.assertTrue(statusFromExtension.status(), ()  -> "Wrong Status flag expected True in returnStatus first extension entry");
        Assertions.assertEquals("testEndpoint Persisted ", statusFromExtension.message(), () -> "Wrong message for given input");
    }

    @Test
    @DisplayName("Duplicate entry of extension")
    @Order(4)
    public void extensionTest_addingANewExtension_StatusMessageMustReturnFalseInIt() throws JSONException, JsonProcessingException {

        //Act
        ResponseEntity<String> responseFromExtension = this.restTemplate.postForEntity("/cezen/add_extension", extensionEndpoint, String.class);
        System.out.println(responseFromExtension.getBody());

        ObjectMapper mapper = new ObjectMapper();
        ReturnStatus statusFromExtension = mapper.readValue(responseFromExtension.getBody(), ReturnStatus.class);

        //Assert
        Assertions.assertFalse(statusFromExtension.status(), ()  -> "Wrong Status flag expected false in returnStatus for duplicate entry ");
        Assertions.assertEquals("Data likely already exists or DB issue", statusFromExtension.message(), () -> "Wrong message for given input expected Data likely already exists or DB issue");
    }

    // delete the created testEndpoint value
    @Test
    @DisplayName("Delete an Extension with endpoint testEndpoint must return true")
    @Order(5)
    public void extensionTest_deletingAnExtension_ifExistsWillReturnTrue(){

        // Arrange
        String url = "/cezen/delete_extension?sipNumber=testEndPoint";

        //Act

        HttpHeaders headers = new HttpHeaders();
        headers.setContentType(MediaType.APPLICATION_JSON);

        HttpEntity<Void> requestEntity = new HttpEntity<>(headers);

        ResponseEntity<Boolean> response = restTemplate.exchange(
                url,
                HttpMethod.DELETE,
                requestEntity,
                Boolean.class
        );

        // if the extension exists
        Assertions.assertTrue(response.getBody());

        response = restTemplate.exchange(
                url,
                HttpMethod.DELETE,
                requestEntity,
                Boolean.class
        );

        // when deleted it won't exist so the endpoint must return false
        Assertions.assertFalse(response.getBody());
    }


}
