package com.liquidnet.service.adam.support;

import org.apache.commons.lang3.StringUtils;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Component;

import javax.crypto.Mac;
import javax.crypto.spec.SecretKeySpec;
import java.nio.charset.StandardCharsets;
import java.security.MessageDigest;
import java.time.Instant;

/**
 * 静默登录 v2：otp = hex(HMAC-SHA256(secret, mobile + Unix秒UTC))，与 v1 DES 隔离。
 */
@Component
public class SilentMobileOtpV2Support {

    private static final String HMAC_SHA256 = "HmacSHA256";

    @Value("${liquidnet.adam.silent-otp-v2-secret:}")
    private String secret;

    @Value("${liquidnet.adam.silent-otp-v2-window-seconds:10}")
    private int windowSeconds;

    public boolean verify(String mobile, String otp) {
        if (StringUtils.isBlank(secret) || StringUtils.isBlank(mobile) || StringUtils.isBlank(otp)) {
            return false;
        }
        if (!otp.matches("^[0-9a-fA-F]{64}$")) {
            return false;
        }
        long now = Instant.now().getEpochSecond();
        long from = now - windowSeconds;
        if (from < 0) {
            from = 0;
        }
        byte[] otpBytes = hexToBytes(otp);
        if (otpBytes == null) {
            return false;
        }
        for (long ts = from; ts <= now; ts++) {
            byte[] expected = sign(mobile, ts);
            if (expected != null && MessageDigest.isEqual(expected, otpBytes)) {
                return true;
            }
        }
        return false;
    }

    public String signHex(String mobile, long epochSecondUtc) {
        byte[] raw = sign(mobile, epochSecondUtc);
        return raw == null ? null : bytesToHex(raw);
    }

    private byte[] sign(String mobile, long epochSecondUtc) {
        try {
            String payload = mobile + epochSecondUtc;
            Mac mac = Mac.getInstance(HMAC_SHA256);
            mac.init(new SecretKeySpec(secret.getBytes(StandardCharsets.UTF_8), HMAC_SHA256));
            return mac.doFinal(payload.getBytes(StandardCharsets.UTF_8));
        } catch (Exception e) {
            return null;
        }
    }

    private static byte[] hexToBytes(String hex) {
        if (hex.length() % 2 != 0) {
            return null;
        }
        byte[] out = new byte[hex.length() / 2];
        for (int i = 0; i < hex.length(); i += 2) {
            out[i / 2] = (byte) Integer.parseInt(hex.substring(i, i + 2), 16);
        }
        return out;
    }

    private static String bytesToHex(byte[] bytes) {
        StringBuilder sb = new StringBuilder(bytes.length * 2);
        for (byte b : bytes) {
            sb.append(String.format("%02x", b & 0xff));
        }
        return sb.toString();
    }
}
