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

import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Component;

import de.oaa.libredeck.web.config.AppleMusicConfig;

import java.nio.charset.StandardCharsets;
import java.security.KeyFactory;
import java.security.PrivateKey;
import java.security.Signature;
import java.security.spec.PKCS8EncodedKeySpec;
import java.time.Instant;
import java.util.Base64;

@Slf4j
@Component
@RequiredArgsConstructor
public class AppleMusicTokenGenerator {

    private static final long TOKEN_LIFETIME = 3600L; // 1 hour in seconds

    private final AppleMusicConfig config;

    private volatile String cachedToken;
    private volatile long   tokenExpiry;

    public boolean isConfigured() {
        return !config.getTeamId().isBlank()
                && !config.getKeyId().isBlank()
                && !config.getPrivateKey().isBlank();
    }

    public String getDeveloperToken() {
        if (cachedToken != null && Instant.now().getEpochSecond() < tokenExpiry - 60) {
            return cachedToken;
        }
        try {
            cachedToken = buildToken();
            tokenExpiry = Instant.now().getEpochSecond() + TOKEN_LIFETIME;
            return cachedToken;
        } catch (Exception e) {
            throw new RuntimeException("Failed to generate Apple Music developer token", e);
        }
    }

    private String buildToken() throws Exception {
        String headerJson  = "{\"alg\":\"ES256\",\"kid\":\"" + config.getKeyId() + "\"}";
        long   now         = Instant.now().getEpochSecond();
        String payloadJson = "{\"iss\":\"" + config.getTeamId() + "\",\"iat\":" + now
                + ",\"exp\":" + (now + TOKEN_LIFETIME) + "}";

        String headerB64  = b64url(headerJson.getBytes(StandardCharsets.UTF_8));
        String payloadB64 = b64url(payloadJson.getBytes(StandardCharsets.UTF_8));
        String sigInput   = headerB64 + "." + payloadB64;

        Signature sig = Signature.getInstance("SHA256withECDSAinP1363Format");
        sig.initSign(loadPrivateKey(config.getPrivateKey()));
        sig.update(sigInput.getBytes(StandardCharsets.UTF_8));

        return sigInput + "." + b64url(sig.sign());
    }

    private static PrivateKey loadPrivateKey(String pem) throws Exception {
        String stripped = pem
                .replace("-----BEGIN PRIVATE KEY-----", "")
                .replace("-----END PRIVATE KEY-----", "")
                .replaceAll("\\s+", "");
        byte[] keyBytes = Base64.getDecoder().decode(stripped);
        return KeyFactory.getInstance("EC").generatePrivate(new PKCS8EncodedKeySpec(keyBytes));
    }

    private static String b64url(byte[] data) {
        return Base64.getUrlEncoder().withoutPadding().encodeToString(data);
    }
}
