package io.delphiplatform.api.security;

import com.fasterxml.jackson.databind.ObjectMapper;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.http.HttpStatus;
import org.springframework.http.ResponseEntity;
import org.springframework.stereotype.Service;
import org.springframework.web.bind.annotation.RequestBody;

import java.io.IOException;
import java.net.URI;
import java.net.URLEncoder;
import java.net.http.HttpRequest;
import java.net.http.HttpResponse;
import java.nio.charset.StandardCharsets;
import java.util.Map;
import java.util.stream.Collectors;

import javax.validation.Valid;

import io.delphiplatform.api.v2.model.AuthTokenRequestBody;
import io.delphiplatform.api.v2.model.AuthTokenResponseBody;
import lombok.extern.slf4j.Slf4j;

@Slf4j
@Service
public class AuthService {

    private final ObjectMapper objectMapper;
    private final AuthConfiguration authConfiguration;
    private final AuthRequestSender authRequestSender;

    @Autowired
    public AuthService(ObjectMapper objectMapper, AuthConfiguration authConfiguration,
        AuthRequestSender authRequestSender) {
        this.objectMapper = objectMapper;
        this.authConfiguration = authConfiguration;
        this.authRequestSender = authRequestSender;
    }

    public ResponseEntity<AuthTokenResponseBody> authenticate(@Valid @RequestBody AuthTokenRequestBody body) {
        body.setAudience(authConfiguration.getAudience());
        Map<String, String> bodyMap = body.getMap();
        try {
            String encoded = bodyMap
                .entrySet()
                .stream()
                .map(e -> e.getKey() + "=" + URLEncoder.encode(e.getValue(), StandardCharsets.UTF_8))
                .collect(Collectors.joining("&"));
            HttpRequest request = HttpRequest.newBuilder()
                .POST(HttpRequest.BodyPublishers.ofString(encoded))
                .uri(URI.create(authConfiguration.getTokenUrl()))
                .setHeader("User-Agent", "Java HttpClient")
                .header("Content-Type", "application/x-www-form-urlencoded")
                .build();
            try {
                HttpResponse<String> response = authRequestSender.send(request);
                if (response.statusCode() == 200) {
                    AuthTokenResponseBody responseBody =
                        objectMapper.readValue(response.body(), AuthTokenResponseBody.class);
                    responseBody.calculateExpiresAt();
                    return new ResponseEntity<>(responseBody, HttpStatus.OK);
                }
                return new ResponseEntity<>(HttpStatus.valueOf(response.statusCode()));
            } catch (InterruptedException e) {
                e.printStackTrace();
                log.error("Couldn't reach authentication server", e);
                return new ResponseEntity<>(HttpStatus.INTERNAL_SERVER_ERROR);
            }
        } catch (IOException e) {
            log.error("Couldn't serialize response for content type application/json", e);
            return new ResponseEntity<>(HttpStatus.INTERNAL_SERVER_ERROR);
        }
    }

}
