package study.iot.support;

import com.hivemq.extension.sdk.api.ExtensionMain;
import com.hivemq.extension.sdk.api.annotations.NotNull;
import com.hivemq.extension.sdk.api.auth.SimpleAuthenticator;
import com.hivemq.extension.sdk.api.auth.parameter.TopicPermission;
import com.hivemq.extension.sdk.api.packets.auth.ModifiableDefaultPermissions;
import com.hivemq.extension.sdk.api.packets.connect.ConnackReasonCode;
import com.hivemq.extension.sdk.api.parameter.ExtensionStartInput;
import com.hivemq.extension.sdk.api.parameter.ExtensionStartOutput;
import com.hivemq.extension.sdk.api.parameter.ExtensionStopInput;
import com.hivemq.extension.sdk.api.parameter.ExtensionStopOutput;
import com.hivemq.extension.sdk.api.services.Services;
import com.hivemq.extension.sdk.api.services.builder.Builders;

import java.nio.charset.StandardCharsets;

/**
 * 教学用设备身份与 ACL 扩展：用户名必须是 device-&lt;clientId&gt;，密码必须是 secret-&lt;clientId&gt;。
 *
 * <p>角色由 clientId 前缀决定：普通设备只允许 devices/&lt;clientId&gt;/#，而 svc- 前缀的
 * 云端服务允许 devices/#。权限通过真实的 broker 扩展点 TopicPermission 授予；失败时以
 * CONNACK NOT_AUTHORIZED 结束连接。这不是 listener 层的假代理。</p>
 */
public final class DeviceSecurityMain implements ExtensionMain {

    public static final String DEVICE_PREFIX = "device-";
    public static final String SECRET_PREFIX = "secret-";
    public static final String SERVICE_PREFIX = "svc-";

    @Override
    public void extensionStart(@NotNull ExtensionStartInput input, @NotNull ExtensionStartOutput output) {
        Services.securityRegistry().setAuthenticatorProvider(providerInput -> {
            String clientId = providerInput.getClientInformation().getClientId();
            return (SimpleAuthenticator) (authInput, authOutput) -> {
                var connect = authInput.getConnectPacket();
                String user = connect.getUserName().map(Object::toString).orElse("");
                String password = connect.getPassword()
                        .map(buffer -> {
                            byte[] bytes = new byte[buffer.remaining()];
                            buffer.duplicate().get(bytes);
                            return new String(bytes, StandardCharsets.UTF_8);
                        })
                        .orElse("");
                if (!(DEVICE_PREFIX + clientId).equals(user) || !(SECRET_PREFIX + clientId).equals(password)) {
                    authOutput.failAuthentication(ConnackReasonCode.NOT_AUTHORIZED, "device credentials rejected");
                    return;
                }
                String topicFilter = clientId.startsWith(SERVICE_PREFIX)
                        ? "devices/#"
                        : "devices/" + clientId + "/#";
                ModifiableDefaultPermissions permissions = authOutput.getDefaultPermissions();
                permissions.add(Builders.topicPermission()
                        .topicFilter(topicFilter)
                        .type(TopicPermission.PermissionType.ALLOW)
                        .qos(TopicPermission.Qos.ALL)
                        .activity(TopicPermission.MqttActivity.ALL)
                        .retain(TopicPermission.Retain.ALL)
                        .sharedSubscription(TopicPermission.SharedSubscription.ALL)
                        .sharedGroup("#")
                        .build());
                authOutput.authenticateSuccessfully();
            };
        });
    }

    @Override
    public void extensionStop(@NotNull ExtensionStopInput input, @NotNull ExtensionStopOutput output) {
        // 停服时无需额外清理：权限随连接生命周期结束。
    }
}
