package com.r11.tools.controller;

import com.fasterxml.jackson.databind.ObjectMapper;
import com.r11.tools.model.MockedMessageDto;
import com.r11.tools.service.KlausService;
import lombok.AllArgsConstructor;
import lombok.SneakyThrows;
import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;
import org.springframework.http.HttpHeaders;
import org.springframework.http.HttpStatus;
import org.springframework.http.MediaType;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.*;

import java.time.LocalDateTime;
import java.util.*;

/**
 * Returns the homepage and provides the api for javascript async requests.
 * @author Gabriel Modzelewski
 * @author Mikołaj Widła
 */
@RestController
@RequestMapping(path = "/api/mock")
@AllArgsConstructor
@CrossOrigin(origins = "*")
public class MockController {
    private final KlausService klausService;
    private final Logger log = LogManager.getRootLogger();

    @ExceptionHandler(Exception.class)
    public void errorHandler(Exception ex){
        log.error(Arrays.toString(ex.getStackTrace()));
    }

    /**
     * Updates queried message with given set of data
     * @param body {@link MockedMessageDto} json representation
     * @return confirmation and 200 OK
     */
    @SneakyThrows
    @PutMapping
    public ResponseEntity<String> updateMessage(@RequestBody String body){
        ObjectMapper mapper = new ObjectMapper();
        MockedMessageDto message = mapper.readValue(body, MockedMessageDto.class);
        return klausService.setMockedResponse(message);
    }

    /**
     * Returns the full list of messages. It's used by javascript on the client side to initialize homepage
     * with data from the database.
     * @param uuidValue the key-uuid of given set of messages
     * @return responds with 200 OK and {@link MockedMessageDto}
     */
    @GetMapping({"/", "/{uuidValue}"})
    public MockedMessageDto getMessage(@PathVariable(required = false) String uuidValue){
        UUID clientUUID;
        MockedMessageDto message ;
        if(uuidValue == null || uuidValue.equals("")) clientUUID = UUID.randomUUID();
        else clientUUID = UUID.fromString(uuidValue);
        message = klausService
                .getMockedMessageByClientUUID(clientUUID)
                .orElse( buildDefaultMessage(clientUUID) );

        return message;
    }


    /**
     * Constructs message with default set of data
     * @param uuid the key-uuid of given set of messages
     * @return message with default dataset
     */
    private MockedMessageDto buildDefaultMessage(UUID uuid){
        Map<String, String> headers = new HashMap<>();
        headers.put("Keep-Alive", "timeout=60");
        headers.put("Connection", "keep-alive");
        headers.put("Date", LocalDateTime.now().toString());
        MockedMessageDto mockedMessageDto = MockedMessageDto.builder()
                .clientUUID(uuid)
                .contentType(MediaType.APPLICATION_XML_VALUE)
                .messageBody("<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n" +
                        "<note>\n" +
                        "  <to>Tove</to>\n" +
                        "  <from>Jani</from>\n" +
                        "  <heading>Reminder</heading>\n" +
                        "  <body>Don't forget me this weekend!</body>\n" +
                        "</note>")
                .httpHeaders(headers)
                .httpStatus(200)
                .build();
        klausService.setMockedResponse(mockedMessageDto);
        return mockedMessageDto;
    }
    /**
     * It's one of the most important features - the bread and butter of the Mocked Service. It's link that allows
     * to receive mocked response from the server and use it to mock!
     * @param clientUUID the key-uuid of given set of messages
     * @return
     */
    @RequestMapping(value = "/r/{clientUUID}")
    public ResponseEntity getMockedResponse(
                                            @PathVariable UUID clientUUID) {
        MockedMessageDto mockedMessageDto = klausService.getMockedResponse(clientUUID);
        HttpHeaders httpHeaders = new HttpHeaders();
        if (mockedMessageDto.getHttpHeaders() != null) mockedMessageDto.getHttpHeaders().forEach(httpHeaders::set);
        httpHeaders.add("Content-Type", mockedMessageDto.getContentType());
        return new ResponseEntity<>(mockedMessageDto.getMessageBody(), httpHeaders,
                Objects.requireNonNull(HttpStatus.valueOf(mockedMessageDto.getHttpStatus())));
    }

}
