package de.oaa.libredeck.web.service;

import com.fasterxml.jackson.databind.JsonNode;
import com.fasterxml.jackson.databind.ObjectMapper;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Component;

import java.io.IOException;
import java.net.URI;
import java.net.URLEncoder;
import java.net.http.HttpClient;
import java.net.http.HttpRequest;
import java.net.http.HttpResponse;
import java.nio.charset.StandardCharsets;

@Slf4j
@Component
public class MusicBrainzClient {

    private static final String API_BASE   = "https://musicbrainz.org/ws/2";
    private static final String USER_AGENT = "LibreDeck/0.1 (https://github.com/libredeck)";
    private static final long   RATE_LIMIT = 1_100; // 1.1s – stays within the 1 req/s limit

    private final HttpClient   http   = HttpClient.newHttpClient();
    private final ObjectMapper mapper = new ObjectMapper();

    private long lastRequestTime = 0;

    /**
     * Looks up the earliest release year for a given recording.
     * Returns an empty string when no confident match is found.
     * This method is synchronized to enforce the MusicBrainz rate limit.
     */
    public synchronized String fetchYear(String artist, String title) {
        try {
            rateLimit();

            String query = "recording:\"" + escapeLucene(title) + "\""
                         + " AND artist:\"" + escapeLucene(artist) + "\"";
            String url = API_BASE + "/recording?query="
                    + URLEncoder.encode(query, StandardCharsets.UTF_8)
                    + "&fmt=json&limit=5";

            String body = get(url);
            JsonNode root = mapper.readTree(body);
            JsonNode recordings = root.path("recordings");

            if (!recordings.isArray()) return "";

            String oldest = "";
            for (JsonNode rec : recordings) {
                if (rec.path("score").asInt(0) < 75) break; // sorted by score desc
                String date = rec.path("first-release-date").asText("");
                if (date.length() >= 4) {
                    String year = date.substring(0, 4);
                    if (oldest.isEmpty() || year.compareTo(oldest) < 0) oldest = year;
                }
            }
            return oldest;

        } catch (Exception e) {
            log.warn("MusicBrainz lookup failed for [{} – {}]: {}", artist, title, e.getMessage());
            return "";
        }
    }

    private void rateLimit() throws InterruptedException {
        long wait = RATE_LIMIT - (System.currentTimeMillis() - lastRequestTime);
        if (wait > 0) Thread.sleep(wait);
        lastRequestTime = System.currentTimeMillis();
    }

    private String get(String url) throws IOException {
        HttpRequest request = HttpRequest.newBuilder()
                .uri(URI.create(url))
                .header("User-Agent", USER_AGENT)
                .header("Accept", "application/json")
                .GET()
                .build();
        try {
            HttpResponse<String> response = http.send(request, HttpResponse.BodyHandlers.ofString());
            if (response.statusCode() == 503) {
                throw new IOException("MusicBrainz rate limit exceeded (503)");
            }
            if (response.statusCode() != 200) {
                throw new IOException("MusicBrainz error " + response.statusCode());
            }
            return response.body();
        } catch (InterruptedException e) {
            Thread.currentThread().interrupt();
            throw new IOException("Request interrupted", e);
        }
    }

    private String escapeLucene(String input) {
        // Escape Lucene special characters relevant to MusicBrainz queries
        return input.replace("\\", "\\\\")
                    .replace("\"", "\\\"")
                    .replace("(", "\\(").replace(")", "\\)")
                    .replace("[", "\\[").replace("]", "\\]")
                    .replace("{", "\\{").replace("}", "\\}")
                    .replace(":", "\\:")
                    .replace("^", "\\^")
                    .replace("~", "\\~")
                    .replace("*", "\\*")
                    .replace("?", "\\?");
    }
}
