package com.prudas.app.service;

import com.prudas.app.config.AppProperties;
import com.prudas.app.dto.ContactRequest;
import com.prudas.app.dto.ContactSubmissionResponse;
import com.prudas.app.dto.PageResponse;
import com.prudas.app.entity.ContactSubmission;
import com.prudas.app.exception.RateLimitExceededException;
import com.prudas.app.exception.ResourceNotFoundException;
import com.prudas.app.repository.ContactSubmissionRepository;
import com.prudas.app.security.RateLimitingService;
import lombok.RequiredArgsConstructor;
import org.springframework.data.domain.Pageable;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import java.time.Duration;

@Service
@RequiredArgsConstructor
public class ContactService {

    private final ContactSubmissionRepository repository;
    private final RateLimitingService rateLimitingService;
    private final RecaptchaClient recaptchaClient;
    private final MailService mailService;
    private final AppProperties appProperties;

    @Transactional
    public void submit(ContactRequest request, String clientIp) {
        // Honeypot: a real visitor never fills this hidden field in.
        if (request.website() != null && !request.website().isBlank()) {
            return; // silently drop — don't tip off the bot
        }

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

        if (!recaptchaClient.isHuman(request.recaptchaToken())) {
            throw new IllegalArgumentException("Verification failed. Please try again.");
        }

        ContactSubmission entity = ContactSubmission.builder()
                .name(sanitize(request.name()))
                .email(request.email().trim().toLowerCase())
                .phone(request.phone())
                .subject(sanitize(request.subject()))
                .message(sanitize(request.message()))
                .ipAddress(clientIp)
                .build();
        repository.save(entity);

        mailService.notifyNewContactSubmission(entity.getName(), entity.getEmail(), entity.getSubject(), entity.getMessage());
    }

    @Transactional(readOnly = true)
    public PageResponse<ContactSubmissionResponse> findAllForAdmin(Pageable pageable) {
        return PageResponse.from(repository.findAllByOrderByCreatedAtDesc(pageable).map(this::toResponse));
    }

    @Transactional
    public void markRead(Long id) {
        ContactSubmission entity = repository.findById(id)
                .orElseThrow(() -> new ResourceNotFoundException("Submission not found: " + id));
        entity.setRead(true);
        repository.save(entity);
    }

    @Transactional
    public void delete(Long id) {
        if (!repository.existsById(id)) {
            throw new ResourceNotFoundException("Submission not found: " + id);
        }
        repository.deleteById(id);
    }

    /**
     * Defense-in-depth: strip characters that have no legitimate place in a
     * plain-text name/subject/message field. The real XSS protection is
     * output encoding on the frontend (React escapes by default) — this is
     * a second layer, not a substitute for it.
     */
    private String sanitize(String input) {
        if (input == null) {
            return null;
        }
        return input.replaceAll("[\\x00-\\x08\\x0B\\x0C\\x0E-\\x1F]", "").trim();
    }

    private ContactSubmissionResponse toResponse(ContactSubmission e) {
        return new ContactSubmissionResponse(e.getId(), e.getName(), e.getEmail(), e.getPhone(), e.getSubject(), e.getMessage(), e.isRead(), e.getCreatedAt());
    }
}
