package pl.polskalokalnie.report;

import java.time.Instant;
import java.util.ArrayList;
import java.util.List;
import org.springframework.http.HttpStatus;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.web.server.ResponseStatusException;
import pl.polskalokalnie.listing.ListingRepository;
import pl.polskalokalnie.listing.PropertyListing;
import pl.polskalokalnie.user.AppUser;
import pl.polskalokalnie.user.UserRepository;

@Service
public class ListingReportService {

    private static final int MAX_ATTACHMENTS = 5;
    private static final int MAX_FILE_NAME_LENGTH = 255;
    private static final int MAX_FILE_TYPE_LENGTH = 120;
    private static final int MAX_DATA_URL_LENGTH = 16_000_000;

    private final ListingReportRepository listingReportRepository;
    private final ListingRepository listingRepository;
    private final UserRepository userRepository;

    public ListingReportService(
            ListingReportRepository listingReportRepository,
            ListingRepository listingRepository,
            UserRepository userRepository
    ) {
        this.listingReportRepository = listingReportRepository;
        this.listingRepository = listingRepository;
        this.userRepository = userRepository;
    }

    @Transactional
    public ListingReportResponse create(CreateListingReportRequest request, String reporterEmail) {
        PropertyListing listing = listingRepository.findById(request.listingId())
                .orElseThrow(() -> new ResponseStatusException(HttpStatus.NOT_FOUND, "Listing not found"));

        ListingReport report = new ListingReport();
        report.setListingId(listing.getId());
        report.setListingTitle(listing.getTitle());
        report.setListingCity(listing.getCity());
        report.setListingOwnerEmail(listing.getOwnerEmail());
        report.setReasonId(request.reasonId().trim());
        report.setReasonTitle(request.reasonTitle().trim());
        report.setDetails(trimToNull(request.details()));
        report.setAttachments(cleanAttachments(request.attachmentNames(), request.attachmentFiles()));
        report.setReporterEmail(reporterEmail);
        report.setReporterName(userRepository.findByEmailIgnoreCase(reporterEmail).map(AppUser::getFullName).orElse(null));
        report.setStatus(ListingReportStatus.OPEN);

        return ListingReportResponse.from(listingReportRepository.save(report));
    }

    public List<ListingReportResponse> findAllForAdmin() {
        return listingReportRepository.findAllByOrderByCreatedAtDesc().stream()
                .map(ListingReportResponse::from)
                .toList();
    }

    @Transactional
    public ListingReportResponse resolve(Long reportId, String adminEmail, String note) {
        ListingReport report = requireReport(reportId);
        report.setStatus(ListingReportStatus.RESOLVED);
        report.setResolvedAt(Instant.now());
        report.setResolvedByEmail(adminEmail);
        report.setResolutionNote(trimToNull(note));
        return ListingReportResponse.from(listingReportRepository.save(report));
    }

    @Transactional
    public ListingReportResponse deleteListingAndResolve(Long reportId, String adminEmail) {
        ListingReport report = requireReport(reportId);
        Long listingId = report.getListingId();

        if (listingId != null && listingRepository.existsById(listingId)) {
            listingRepository.deleteById(listingId);
        }

        report.setListingDeleted(true);
        report.setStatus(ListingReportStatus.RESOLVED);
        report.setResolvedAt(Instant.now());
        report.setResolvedByEmail(adminEmail);
        if (report.getResolutionNote() == null || report.getResolutionNote().isBlank()) {
            report.setResolutionNote("Ogłoszenie usunięte przez administratora po zgłoszeniu.");
        }
        return ListingReportResponse.from(listingReportRepository.save(report));
    }

    private ListingReport requireReport(Long id) {
        return listingReportRepository.findById(id)
                .orElseThrow(() -> new ResponseStatusException(HttpStatus.NOT_FOUND, "Report not found"));
    }

    private static String trimToNull(String value) {
        if (value == null) {
            return null;
        }
        String trimmed = value.trim();
        return trimmed.isEmpty() ? null : trimmed;
    }

    private static List<ListingReportAttachment> cleanAttachments(
            List<String> names,
            List<ListingReportAttachmentPayload> files
    ) {
        List<ListingReportAttachment> cleaned = new ArrayList<>();

        if (files != null) {
            for (ListingReportAttachmentPayload payload : files) {
                if (payload == null) {
                    continue;
                }
                String fileName = truncate(trimToNull(payload.fileName()), MAX_FILE_NAME_LENGTH);
                if (fileName == null) {
                    continue;
                }
                ListingReportAttachment attachment = new ListingReportAttachment();
                attachment.setFileName(fileName);
                attachment.setFileType(truncate(trimToNull(payload.fileType()), MAX_FILE_TYPE_LENGTH));
                String dataUrl = trimToNull(payload.dataUrl());
                if (dataUrl != null && dataUrl.length() > MAX_DATA_URL_LENGTH) {
                    dataUrl = null;
                }
                attachment.setDataUrl(dataUrl);
                cleaned.add(attachment);
                if (cleaned.size() >= MAX_ATTACHMENTS) {
                    return cleaned;
                }
            }
        }

        if (names != null) {
            for (String name : names) {
                String trimmed = truncate(trimToNull(name), MAX_FILE_NAME_LENGTH);
                if (trimmed == null) {
                    continue;
                }
                boolean exists = cleaned.stream().anyMatch(item -> trimmed.equalsIgnoreCase(item.getFileName()));
                if (exists) {
                    continue;
                }
                ListingReportAttachment attachment = new ListingReportAttachment();
                attachment.setFileName(trimmed);
                cleaned.add(attachment);
                if (cleaned.size() >= MAX_ATTACHMENTS) {
                    break;
                }
            }
        }

        return cleaned;
    }

    private static String truncate(String value, int maxLength) {
        if (value == null) {
            return null;
        }
        if (value.length() <= maxLength) {
            return value;
        }
        return value.substring(0, maxLength);
    }
}
