package io.github.shirainbown.sharelink.emailcode;

import lombok.RequiredArgsConstructor;
import org.springframework.stereotype.Component;
import reactor.core.publisher.Mono;
import run.halo.app.core.extension.User;
import run.halo.app.core.extension.notification.Reason;
import run.halo.app.core.extension.notification.Subscription;
import run.halo.app.extension.GroupVersion;
import run.halo.app.notification.NotificationCenter;
import run.halo.app.notification.NotificationReasonEmitter;
import run.halo.app.notification.UserIdentity;

/**
 * Sends email verification codes through the Halo notification chain. The email address
 * is resolved by the core subscriber email resolver from the anonymous user identity
 * ({@code anonymousUser#<email>}), so an email notifier (SMTP) must be configured in the
 * console first.
 */
@Component
@RequiredArgsConstructor
public class EmailCodeNotificationSender {
    public static final String REASON_TYPE = "sharelink-download-verification";
    public static final String LINK_REASON_TYPE = "sharelink-link-verification";

    private final NotificationReasonEmitter reasonEmitter;
    private final NotificationCenter notificationCenter;

    public Mono<Void> sendVerificationCode(String email, String displayName, String code,
                                           long expirationMinutes) {
        return sendVerificationCode(REASON_TYPE, email, displayName, code, expirationMinutes);
    }

    public Mono<Void> sendVerificationCode(String reasonType, String email,
                                           String displayName, String code,
                                           long expirationMinutes) {
        var identity = UserIdentity.anonymousWithEmail(email);
        var interestReason = createInterestReason(identity);
        var subscribe = notificationCenter.subscribe(
            createSubscriber(identity), interestReason);
        var emitReason = reasonEmitter.emit(reasonType, builder -> builder
            .attribute("code", code)
            .attribute("expirationAtMinutes", String.valueOf(expirationMinutes))
            .attribute("username", displayName)
            .author(identity)
            .subject(Reason.Subject.builder()
                .apiVersion(interestReason.getSubject().getApiVersion())
                .kind(User.KIND)
                .name(identity.name())
                .title("资源下载邮箱验证：" + email)
                .build()));
        return Mono.when(subscribe).then(emitReason);
    }

    public Mono<Void> unsubscribe(String email) {
        var identity = UserIdentity.anonymousWithEmail(email);
        return notificationCenter.unsubscribe(createSubscriber(identity),
            createInterestReason(identity));
    }

    private static Subscription.Subscriber createSubscriber(UserIdentity identity) {
        var subscriber = new Subscription.Subscriber();
        subscriber.setName(identity.name());
        return subscriber;
    }

    private static Subscription.InterestReason createInterestReason(UserIdentity identity) {
        var interestReason = new Subscription.InterestReason();
        interestReason.setReasonType(REASON_TYPE);
        interestReason.setSubject(Subscription.ReasonSubject.builder()
            .apiVersion(new GroupVersion(User.GROUP, User.KIND).toString())
            .kind(User.KIND)
            .name(identity.name())
            .build());
        return interestReason;
    }
}
