package de.oaa.libredeck.web.service.youtube;

import com.fasterxml.jackson.databind.JsonNode;
import com.fasterxml.jackson.databind.ObjectMapper;
import de.oaa.libredeck.web.config.YouTubeConfig;
import de.oaa.libredeck.web.model.Track;
import de.oaa.libredeck.web.service.TrackNormalizer;
import lombok.RequiredArgsConstructor;
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;
import java.util.ArrayList;
import java.util.List;

@Slf4j
@Component
@RequiredArgsConstructor
public class YouTubeApiClient {

    private static final String API_BASE    = "https://www.googleapis.com/youtube/v3";
    private static final long   RATE_LIMIT  = 100; // ms
    private static final int    MAX_TRACKS  = 500;

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

    private long lastRequest = 0;

    public String fetchPlaylistTitle(String playlistId) throws IOException {
        String url = API_BASE + "/playlists?part=snippet&id=" + enc(playlistId) + "&key=" + enc(config.getApiKey());
        JsonNode root = mapper.readTree(get(url));
        return root.path("items").path(0).path("snippet").path("title").asText("Playlist " + playlistId);
    }

    public List<Track> fetchTracks(String playlistId) throws IOException {
        List<Track> result = new ArrayList<>();
        String pageToken = null;

        do {
            String url = API_BASE + "/playlistItems?part=snippet&playlistId=" + enc(playlistId)
                    + "&maxResults=50" + (pageToken != null ? "&pageToken=" + enc(pageToken) : "")
                    + "&key=" + enc(config.getApiKey());
            JsonNode root = mapper.readTree(get(url));

            // Enforce limit on first page using reported total
            if (result.isEmpty()) {
                int total = root.path("pageInfo").path("totalResults").asInt(0);
                if (total > MAX_TRACKS) {
                    throw new IllegalArgumentException(
                            "Diese Playlist enthält " + total + " Einträge. "
                            + "YouTube-Playlists werden nur bis " + MAX_TRACKS + " Titel unterstützt.");
                }
            }

            for (JsonNode item : root.path("items")) {
                JsonNode snippet = item.path("snippet");
                String videoId = snippet.path("resourceId").path("videoId").asText("");
                if (videoId.isEmpty()) continue;
                String rawTitle = snippet.path("title").asText("");
                if (rawTitle.equals("Private video") || rawTitle.equals("Deleted video")) continue;
                String channel = snippet.path("videoOwnerChannelTitle").asText("");

                String artist;
                String title;

                // Primary source: parse title using known separators
                // 1) "Artist - Title"  (spaces around dash)
                // 2) "Artist-Title"    (no spaces around dash)
                // 3) "Artist: Title"   (colon, e.g. "Metallica: Enter Sandman")
                // 4) fallback: channel name as artist
                int sep = rawTitle.indexOf(" - ");
                if (sep > 0) {
                    artist = rawTitle.substring(0, sep).trim();
                    title  = rawTitle.substring(sep + 3).trim();
                } else {
                    int bareDash = rawTitle.indexOf("-");
                    if (bareDash > 0) {
                        artist = rawTitle.substring(0, bareDash).trim();
                        title  = rawTitle.substring(bareDash + 1).trim();
                    } else {
                        int colon = rawTitle.indexOf(": ");
                        if (colon > 0) {
                            artist = rawTitle.substring(0, colon).trim();
                            title  = rawTitle.substring(colon + 2).trim();
                        } else {
                            artist = channel.replaceAll("(?i)\\s*-\\s*topic$", "")
                                            .replaceAll("(?i)VEVO$", "")
                                            .trim();
                            title  = rawTitle;
                        }
                    }
                }

                title = TrackNormalizer.cleanTitle(title);
                String[] ta = TrackNormalizer.extractFeaturing(title, artist);
                result.add(new Track(videoId, ta[0], ta[1], "",
                        "https://www.youtube.com/watch?v=" + videoId));
            }

            JsonNode next = root.path("nextPageToken");
            pageToken = next.isMissingNode() || next.isNull() ? null : next.asText();
        } while (pageToken != null);

        return result;
    }

    /** Year is resolved via MusicBrainz in GenerateController; nothing to do here. */
    public String fetchTrackYear(String videoId) {
        return "";
    }

    // ── HTTP ─────────────────────────────────────────────────────────────────

    private synchronized String get(String url) throws IOException {
        long wait = RATE_LIMIT - (System.currentTimeMillis() - lastRequest);
        if (wait > 0) {
            try { Thread.sleep(wait); } catch (InterruptedException e) {
                Thread.currentThread().interrupt();
                throw new IOException("Request interrupted", e);
            }
        }
        lastRequest = System.currentTimeMillis();

        try {
            HttpResponse<String> resp = http.send(
                    HttpRequest.newBuilder().uri(URI.create(url)).GET().build(),
                    HttpResponse.BodyHandlers.ofString());
            if (resp.statusCode() != 200) {
                JsonNode err = mapper.readTree(resp.body()).path("error");
                String msg = err.isMissingNode() ? resp.body()
                        : err.path("message").asText(resp.body());
                throw new IOException("YouTube API error " + resp.statusCode() + ": " + msg);
            }
            return resp.body();
        } catch (InterruptedException e) {
            Thread.currentThread().interrupt();
            throw new IOException("Request interrupted", e);
        }
    }

    private static String enc(String s) {
        return URLEncoder.encode(s, StandardCharsets.UTF_8);
    }
}
