package com.lingniu.ingest.protocol.gb32960.auth;

import com.lingniu.ingest.protocol.gb32960.config.Gb32960Properties;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import java.util.Arrays;
import java.util.Collections;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Objects;
import java.util.Set;

/**
 * 平台登入（0x05）账号白名单校验器。
 *
 * <p>与 {@link Gb32960VinAuthorizer} 区别：
 * <ul>
 *   <li>车辆登入靠 VIN 白名单（无密码）
 *   <li>平台登入有 12B 用户名 + 20B 密码 + 可选 IP 白名单
 * </ul>
 *
 * <p>平台账号不仅用于 0x05 鉴权，也会绑定到 channel attribute，后续 0x02/0x03 上报会用它选择
 * vendor profile。账号配错时，可能表现为 0x30+ 厂商字段全部落 Raw。
 *
 * <p>匹配规则：
 * <ol>
 *   <li>配置列表为空 → 返回 {@link Result#ALLOW_NO_POLICY}（兼容老行为，放行但标记未鉴权）
 *   <li>用户名不存在 → {@link Result#DENY_UNKNOWN_USER}
 *   <li>密码不匹配 → {@link Result#DENY_BAD_PASSWORD}
 *   <li>IP 白名单非空且 peer IP 不在其中 → {@link Result#DENY_IP_NOT_ALLOWED}
 *   <li>全部通过 → {@link Result#ALLOW}
 * </ol>
 */
public final class Gb32960PlatformAuthorizer {

    private static final Logger log = LoggerFactory.getLogger(Gb32960PlatformAuthorizer.class);

    private final Map<String, Gb32960Properties.Auth.Platform> byUsername;

    public Gb32960PlatformAuthorizer(List<Gb32960Properties.Auth.Platform> platforms) {
        Map<String, Gb32960Properties.Auth.Platform> map = new HashMap<>();
        for (Gb32960Properties.Auth.Platform p : platforms) {
            if (p.getUsername() == null || p.getUsername().isBlank()) continue;
            map.put(p.getUsername().trim(), p);
        }
        this.byUsername = Collections.unmodifiableMap(map);
        log.info("[gb32960] platform authorizer loaded {} platform(s)", this.byUsername.size());
    }

    /**
     * 鉴权一次平台登入请求。
     *
     * @param username 终端发来的用户名
     * @param password 终端发来的密码
     * @param peerIp   连接对端 IP（纯 IPv4/IPv6，不含端口）
     */
    public Result authenticate(String username, String password, String peerIp) {
        if (byUsername.isEmpty()) return Result.ALLOW_NO_POLICY;
        Gb32960Properties.Auth.Platform entry = byUsername.get(username == null ? "" : username.trim());
        if (entry == null) return Result.DENY_UNKNOWN_USER;
        if (!Objects.equals(entry.getPassword(), password)) return Result.DENY_BAD_PASSWORD;
        Set<String> allowed = entry.getAllowedIps() == null
                ? Set.of()
                : entry.getAllowedIps().stream()
                        .filter(Objects::nonNull)
                        .flatMap(value -> Arrays.stream(value.split(",")))
                        .map(String::trim)
                        .filter(ip -> !ip.isBlank())
                        .collect(java.util.stream.Collectors.toSet());
        if (!allowed.isEmpty() && (peerIp == null || !allowed.contains(peerIp))) {
            return Result.DENY_IP_NOT_ALLOWED;
        }
        return Result.ALLOW;
    }

    public int size() {
        return byUsername.size();
    }

    public boolean isEnforcing() {
        return !byUsername.isEmpty();
    }

    /** 鉴权结果枚举，调用方根据结果选择应答标志和日志级别。 */
    public enum Result {
        /** 匹配成功。 */
        ALLOW(true),
        /** 配置列表为空，放行但标记未鉴权。 */
        ALLOW_NO_POLICY(true),
        /** 用户名不存在。 */
        DENY_UNKNOWN_USER(false),
        /** 密码错误。 */
        DENY_BAD_PASSWORD(false),
        /** IP 白名单拒绝。 */
        DENY_IP_NOT_ALLOWED(false);

        private final boolean accepted;
        Result(boolean accepted) { this.accepted = accepted; }
        public boolean accepted() { return accepted; }
    }
}
