package io.github.shirainbown.sharelink.verify;

import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Component;
import org.springframework.web.reactive.function.server.RouterFunction;
import org.springframework.web.reactive.function.server.RouterFunctions;
import org.springframework.web.reactive.function.server.ServerRequest;
import org.springframework.web.reactive.function.server.ServerResponse;
import org.springframework.web.server.ServerWebInputException;
import reactor.core.publisher.Mono;
import run.halo.app.core.extension.endpoint.CustomEndpoint;
import run.halo.app.extension.GroupVersion;
import io.github.shirainbown.sharelink.SharelinkSettingConfigGetter;
import io.github.shirainbown.sharelink.emailcode.EmailCodeManager;
import io.github.shirainbown.sharelink.emailcode.EmailCodeNotificationSender;
import io.github.shirainbown.sharelink.util.EmailUtils;
import io.github.shirainbown.sharelink.util.IpUtils;

/**
 * Public endpoints for the download email verification flow, mounted under
 * {@code /apis/api.sharelink.halo.run/v1alpha1} and opened to anonymous visitors via the
 * aggregate-to-anonymous role template.
 */
@Slf4j
@Component
@RequiredArgsConstructor
public class EmailVerifyEndpoint implements CustomEndpoint {

    private final EmailCodeManager emailCodeManager;
    private final SharelinkSettingConfigGetter settingConfigGetter;
    private final VerifiedEmailService verifiedEmailService;

    @Override
    public RouterFunction<ServerResponse> endpoint() {
        return RouterFunctions.route()
            .POST("email-verify/-/send", this::sendCode)
            .POST("email-verify/-/check", this::checkVerified)
            .build();
    }

    private Mono<ServerResponse> sendCode(ServerRequest request) {
        return settingConfigGetter.getEmailVerifyConfig()
            .flatMap(config -> request.bodyToMono(SendCodeRequest.class)
                .switchIfEmpty(Mono.error(new ServerWebInputException("请求体不能为空")))
                .flatMap(body -> {
                    var email = EmailUtils.normalizeEmail(body.email());
                    if (email == null || !EmailUtils.isValidEmail(email)) {
                        return Mono.error(new ServerWebInputException("邮箱格式不正确"));
                    }
                    var clientIp = IpUtils.clientIp(request.exchange().getRequest());
                    return emailCodeManager.sendCode(email, clientIp, config,
                            resolveReasonType(body.purpose()))
                        .then(ServerResponse.ok().bodyValue(new SendCodeResponse(true,
                            config.getCodeExpireMinutes(),
                            config.getResendIntervalSeconds())));
                }));
    }

    /**
     * Maps the optional {@code purpose} field of the send request to a notification
     * reason type. Unknown or blank purposes fall back to the download template so old
     * clients keep working.
     */
    private static String resolveReasonType(String purpose) {
        if ("link".equalsIgnoreCase(purpose)) {
            return EmailCodeNotificationSender.LINK_REASON_TYPE;
        }
        return EmailCodeNotificationSender.REASON_TYPE;
    }

    private Mono<ServerResponse> checkVerified(ServerRequest request) {
        return request.bodyToMono(CheckRequest.class)
            .switchIfEmpty(Mono.error(new ServerWebInputException("请求体不能为空")))
            .flatMap(body -> {
                var email = EmailUtils.normalizeEmail(body.email());
                if (email == null || !EmailUtils.isValidEmail(email)) {
                    return Mono.error(new ServerWebInputException("邮箱格式不正确"));
                }
                return verifiedEmailService.isVerified(email)
                    // 登记查询失败按未验证处理，前端会回退到发送验证码流程。
                    .onErrorResume(e -> {
                        log.warn("查询邮箱验证状态失败，按未验证处理: {}", email, e);
                        return Mono.just(false);
                    })
                    .flatMap(verified -> ServerResponse.ok()
                        .bodyValue(new CheckResponse(verified)));
            });
    }

    @Override
    public GroupVersion groupVersion() {
        return GroupVersion.parseAPIVersion("api.sharelink.halo.run/v1alpha1");
    }

    public record SendCodeRequest(String email, String purpose) {
    }

    public record SendCodeResponse(boolean sent, long expireMinutes, long resendAfterSeconds) {
    }

    public record CheckRequest(String email) {
    }

    public record CheckResponse(boolean verified) {
    }
}
