package de.votian.web.service;

import de.votian.web.model.SessionData;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.beans.factory.ObjectProvider;
import org.springframework.beans.factory.support.ScopeNotActiveException;
import org.springframework.core.ParameterizedTypeReference;
import org.springframework.core.io.ByteArrayResource;
import org.springframework.http.HttpEntity;
import org.springframework.http.HttpHeaders;
import org.springframework.http.HttpMethod;
import org.springframework.http.MediaType;
import org.springframework.http.ResponseEntity;
import org.springframework.stereotype.Service;
import org.springframework.http.client.MultipartBodyBuilder;
import org.springframework.util.MultiValueMap;
import org.springframework.web.client.RestTemplate;

import java.util.Collections;
import java.util.List;
import java.util.Map;

@Service
public class RestClientService {

    private final RestTemplate restTemplate;
    private final String baseUrl;
    private final ObjectProvider<SessionData> sessionDataProvider;

    public RestClientService(RestTemplate restTemplate,
                             @Qualifier("servicesBaseUrl") String baseUrl,
                             ObjectProvider<SessionData> sessionDataProvider) {
        this.restTemplate = restTemplate;
        this.baseUrl = baseUrl;
        this.sessionDataProvider = sessionDataProvider;
    }

    public <T> T get(String path, Class<T> type) {
        ResponseEntity<T> response = restTemplate.exchange(
                baseUrl + path, HttpMethod.GET, authorizedEntity(null), type);
        return response.getBody();
    }

    public <T> List<T> getList(String path, ParameterizedTypeReference<List<T>> typeRef) {
        ResponseEntity<List<T>> response = restTemplate.exchange(
                baseUrl + path, HttpMethod.GET, authorizedEntity(null), typeRef);
        return response.getBody() != null ? response.getBody() : Collections.emptyList();
    }

    public <T> T post(String path, Object body, Class<T> type) {
        ResponseEntity<T> response = restTemplate.exchange(
                baseUrl + path, HttpMethod.POST, authorizedEntity(body), type);
        return response.getBody();
    }

    public <T> T postMultipart(String path, byte[] fileBytes, String filename, String contentType,
                               Map<String, String> fields, Class<T> type) {
        MultipartBodyBuilder builder = new MultipartBodyBuilder();
        ByteArrayResource resource = new ByteArrayResource(fileBytes) {
            @Override
            public String getFilename() {
                return filename;
            }
        };
        builder.part("file", resource)
                .filename(filename)
                .contentType(contentType != null && !contentType.isBlank()
                        ? MediaType.parseMediaType(contentType)
                        : MediaType.APPLICATION_OCTET_STREAM);
        for (Map.Entry<String, String> entry : fields.entrySet()) {
            builder.part(entry.getKey(), entry.getValue());
        }

        ResponseEntity<T> response = restTemplate.exchange(
                baseUrl + path,
                HttpMethod.POST,
                authorizedMultipartEntity(builder.build()),
                type
        );
        return response.getBody();
    }

    public void put(String path, Object body) {
        restTemplate.exchange(baseUrl + path, HttpMethod.PUT, authorizedEntity(body), Void.class);
    }

    public void delete(String path) {
        restTemplate.exchange(baseUrl + path, HttpMethod.DELETE, authorizedEntity(null), Void.class);
    }

    public Map<String, Object> getMap(String path) {
        ResponseEntity<Map<String, Object>> response = restTemplate.exchange(
                baseUrl + path, HttpMethod.GET, authorizedEntity(null),
                new ParameterizedTypeReference<Map<String, Object>>() {});
        return response.getBody() != null ? response.getBody() : Collections.emptyMap();
    }

    public byte[] getBytes(String path) {
        ResponseEntity<byte[]> response = restTemplate.exchange(
                baseUrl + path, HttpMethod.GET, authorizedEntity(null), byte[].class);
        return response.getBody() != null ? response.getBody() : new byte[0];
    }

    private HttpEntity<Object> authorizedEntity(Object body) {
        return new HttpEntity<>(body, authorizedHeaders(MediaType.APPLICATION_JSON));
    }

    private HttpEntity<MultiValueMap<String, HttpEntity<?>>> authorizedMultipartEntity(MultiValueMap<String, HttpEntity<?>> body) {
        return new HttpEntity<>(body, authorizedHeaders(MediaType.MULTIPART_FORM_DATA));
    }

    private HttpHeaders authorizedHeaders(MediaType contentType) {
        HttpHeaders headers = new HttpHeaders();
        headers.setContentType(contentType);
        SessionData sessionData = currentSessionData();
        if (sessionData != null && sessionData.getAuthToken() != null && !sessionData.getAuthToken().isBlank()) {
            headers.setBearerAuth(sessionData.getAuthToken());
        }
        return headers;
    }

    private SessionData currentSessionData() {
        try {
            return sessionDataProvider.getIfAvailable();
        } catch (ScopeNotActiveException | IllegalStateException ignored) {
            return null;
        }
    }
}
