package com.prudas.app.service;

import com.prudas.app.config.AppProperties;
import com.prudas.app.dto.NewsletterRequest;
import com.prudas.app.entity.NewsletterSubscriber;
import com.prudas.app.exception.RateLimitExceededException;
import com.prudas.app.repository.NewsletterSubscriberRepository;
import com.prudas.app.security.RateLimitingService;
import lombok.RequiredArgsConstructor;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import java.security.SecureRandom;
import java.time.Duration;
import java.util.Base64;

@Service
@RequiredArgsConstructor
public class NewsletterService {

    private final NewsletterSubscriberRepository repository;
    private final RateLimitingService rateLimitingService;
    private final MailService mailService;
    private final AppProperties appProperties;
    private final SecureRandom secureRandom = new SecureRandom();

    @Transactional
    public void subscribe(NewsletterRequest request, String clientIp) {
        if (request.website() != null && !request.website().isBlank()) {
            return; // honeypot tripped
        }

        if (!rateLimitingService.tryConsume("newsletter:" + clientIp, appProperties.rateLimit().newsletterPerHour(), Duration.ofHours(1))) {
            throw new RateLimitExceededException("Too many subscription attempts from this address. Please try again later.");
        }

        String email = request.email().trim().toLowerCase();
        if (repository.existsByEmail(email)) {
            return; // idempotent from the caller's point of view; avoids leaking existence via error codes
        }

        byte[] tokenBytes = new byte[24];
        secureRandom.nextBytes(tokenBytes);
        String token = Base64.getUrlEncoder().withoutPadding().encodeToString(tokenBytes);

        NewsletterSubscriber subscriber = NewsletterSubscriber.builder()
                .email(email)
                .confirmed(false)
                .confirmationToken(token)
                .build();
        repository.save(subscriber);

        mailService.sendNewsletterConfirmation(email, "/newsletter/confirm?token=" + token);
    }

    @Transactional
    public boolean confirm(String token) {
        return repository.findByConfirmationToken(token)
                .map(subscriber -> {
                    subscriber.setConfirmed(true);
                    subscriber.setConfirmationToken(null);
                    repository.save(subscriber);
                    return true;
                })
                .orElse(false);
    }
}
