package com.ttstd.signaling.service;

import com.ttstd.signaling.security.AuthException;
import com.ttstd.signaling.security.SecurityProperties;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.stereotype.Service;

import javax.crypto.Mac;
import javax.crypto.spec.SecretKeySpec;
import java.nio.charset.StandardCharsets;
import java.security.InvalidKeyException;
import java.security.NoSuchAlgorithmException;
import java.time.Instant;
import java.util.ArrayList;
import java.util.Base64;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;

/**
 * TURN 短期凭证服务（RFC 7635 风格）。
 *
 * <p>客户端在发起/接受连接前，向本服务换取一组有效期有限的 TURN 凭证：
 * <ul>
 *   <li>{@code username} 形如 {@code <expSeconds>:<random>}，过期时间编码其中；</li>
 *   <li>{@code credential} 为 {@code HMAC-SHA1(sharedSecret, username)} 的 Base64；</li>
 *   <li>凭证到期需重新获取，避免长时间有效的静态密钥泄露风险。</li>
 * </ul>
 *
 * <p>返回的 {@code iceServers} 可直接用于 WebRTC 的 {@code RTCPeerConnection} 配置。
 */
@Service
public class TurnCredentialService {

    private static final Logger logger = LoggerFactory.getLogger(TurnCredentialService.class);
    private static final Base64.Encoder B64 = Base64.getEncoder();

    private final SecurityProperties properties;

    public TurnCredentialService(SecurityProperties properties) {
        this.properties = properties;
    }

    /** 是否已配置并可发放 TURN 凭证。 */
    public boolean isEnabled() {
        return properties.getTurn().isEnabled()
                && properties.getTurn().getSharedSecret() != null
                && !properties.getTurn().getSharedSecret().isBlank()
                && properties.getTurn().getUrls() != null
                && !properties.getTurn().getUrls().isBlank();
    }

    /**
     * 生成一组 TURN 短期凭证。
     *
     * @param scope 用途提示（如 "controller:" + bindingId），仅用于审计/日志，不影响计算
     * @return 包含 iceServers 的响应体
     */
    public Map<String, Object> issue(String scope) {
        if (!isEnabled()) {
            throw AuthException.unauthorized("TURN 凭证服务未启用");
        }
        SecurityProperties.Turn turn = properties.getTurn();
        long ttl = turn.getTtlSeconds();
        long expSeconds = Instant.now().getEpochSecond() + ttl;
        String username = expSeconds + ":" + randomToken(12);
        String credential = hmacSha1(turn.getSharedSecret(), username);

        List<Map<String, String>> iceServers = new ArrayList<>();
        for (String rawUrl : turn.getUrls().split(",")) {
            String url = rawUrl.trim();
            if (url.isEmpty()) {
                continue;
            }
            Map<String, String> server = new LinkedHashMap<>();
            server.put("urls", url);
            server.put("username", username);
            server.put("credential", credential);
            iceServers.add(server);
        }

        Map<String, Object> response = new LinkedHashMap<>();
        response.put("iceServers", iceServers);
        response.put("expiresAt", expSeconds);
        response.put("ttlSeconds", ttl);
        logger.debug("发放 TURN 凭证: scope={} exp={}", scope, expSeconds);
        return response;
    }

    private static String hmacSha1(String key, String data) {
        try {
            Mac mac = Mac.getInstance("HmacSHA1");
            mac.init(new SecretKeySpec(key.getBytes(StandardCharsets.UTF_8), "HmacSHA1"));
            byte[] raw = mac.doFinal(data.getBytes(StandardCharsets.UTF_8));
            return B64.encodeToString(raw);
        } catch (NoSuchAlgorithmException | InvalidKeyException e) {
            throw new IllegalStateException("TURN 凭证计算失败", e);
        }
    }

    private static String randomToken(int length) {
        java.security.SecureRandom rnd = new java.security.SecureRandom();
        final String alphabet = "0123456789abcdefghijklmnopqrstuvwxyz";
        StringBuilder sb = new StringBuilder(length);
        for (int i = 0; i < length; i++) {
            sb.append(alphabet.charAt(rnd.nextInt(alphabet.length())));
        }
        return sb.toString();
    }
}
